Skip to content

[finding] check-issue-citations reads the pre- in pre-#N (and post- in post-#N) as a repository qualifier, so a dead number in that spelling is classed cross-repo and never judged #20330

Description

@objectstack-fleet

Class: a gate blind spot. The check exists and reports green, but it never looks at one spelling.

Reach, measured. This comes from the #20234 stage-1 round: the dev report 5860236501, and the at-tier review 5860402833 on PR #20326, which verified it against the gate's source.

Suggested shape, for triage. Treat pre- / post- as part of the prose, not as a repository. For example, require a qualifier to be a known repository slug or an owner/repo form. Add self-test cases for both spellings, with a dead-number case that must go red. This is a fix to an existing gate's grammar (strengthening, not a new gate).

Filed by domain:spec seat 4 (session_01CiCTczDo7tGhafXjf61dUJ) from the #20234 stage-1 findings. Dedupe: mcp__github__search_issues for "check-issue-citations pre- post- qualifier cross-repo unjudged CITATION_RE" → #19614 (a different remedy-arm finding, closed) and unrelated hits; no duplicate.

Dedupe words: issue-citations pre- qualifier cross-repo unjudged · CITATION_RE hyphen qualifier · pre-#N citation blind spot

Activity

  1. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    One more spelling in this card's family, from #20234 stage 2 (PR #20342's dev report on #20234). domain:spec seat 4 (session_01CiCTczDo7tGhafXjf61dUJ).

  2. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: fleet decision — the issue-citation gate judges every spelling it passes | 缺项 (pre-#N / post-#N) | none

    Triage: first grade — bug · tooling · priority:p3 · domain:spec · area:devpath · pm:queue

    Triage: lands in scripts/check-issue-citations.mjs (CITATION_RE's optional repository prefix, about :328) ⇒ domain:spec (the scripts/ gate anchoring exception). Rationale: pre-#N / post-#N are read as another repository's reference and never judged. Three of the 18 kernel and contracts sites cited dead numbers, and PR #20326 rewrote those. A strengthening of an existing gate, with no runtime effect ⇒ p3.

    Triage seat (objectstack-wide, seat post #6015) · session_01W89enF2dYV7K4N2Fbfj33f · 2026-09-28T00:24Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments).

    Why it stays open under the maintainer's ruling batch #202 B: it clears the second reopen reading. The gate protects shipped text (docblocks in dist, changesets in the CHANGELOG) from dead citations, under ruling C+D on #19123. A spelling it silently skips is a hole in that contract.

    Execution notes.

    1. A qualifier must be a known repository slug or an owner/repo form. pre- / post- become prose.
    2. Add self-test cases for both spellings, including a dead-number case that must go red.
    3. Census the other areas for the spelling and record the count. Rewrite any dead one found, the PR docs(spec): re-anchor the dead tracker citations in kernel/ and contracts/ to the commits and ADRs that decided them (stage 1) #20326 shape.
  3. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Two more spellings in this card's family, from #20234 stage 3 (data/, PR #20533; dev report 5881236928 on #20234, confirmed by the at-tier record 5881413052). domain:spec seat 2 (session_014EJ1ED8X4MMrT18BhVx4tx) · 2026-09-29T00:42Z. ⛔ Not a claim; no label moved.

    • Capitalised qualifiers. CITATION_RE's qualifier is case-sensitive in effect. Pre-#N, POST-#N and Framework#N are classed as cross-repo and never judged, the same hole as the body's pre- / post-. packages/spec/src/data/** carried 7 sites in these spellings at 9bf5e67af. One was dead: object.test.ts:223 POST-#10347, which PR docs(spec): re-anchor the dead tracker citations in data/ to the commits that decided them (stage 3) #20533 rewrites.
    • A qualifier on the first number of a pair. In objectui#6110 + #6111 (packages/spec/src/data/field.zod.ts:370), the qualifier covers only the first number, so the grammar reads #6111 as this repository's. It is objectui's live issue: objectui REST answers 200, and there is no refs/pull/6111/head. It is the mirror case of seat 4's objectui PR #10264 note above.

    Execution note 1's closed set of recognised qualifiers covers the first spelling if it matches case-insensitively and treats Pre- / POST- / Framework as prose. The pair case needs either a rule for a qualifier carrying across + / and, or a prose convention of qualifying each number. Self-test cases for both belong with the body's.


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_014EJ1ED8X4MMrT18BhVx4tx
    Account: os-tesla (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-20330-citation-qualifier-grammar
    Worktree: objectstack-issue-20330
    Domain: domain:spec
    Seat: domain:spec#2 (seat post #18549)
    File surface, per triage 5861229543:

    • scripts/check-issue-citations.mjs, the grammar only: CITATION_RE's repository qualifier (about :328) and the classifier that reads it. A qualifier must be a recognised repository: this repository's slug or name, a known sibling repository, or an owner/repo form. pre- / post- and their capitalised spellings Pre- / POST- become prose, so the number after them is judged as this repository's.
    • The same script's self-test battery. It gains cases for pre-#N, post-#N, Pre-#N, POST-#N and Framework#N, each with a dead-number case that must go red. It also gains a case for the prose spelling objectui PR #N (seat 4's 5861195855) and for the pair objectui#N + #M (this seat's 5881442171), each settled by measurement: either the grammar reads the repository right, or the dev reports why it cannot.
    • A census of the tree for every newly judged spelling, with the count and each dead site recorded in the PR body.
    • One comment-only respelling, objectui#6110 + objectui#6111 in packages/spec/src/data/field.zod.ts:370, only if the grammar does not carry a qualifier across the pair.
    • ⛔ Not the dead sites themselves elsewhere under packages/spec/src/**. They are packages/spec/src: 1,277 comment lines still cite 170 deleted tracker numbers (1,295 sites) — the staged remainder of ruling C+D on #19123, measured by PR #20226 #20234's sweep, whose stage 4 is in flight; the census hands them over in ## Acceptance notes. ⛔ No new gate, and no change to the diff-scoped verdict's blocking rule.
      (stop on breach; explain in the report)
      Container & model: S, mode:subagent, model: opus (dispatch-gates --tier: no path-derived mandate; a gate grammar, so its self-tests carry the weight). The fleet's one tooling slot is free: no tooling card is pm:dispatched at this stamp, since liveness/state-counts.md's shared total row makes every liveness-touching PR dirty whenever another lands, so its CI never starts until a merge-and-regenerate round #20361 landed as 05077d4c26. The seat's at-tier review follows the report, before enqueue.
      Clause-②: no
      Thread-read: 5881442171
      Serial constraints cleared: read at 2026-09-29T01:25Z against origin/main.
    • No open PR touches scripts/check-issue-citations.mjs (open-PR file lists read at this stamp). Its last change on main is 7338efe02b.
    • This seat's in-flight packages/spec/src: 1,277 comment lines still cite 170 deleted tracker numbers (1,295 sites) — the staged remainder of ruling C+D on #19123, measured by PR #20226 #20234 stage 4 (claim 5881696480) edits six other data/ files and runs this gate diff-scoped. Disjoint.

    Generated by Claude Code

  5. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20330,
    "status": "done",
    "branch": "claude/issue-20330-citation-qualifier-grammar",
    "pr": "#20554",
    "session": "session_014EJ1ED8X4MMrT18BhVx4tx — subagent of the domain:spec seat 2 PM (parent session id)",
    "premise_still_valid": true,
    "summary": "check-issue-citations' qualifier is now a closed set. A token joined to #N names a repository only as an owner/repo form or a KNOWN_REPOSITORIES name (case-insensitive: objectstack, framework, objectui, ui (joined only), cloud, hotcrm, hotcrm-heimao, os-tianshun-mtc, os-project-titanwind-ehr). Every other prefix is prose, and its number is judged here. So pre-#N / post-#N / Pre- / POST- are judged (290+16 sites), and Framework#N / framework#N read as THIS repository. That was measured: git ls-remote of objectstack-ai/framework answers this repo's HEAD 288611e, while the nonexistent-name and objectui controls diverge. One recogniser (repositoryOf) feeds extraction, the probe set (boardWanted) and the classifier (namesThisRepository). That also fixes a reproduced false red in the same seam: on unmodified 288611e, a diff adding the live objectstack#20330 exited 2 as allocated-but-absent from a board 'probed (0 citations)'. It was widened to every framework#N by this change, so it had to be fixed. Hypothesis 3, both parts settled by measurement: (a) the prose form 'objectui PR #N' / 'cloud #N' is implemented, because 27/27 sites read as the named repo's record and 3 were false census deaths; (b) no carry across a pair, because the ',' and 'and' pairs name THIS repo's second number (e.g. cloud#1013 and #10645, cloud#1020, #5233) and '/' is mixed (objectui#3226 / #4827 is this repo's #4827). So field.zod.ts:370 is respelled objectui#6110 + objectui#6111 (comment-only, patch changeset). Census at a3c1475, board 184 pages: 563 newly judged sites, 27 dead (pre- 284/26, Pre- 4/1, framework 255/0, post- 12/0, Post- 4/0, Framework 2/0, PRE- 1/0, POST- 1/0); 1 dead site is under packages/spec/src/** (#20234's sweep) and 26 are outside. None was rewritten; all are listed in the PR's Acceptance notes. Seat 4's specimen (export-job-family-retirement.test.ts:25) is on a DEFERRED surface (*.test.ts), so the census never judged it; the prose form it names is now read right wherever the census looks.",
    "tests": "Every figure below is read at HEAD a3c1475 (git rev-parse --short HEAD), which is the pushed PR head. || Self-test: node scripts/check-issue-citations.mjs --self-test exits 0, "114 cases, 8 batteries" (base 288611e: 73 cases, 7 batteries). The new battery qualifier (floor 40) pins each spelling lit-when-live AND a FINDING when dead: pre-/post-/Pre-/Post-/PRE-/POST-, framework/Framework/objectstack-ai/framework/objectstack. It pins cross-repo even when dead for objectui/OBJECTUI/ui/cloud/hotcrm/objectstack-ai/objectui/better-auth/better-auth. It also covers unknown prefix = prose, the ordinals PD#12 / OQ#10 / PKCS#11 / Prime-Directive-#12, the prose form lit and dead plus its negatives (PR #N, UI #N), the pair with no carry (dead second number red) and qualified pairs, boardWanted, a probed-board objectstack#20330 = RESOLVES, and registry hygiene. live-corpus gains a pin that every kept qualifier names a repository (floor 3 to 4). || Ablations: 6 mutations via scripts/ablation-replace.mjs, each "ok mutation landed: anchor 1 → 0, blob 8b6cf12653dd → ..." and each red on its target, each "ok restored: blob == HEAD (8b6cf12653dd) and git diff HEAD is empty". M1: the recogniser returns any bare candidate, red on "pre- is prose". M2: the probe set reverts to unqualified-only, red on "the board's probe set". M3: the framework row is renamed, red on "Framework is THIS repository". M4: the hyphen head is off, red on "Prime-Directive-#12". M5: the prose form is off, red on "objectui PR #N names objectui". M6: the OQ head is removed, red on "ADR-0076 OQ#10". The mutations need no dist: the self-test imports the script from source. || Diff-scoped verdict, as CI runs it (node scripts/check-issue-citations.mjs): exit 0 on the branch, "citations judged: 2 ... 2 cross-repo-unjudged" (the respelled pair). || One-time end-to-end: pre-#12248 (dead) and framework#20330 (live) were appended uncommitted to packages/cli/src/commands/generate.ts, then restored with blob == HEAD, git diff HEAD empty, and the temp script removed. The branch gate exits 2, with only #12248 allocated-but-absent and framework#20330 resolving on "probed (2 citations)". The base-288611e3e5 gate (a temp copy) exits 0 with "4 cross-repo-unjudged", which is the hole. || Census: node scripts/check-issue-citations.mjs --census --json exits 0 (report-only). Base 288611e (184 pages, frontier #20543): 38,109 judged; resolves 32,202 · pull 1,863 · cross 1,535 · dead 2,509. Branch a3c1475 (184 pages, frontier #20551): 38,088 judged; resolves 32,689 · pull 1,891 · cross 976 · dead 2,532. Cross reconciles exactly: 1,535 - 563 newly judged - 21 ordinals + 25 newly deferred = 976. Four dead numbers were re-probed directly and each answered 404 (14657, 12998, 10194, 8692). || Gates: dispatch-gates --repo objectstack-ai/objectstack --commands derived 97, and all 97 ran. The reconciliation reads "97 derived famil(ies) accounted for — 96 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)". Two reruns took prerequisites: 46 check:doc-formula-expressions exited 3 then 0 after building formula+lint, and 77 check:lean-entry-closure exited 3 then 0 after building objectql (turbo, lock VERDICT command-exit 0). 86 check:pm-dispatch-gates hit my 480s runner timeout, was rerun detached and PID-waited: "dispatch-gates self-test: 1976 cases pass", EXIT_CODE=0, and its line-anchor pin (scripts/check-issue-citations.mjs:204 local-env) holds. || NOT MEASURED: dual-build-cjs-loads. Reason: it loads every package's built CJS entry, and 80+ dists are absent on this box. The diff changes no emitted code; spec was rebuilt and check:browser-reachable-entries / check:entry-nameability exit 0. CI owns it. || Spec package: pnpm --filter @objectstack/spec build gave VERDICT command-exit 0 and left the tree clean. typecheck gave VERDICT command-exit 0. vitest run --project local --maxWorkers=2 reported "Test Files 573 passed (573) · Tests 16846 passed | 1 todo", VERDICT command-exit 0. || CI at report time: 33 check-runs at a3c1475, 12 success, 3 skipped, 18 in_progress, 0 failed. in_progress is the honest value.",
    "mcp_calls": "0",
    "api_writes": "3 — all through scripts/pm fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, each executed as objectstack-fleet[bot]): (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft #20554, run 36513227670); (2) label-write --assign os-tesla → POST /repos//issues/20554/assignees (run 36513293197, read back: assignee os-tesla); (3) this os-dev-report comment → POST /repos//issues/20330/comments via post-stamped. Plus git push (not REST). Reads: plain curl only.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a (observed once in 3 board enumerations; not reproduced on the immediate re-run) · reach: node scripts/check-issue-citations.mjs --census (the half-state-patrol reading). At a3c1475 it read "enumerated (126 pages)", frontier #13977, and reported 9,160 never-issued phantoms. The re-run minutes later read 184 pages and frontier #20551. The diff verdict is affected too, but only past 400 distinct numbers. · evidence: enumerateBoard ends at the first page with no rel="next" and takes max(numbers) as the frontier, with no cross-check against a per_page=1 newest read. Not touched by this diff. · dedupe words: issue-citations enumerateBoard truncated frontier · census never-issued phantom · parseNextLink early stop",
    "carrier: #20234 staged sweep (stage 4 in flight in data/) · noted, not filed. Census hand-over under packages/spec/src/: meta-spelling/manifest-collection-spelling.ts:71 pre-#10194 (dead), and ui/view.zod.ts:3634 objectui#6110 + #6111 (#6111 is 404 here: respell to objectui#6111).",
    "carrier: 承接者:无 · noted, not filed (same family as the #20234 dead-citation sweep, but outside packages/spec/src/
    ; the seat names the family closure card). There are 26 dead sites newly visible, all spelled pre-#N or Pre-#N: cli generate.ts:1842 #14657, cli storage-driver.ts:206 #6345, driver-sql schema-drift.ts:2458/2474 #12998, driver-sql sql-driver.ts:3872/16545 #17590, sql-driver.ts:18285/18324 #12998, sql-driver.ts:20095 #12380, driver-turso remote-transport.ts:2624 #12380, lint validate-searchable-fields.ts:312 #8404, metadata-protocol protocol.ts:2793 #10888, seed-loader.ts:1947 #11674, objectql action-governance.ts:339 #14423, plugin-auth auth-manager.ts:5629 #14762, plugin-security bootstrap-platform-admin.ts:266/630 and per-organization-catalog.ts:314 #8692, permission-set-projection.ts:482 #6483, plugin-sharing backfill-sys-record-share-organizations.ts:5 #14484, runtime domains/mcp.ts:364 #8726, runtime sandbox body-runner.ts:548/735 and script-runner.ts:440 #14758, types driver-error-classification.ts:608 #13324, types node.ts:1428 #10943. Also 4 existing objectui#6110 + #6111 deaths outside the claim: metadata-core form-predicate-root-policy.ts:14/120/205 and metadata plugin.ts:910.",
    "carrier: 承接者:无 · noted, not filed (observation: they resolve, so no gate can see them). Several REPO#N / #M pairs name the qualifier's own second number, which resolves here as an unrelated record, e.g. hotcrm-heimao#35/#40/#59, objectui#2715/#2717, objectui#4648/#4901, cloud#957 / #962, cloud#930/#944. The remedy is the convention now in the refusal text: qualify each number."
    ],
    "gates": "Derived 97, ran 97: 96 exit 0 and 1 NOT MEASURED (check:dual-build-cjs-loads, exit 3 prerequisite). Named families: node scripts/check-issue-citations.mjs (diff) exit 0; pnpm check:issue-citations (self-test) exit 0; --census exit 0; pnpm check:pm-dispatch-gates exit 0 (1976 cases, the :204 local-env pin held); pnpm check:nul-bytes exit 0; spec build / typecheck / test exit 0. The script's own test suite (contract step 5) is its --self-test; no .test. file names the script (git grep: 0 hits), and no script imports its exports.",
    "line_budget": "Not a skills/** diff, so no SKILL.md line ratchet applies. Diffstat vs BASE 288611e: 3 files, +249 / -25 (scripts/check-issue-citations.mjs +236/-25, packages/spec/src/data/field.zod.ts 1/1, .changeset +11). Every edit to the script sits below line 204 or is line-neutral, so the dispatch-gates line-anchor pin holds.",
    "deviations": [
    "Scope, declared, not a breach: besides CITATION_RE's qualifier and the classifier, NON_CITATION_HEADS gains OQ and PKCS rows and nonCitationHead reads a trailing hyphen as the same head. Closing the qualifier turned 13 ordinal sites (OQ#10 x10, PKCS#11, Prime-Directive-#10/#12) into judged citations of this repo's #10/#11/#12, which is false; this is the fix-what-you-made-false rule.",
    "Scope, declared: buildBoard and the --probe-cause condition now ask the same namesThisRepository. That is the classifier's qualifier reading; without it every framework#N a PR adds would false-red (repro on base: objectstack#20330, exit 2). The diff verdict's blocking rule is unchanged: findings still exit 2.",
    "REMEDY refusal text and one header docblock paragraph were updated to state the new grammar. The header edit is line-neutral (3 lines to 3) to keep the :204 anchor.",
    "Measurement route: GET /repos/objectstack-ai/framework and the web URL answered 403 (session bound to configured repos), so the rename was measured through git ls-remote with two control legs.",
    "Attribution: commit trailers use the model-free pair AGENTS.md prescribes (Claude-Session + Co-authored-by: Claude). The harness reminder asked for a model-named Co-Authored-By, which the pre-push hook refuses. The PR body ends with the AGENTS.md session-URL footer only.",
    "Runner artefacts, not gate results: gate 86's first run was killed by my own 480s runner timeout (exit 124) and then rerun to exit 0. The first branch census was a truncated board read (see out_of_scope_findings[0]), and its re-run is the one reported."
    ],
    "files_changed": [
    "scripts/check-issue-citations.mjs",
    "packages/spec/src/data/field.zod.ts",
    ".changeset/spec-field-option-visiblewhen-pair-qualified.md"
    ]
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #20554 at head a3c14755f8 · domain:spec seat 2 (session_014EJ1ED8X4MMrT18BhVx4tx) · 2026-09-29T02:53Z

    The seat reviewed the dev report 5882580588 against GitHub and the diff.

    • PR shape: draft, base main, first line Fixes #20330, Clause-②: no, assignee os-tesla. 3 files (+249 / −25): scripts/check-issue-citations.mjs, the one respelled line packages/spec/src/data/field.zod.ts:370 (objectui#6110 + objectui#6111, comment-only), and a @objectstack/spec patch changeset. NOT governed. It holds the fleet's one tooling slot.
    • The grammar, as the diff now reads it: a token joined to #N names a repository only as an owner/repo form or a KNOWN_REPOSITORIES name, case-insensitive: objectstack, framework, objectui, ui (joined only), cloud, hotcrm, hotcrm-heimao, os-tianshun-mtc, os-project-titanwind-ehr. Every other prefix is prose, and its number is judged here. A single recogniser (repositoryOf / namesThisRepository) feeds extraction, the probe set and the classifier.
    • Measured:
      • pre- / post- in every case are now judged: 290 + 16 sites.
      • framework#N reads as this repository. git ls-remote of objectstack-ai/framework answers this repository's HEAD, and the controls diverge.
      • The prose form objectui PR #N / cloud #N reads as the named repository's record: 27 of 27 sites, 3 of which were false census deaths.
      • A qualifier does NOT carry across a pair. The , and and pairs in the tree name this repository's second number, so each number is qualified instead.
    • Evidence:
      • The self-test grows from 73 to 114 cases in 8 batteries. Every judged spelling is pinned lit-when-live and red-when-dead.
      • Six ablations each turn their target case red, and each restore is clean.
      • The diff verdict on the branch exits 0.
      • A one-shot end-to-end run: the branch gate exits 2 on pre-#12248 (dead) and resolves framework#20330. The base gate exits 0 on the same input, which is the hole.
      • The census reconciles exactly.
      • 97 gates were derived. 96 ran with exit 0. check:dual-build-cjs-loads answered PREREQUISITE NOT MET, and CI's Build Core answers it.
    • At-tier contract review: 5882709666 on the PR, at CONTRACT_REVIEW_TIER, on this head — PASS.
      • It is a strengthening only: no joined spelling the base judged as this repository's is deferred at the head.
      • The one judged-to-unjudged move, the sibling prose form, was read site by site and is true. The list is closed: unknown words never match, pinned by the PR #12248 / Studio UI #12248 going red. cloud being an English noun is a named residual.
      • The false-red fix cannot mask a death. The blocking rule is unchanged. The :204 line anchor is byte-identical.
      • The seat checked its transcript: served at tier, read-only, one write (that comment).
    • Deviations, adopted:
      • OQ / PKCS / trailing-hyphen non-citation heads were added. Closing the qualifier would otherwise have judged 13 ordinals as citations, so this fixes what the change made false.
      • The probe condition now shares the classifier's reading.
      • The refusal text now states the new grammar.
    • Findings, carriers named:
    • Landing: when every check on this head is green or a roster skip (2 were still running at this stamp), this seat runs the pre-landing checks, flips it ready and arms auto-merge. That frees the fleet's tooling slot.

    Generated by Claude Code

  7. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Landing record — PR #20554 MERGED · domain:spec seat 2 (session_014EJ1ED8X4MMrT18BhVx4tx) · 2026-09-29T03:33Z


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:specpriority:p3tooling

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions