Skip to content

fix(qa): drop the rotting :NNN source-line citations from the platform checklist - #13786

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-13482-checklist-line-citations
Aug 31, 2026
Merged

fix(qa): drop the rotting :NNN source-line citations from the platform checklist#13786
os-project-manager merged 3 commits into
mainfrom
claude/issue-13482-checklist-line-citations

Conversation

@claude

@claude claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes #13482

Ruling step (1): drop the :NNN suffixes from the QA platform checklist, because the file plus the symbol name is already the load-bearing half and the line number is the only part that rots. Plus the cheap half of the enforcement the card was actually graded on — the gate now refuses a line citation instead of parsing past it.

The card demonstrated its own defect between being written and being dispatched

The card recorded the post-#13479 positions as PluginRuntimeSchema 103, PluginPackagingSchema 119, ManifestSchema 156. Measured on the tree this branch forks from:

$ grep -nE '^export const (ManifestSchema|PluginRuntimeSchema|PluginPackagingSchema)' \
    packages/spec/src/kernel/manifest.zod.ts
103:export const PluginRuntimeSchema = z
119:export const PluginPackagingSchema = z
158:export const ManifestSchema = z.object({

ManifestSchema moved another two lines while the card sat in the queue. Nothing in the repo noticed, because nothing resolves a citation. That is the argument for this change, made by the card about itself.

What counts as a source-line citation, and how many there were

Criterion. A colon followed by digits (optionally a range), reached one of two ways, because the ledger spelled it both ways:

  1. file-anchored — attached to a source filename: manifest.zod.ts:NNN;
  2. bare — continuing a filename named earlier in the same sentence: ManifestSchema id :NNN and version :NNN.

Everything else that shares the colon-then-digit shape is excluded by a negative lookbehind on a word character or a quote: HTTP status (status:409), config literals in prose ({maxRetries:3}), URL ports (http://localhost:3000), clock times (08:00, ...T00:00:00Z) and JSON quoted in prose ({"scannedTypes":1}). Each of those five is pinned as a silent case in the gate's self-test.

Census: 1,647 citations across 18 files — 1,034 file-anchored, 613 bare; 1,051 of them ranges.

file citations file citations
areas/cli.json 265 areas/api-backend.json 78
areas/identity-auth.json 203 areas/attachments-storage.json 73
areas/integration-system.json 188 areas/studio-authoring.json 62
areas/platform-core.json 180 areas/approvals.json 56
areas/records-forms.json 161 areas/dashboards.json 16
FOLLOW-UPS.md 121 areas/search.json 16
areas/access-security.json 105 areas/ai.json 14
areas/automation.json 103 areas/i18n.json, RUNNER.md, SWEEP.md 3, 2, 1

The number was arrived at twice by different tools — a Python pass over JSON string values only (so JSON syntax could never be counted) and the gate's own JavaScript detector run against the pre-change tree. Both give 1,647.

On the crude grep -ohE ':[0-9]{2,4}\b' figure of 1,621: it was wrong in both directions and the two errors nearly cancelled. Compared position by position against the real census: 1,601 agree, 20 crude false positives (all timestamps, config literals and ports) and 46 real citations the crude pattern missed — five-and-more-digit line numbers such as rest-server.ts:11298-11311, where [0-9]{2,4} matches four digits and then \b fails, plus single-digit ones like useAiSurface.ts:9-40. Net −20 +46 = +26, and 1,621 + 26 = 1,647. Treating it as an upper bound was the right call; it was in fact an under-count.

The citations that carried only a line number (A2.3 — the real difficulty)

613 of the 1,647 carried no filename of their own. Stripping those cannot simply delete a suffix, so they were split by what the deletion leaves behind:

  • 43 sat alone inside parenthesesholds ONLY auditor (:NNN), so .... The parentheses went with the citation; nothing else in the sentence referred to it.
  • 74 sat behind a preposition or conjunction whose only object was the line numberthe maxSize guard at :NNN and the mime read at :NNN both require .... Deleting the number alone leaves guard at and, so the orphaned connector went too: the maxSize guard and the mime read both require .... Consumed: at 65, and 8, from 1.
  • the remainder followed a symbol or nounrenderSubject :NNN, beforeInsert :NNN — where the symbol name is precisely the load-bearing half the ruling relies on, and removal needs no repair.

Two words were nearly lost to over-eager rules and were caught before they landed: line is a noun in this ledger (the mode line :NNN, the aggregate sweep line :NNN), not a preposition, so it is not consumed; and a blanket empty-bracket cleanup would have deleted a real [] literal in returns [] on a clean ledger. The corpus holds 146 [] literals and 26 () call spellings, all preserved.

Integrity check on the result, over all 887 changed strings: the only words that disappear are the 74 connectors listed above (zero non-connector word losses, zero words gained); [], () and backtick counts are identical before and after; the open-paren count drops by exactly 43, matching the parenthesised groups; and zero citation-shaped tokens remain. The 36 colon-then-digit tokens that are not citations are all still there.

The area files are edited as raw text at the string-literal level, never re-serialised — every citation token was first proven to sit inside a JSON string value, so the diff is 887 insertions against 887 deletions with no reformatting.

The gate half

scripts/check-platform-checklist.mjs gains a source-line-citation refusal over the whole family (20 files; runs/ excluded, since run records are outputs written against whatever the ledger said at the time).

⛔ This is not the symbol resolver from ruling step (2). It is the cheap half: it keeps the class from coming back, and it is what turns "nothing validates them" into a red. The self-test pins both directions — 6 firing cases covering every spelling the ledger actually carried (file-anchored, range, bare continuation, parenthesised, ~: approximate, comma/slash-chained) and 7 silent cases covering the neighbours above plus the README's own placeholder spelling of the ban. It also carries an inline positive control, for the reason this gate's siblings do: the ledger is clean now, so the check's real output is permanently empty and a detector that quietly stopped matching would look exactly like a ledger that stayed clean — which is the same exit-0-by-construction shape the card is about.

README.md carried the convention that reproduced the defect — "Every call cites framework source at file:line" — and now states the opposite, with the reason and the ban.

Ablation (the check must be able to go red). Re-introducing the card's own citation into areas/cli.json, with the mutation proven on disk before the run and the restore proven by observed state after it:

HEAD blob:      0c8f36ddc60b0d6eef0ac6988e6486e2a560c961
marker count before=0 after=1
post-mutation:  4e0555da0bdbe7e6ce7a47e084f2f4ed954a8a86   (differs from HEAD blob => on disk)
ABLATED GATE EXIT=1
check-platform-checklist: 3 problem(s)
  ✗ areas/cli.json: SOURCE LINE CITATION — `... manifest.zod«.ts:158» — ManifestSchema id`...

Restore verified by state, not by an exit code: git diff HEAD empty, git status --short empty, worktree blob 0c8f36dd... byte-identical to the HEAD blob, marker count back to 0. Gate green again afterwards.

⚠️ How far this enforcement actually reaches, stated plainly rather than implied. pnpm check:platform-checklist is not wired into CIlint.yml records that as a standing maintainer decision, and the gate runs on a manual periodic cadence instead. So this refusal fails for whoever runs it, which is not the same as failing every PR. The gap between "there is a gate" and "its red reaches somebody" is already carded and awaiting the maintainer in #11730; nothing here changes that decision or pre-empts it. It is called out because this card is about a signal that reads as verified when it is not, and a new check whose reach is overstated would be the same defect one level up.

The file: selector in scripts/checklist-select.mjs — the one consumer that reads source[] — matches by path substring, so dropping a line suffix cannot change what it selects; its 17-case self-test passes.

Why step (2) is not in this PR

The ruling fixed the order, not the packaging. The census is the basis for splitting: step (2) means re-authoring 1,647 citations across 260 items into a structured, resolvable anchor and adding a resolver with its own registration, self-test and baseline. The shape to copy already exists in this very gate — enumSource {file, export, expect} is resolved today (file existence, export presence, member count), so the follow-up is a known quantity rather than a design question. Doing it here would bury a 1,647-line prose cleanup inside a new verification surface and make both unreviewable. Step (1) removes a false "verified against source" signal today; step (2) should be its own card, and this PR leaves the ledger in exactly the state that card wants as input.

Verification

Union run at 137e04a17, on the merged tree.

  • pnpm check:platform-checklistGREEN, exit 0. source citations: 20 family files carry no file:line pin; (self-checks: ... + 13 source-line-citation assertions)
  • node scripts/check-platform-checklist.mjs --self-testGREEN, exit 0, 141 assertions (13 new)
  • pnpm lint (eslint . --no-inline-config, whole repo, not narrowed) — GREEN, exit 0
  • pnpm check:nul-bytesGREEN, exit 0, 7,583 files scanned
  • Path-derived family, node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack — 14 families: 13 GREEN, 1 NOT MEASURED (scripts/check-test-completeness.mjs, exit 3, PREREQUISITE NOT MET — it grades a saved turbo run test log and its own text says to record it as NOT MEASURED locally). check:doc-formula-expressions was PREREQUISITE NOT MET until @objectstack/formula and @objectstack/lint were built, then GREEN.

Every exit code above was captured before any pipe.

Authored by Claude Code, session session_01Pk26oZ12t5N1hwGW1m1MgC (os-dev seat).

No changeset: the diff publishes nothing — internal QA ledger under docs/qa/** plus one repo gate script. skip-changeset applied.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants