Skip to content

fix(gates): judge spec member citations in published prose, not only in comments (objectui#7995) - #8370

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-7995-spec-symbol-derivation-scan-surface
Sep 7, 2026
Merged

baozhoutao merged 2 commits into
mainfrom
claude/issue-7995-spec-symbol-derivation-scan-surface

Conversation

@claude

@claude claude Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #7995

Both properties objectui#7995 names are closed. One of them this PR closes; the other was already closed two days before the card was filed, and that half is a measurement, not an implementation. Everything below is a reading taken on this branch, and every number is reproducible from the commands quoted.

Property 1 — prose in content/docs/** is judged by nothing. CLOSED HERE.

Probe before, on base 411a132c0, using the gate own machinery rather than a regex — a false spec attribution planted as prose in content/docs/fields/lookup.mdx:

+ Per `@objectstack/spec`, `SelectOptionSchema.icon` is the canonical option icon key.
  ->  node scripts/check-spec-symbol-derivation.mjs   exit 0   (SILENT)

SelectOptionSchema is a live export and icon is refused by name (unrecognized_keys, probed against the installed 17.3.0), so the sentence is false at the member — and the gate had nothing to say, because sourceFiles() walks packages/*/src and nothing else.

Probe after, same plant on this branch:

->  exit 1
    1 passage cites a spec member the spec does not declare:
      `SelectOptionSchema.icon`  content/docs/fields/lookup.mdx:194

How. Rule 4 at member granularity asks only whether a NAME the prose spells exists in the spec — no declaration, no compile-time tie, no claim phrase. That question is transport-free, so its scan surface widens to content/docs/** and the rule itself does not move: the citation grammar is factored into a single memberCitationsIn, and the TypeScript comment scanner was refactored ONTO it rather than having a second copy written beside it. Two dialects of one rule is exactly what commandment 0.1 (contract-first) forbids, and a gate is not exempt. What stays transport-specific decides nothing: a comment BLOCK is the scope on one side, a markdown PARAGRAPH on the other. That analogue is load-bearing — a markdown heading carries no sentence terminator, so without blank lines as hard boundaries the same-sentence test would measure across a heading into an unrelated section (pinned).

Census, taken BEFORE enforcement

A gate written against an unknown population is how a scan-surface widening lands as a wall of reds. Measured on 411a132c0 against @objectstack/spec@17.3.0, over content/docs/**, with an instrumented copy:

documentation pages                                         184
  mentioning @objectstack/spec at all                        32
raw Symbol.member citations in those pages                  183
  naming a spec export whose member set is knowable            5
  within CLAIM_WINDOW and the same sentence                    3
    resolve against the family authored shape                  3
    excluded as Zod API                                        0
    DANGLING                                                   0

The three survivors are FormViewSchema.type, DashboardWidgetSchema.filterBindings and PageHeaderProps.icon, and all three are correct.

Fix-or-ledger decision: neither is needed — the false set is EMPTY. No prose correction, no waiver, no ledger, no allowlist. Under the four axes that is the only defensible outcome: 实际业务需求 — there is no measured debt to serve, so a ledger would be a declaration with nothing behind it; 项目长远合理性 — this repo has landed the shrink-only ledger idiom six times this shift and a seventh with zero rows would dilute the idiom, not extend it; 防 AI 犯错 — an empty ledger block is a name reserved for a future waiver, which is the opposite of a tightening; 创业阶段不扩散 — the widening stops at content/docs/**, the surface the card demonstrated, and does not reach package READMEs or docs/adr/**.

Also measured: skipping fenced code moves the raw count 183 to 128 and leaves the judged 5 / 3 / 0 identical, so there is no fence rule. A fence rule would have been a precision claim with nothing behind it.

Property 2 — the dangling member tail. ALREADY CLOSED; the card premise is stale.

Probe before, on base 411a132c0, a dangling member citation planted in a scanned source (packages/types/src/select-option.ts):

/** Per `@objectstack/spec`, `SelectOptionSchema.icon` is the option icon key. */
  ->  exit 1
      1 comment cite a spec member the spec does not declare:
        `SelectOptionSchema.icon`  packages/types/src/select-option.ts:61

The gate already judges the member tail. It has since d28d87bf4 (PR #7617, objectui#7513, merged 2026-09-04) — two days before objectui#7995 was filed on 2026-09-06. The card sentence "It validates a citation at symbol granularity" describes the gate as it was before that commit.

Second, and independently: the two specimens the card names are no longer dangling at all. Resolved against the installed @objectstack/spec@17.3.0 through the gate own resolver:

SelectOptionSchema.description  ->  LIVE
FieldSchema.rows                ->  LIVE
DashboardSchema.title           ->  DANGLING   (the third #7513 specimen, still dangling)
SelectOptionSchema.icon         ->  DANGLING   (the specimen used by the probes above)

17.3.0 executes the maintainer 2026-08-25 ruling on objectui#6140 / objectui#6153 and declares both keys; packages/types/src/__tests__/select-option-spec-extension-7014.test.ts already pins that boundary in both directions. So a PR that "corrected" those two citations today would be writing a new false claim in the opposite direction.

Residue declared: this PR implements nothing for property 2. It measures it, and it keeps the property under test through the shared grammar — the probe above is a leg of the ablation, and the pin asserts a comment and a page are judged identically on the same sentence.

Ablation, on the committed tree

Every leg: trap plus restore, mutation proven on disk by a grep -c on the exact injected text before any result is read, restore proven by git hash-object equal to the HEAD blob AND an empty git diff HEAD. No build is involved — this gate is a scripts/ entry point that reads sources directly and resolves the spec from node_modules, so there is no dist/ for a stale artifact to hide in.

(iv) unmodified tree, same run                                   exit 0  GREEN
(i)  false PROSE citation in content/docs/**                     exit 1  RED, names file and citation
(ii) dangling member tail in a scanned source                    exit 1  RED, names the member
(iii)CORRECT prose citation (SelectOptionSchema.description)     exit 0  GREEN  (not reddening on everything)
(v)  ablate the prose call site in main(), replant (i)           exit 0  the RED vanishes
(vi) ablate the PARAGRAPH boundary                     vitest exit 1, 4 pins red
(vii)ablate the shared grammar inside the prose scanner vitest exit 1, 4 pins red
final unmodified                                    gate exit 0, vitest 69/69, git diff HEAD empty

Legs (vi) and (vii) exist because (v) alone does not discriminate: (v) proves the WIRING in main() does the work, and the unit pins stay green under it because they call the scanner directly. (vi) and (vii) are what prove the PINS bite.

Gates, each captured by redirect and read from the gate own verdict line

node scripts/check-spec-symbol-derivation.mjs                       exit 0
pnpm exec vitest run scripts/__tests__/check-spec-symbol-derivation.test.ts  exit 0   69 passed
pnpm exec vitest run scripts/__tests__/                             exit 0   120 files, 3584 passed
pnpm check:doc-snippets                                             exit 0
pnpm check:doc-examples                                             exit 0
pnpm check:doc-fences                                               exit 0
pnpm check:skill-examples                                           exit 0
pnpm check:lint-rule-coverage                                       exit 0
pnpm check:entry-guard                                              exit 0
pnpm check:control-bytes                                            exit 0
pnpm check:comment-mask-corpus                                      exit 0
pnpm check:unreferenced-sources                                     exit 0
pnpm type-check:scripts                                             exit 0
pnpm lint:root                                                      exit 0   32 warnings, 0 errors
node scripts/check-changeset-presence.mjs                           exit 0   no changeset owed
node scripts/check-governed-queue-guard.mjs --test (both paths)      exit 0   NOT GOVERNED

check:doc-snippets, check:doc-examples and check:skill-examples first returned exit 2, PRECONDITION NOT MET (unbuilt packages) — read as NOT MEASURED, never as a failure. They were re-run after turbo run build and are the exit 0 above.

pnpm lint:root is the repository-wide root run, not a narrowed one, so no narrowing needs declaring for it. The three package-level farms (pnpm lint, pnpm test, pnpm type-check through turbo) are CI-owned; this diff is two files under scripts/, which no package build or package test reads.

Filed while here

objectui#8369 — check-lint-rule-coverage walks gitignored build output (apps/console/plugin.js), so the gate and its pin both turn red after any local build and green again when the artifact is removed, with nothing else changed. Hit on this branch and confirmed independent of this diff by that control. Not touched here.

Files

scripts/check-spec-symbol-derivation.mjs
scripts/__tests__/check-spec-symbol-derivation.test.ts

No changeset is owed: neither path is published source of a released package (verified by check-changeset-presence.mjs, exit 0).

Draft, per the dispatch. The seat did not flip it ready and did not queue it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr


Generated by Claude Code

…in comments (objectui#7995)

`check-spec-symbol-derivation` read only the comments of `packages/*/src`, so a
false `@objectstack/spec` attribution written as prose in `content/docs/**` was
judged by nothing. objectui#7537 is the receipt: PR #7510 corrected three JSDoc
copies of one such claim and a fourth copy in `content/docs/fields/lookup.mdx`
shipped untouched, because no gate reads it.

Rule 4 at member granularity asks only whether a name the prose spells exists in
the spec, which needs no declaration, no tie and no claim phrase. So its scan
surface widens to `content/docs/**` and the rule itself does not move: the
citation grammar is factored into one `memberCitationsIn`, and the TypeScript
comment scanner was refactored ONTO it rather than having a second copy written
beside it. A comment BLOCK is the scope on one side, a markdown PARAGRAPH on the
other; everything that decides a verdict is shared.

Census taken BEFORE enforcement, on 411a132 against `@objectstack/spec@17.3.0`:
184 documentation pages, 32 mentioning the spec, 183 raw `Symbol.member`
citations, 5 naming a spec export with a knowable member set, 3 surviving
proximity and same-sentence, 3 resolving, 0 dangling. Zero corrections, zero
waivers, no ledger. Skipping fenced code moves the raw count 183 -> 128 and
leaves the judged population identical, so there is no fence rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
…jectui#7995)

The count line read "1 passage cite"; the singular arm now carries the verb.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
@github-actions github-actions Bot added the tests label Sep 7, 2026
@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

ACCEPT (#7995) — reviewed on the tree at head 072c20e58 against origin/main 37149ec4f, read 2026-09-07T14:49Z; domain:devx @ objectui seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr. Merges clean. Two files under scripts/, +334/−36.

Property 1 — closed, and the probe is the proof. A false prose citation planted in content/docs/fields/lookup.mdx was SILENT on the base (exit 0) and reds on the head naming file, line and citation. The citation grammar was factored into one memberCitationsIn and the comment scanner refactored ONTO it, so the two transports cannot drift into two dialects — only the cutting (comment block versus markdown paragraph) is transport-specific. That is the right shape: the rule did not move, its reach did.

Property 2 — FALSIFIED, and the seat verified it independently. The card says the gate drops the member tail. It does not, and has not since d28d87bf4 (PR #7617, objectui#7513, "judge spec citations at member granularity") — which I confirmed on the tree: that commit is on main and landed 2026-09-04, two days BEFORE this card was filed on 09-06. The card's own two specimens (SelectOptionSchema.description, FieldSchema.rows) resolve LIVE against the installed @objectstack/spec@17.3.0, which executes the maintainer's 2026-08-25 ruling on #6140/#6153. So correcting them today would have written a NEW false claim in the opposite direction. The dev measured this instead of implementing against a stale premise, and that is the outcome I want from a brief whose facts have moved.

Census before enforcement, and the honest consequence: 184 pages, 32 mentioning the spec, 183 raw citations narrowing to 3 that resolve — and zero dangling. So the widening lands with zero corrections, zero waivers and NO ledger. The reasoning for refusing a seventh empty ledger is right on every axis: a ledger with no rows is a name reserved for a future waiver, which is the opposite of a tightening.

Ablation discriminates rather than merely reds. Seven legs, including a CORRECT prose citation that must stay GREEN, plus two legs (vi, vii) whose only job is to prove the PINS bite rather than the wiring — because ablating the call site alone leaves the unit pins green and would have proved nothing about them.

On the dev's routing question: Fixes #7995 is right and this seat confirms it. Both properties are judged on the tree; one by this PR, one by #7513 before the card existed; the residue is declared here and in the PR. Reopening is cheap if the queue reads the card as asking for more, and the measurement above is what it would need.

Two things carried out of this run: the gate's rule reads a SPELLING, not an assertion, so a page explaining that the spec REFUSES a key reads like one claiming it declares it — an inherited property of the comment transport, now documented in the header, with a measured population of zero and the escape being the wording the repaired comments already use. And #8369 is filed against check-lint-rule-coverage — the gate that landed 45 minutes ago on #7908 — for walking gitignored build output, so it reds after any local build. It is not red on main (CI builds clean), so this is a dev-experience defect for triage to grade, ⛔ not a main-damage event and ⛔ not this seat's to self-grade.

Next: CI convergence → flip ready → post-flip guard → auto-merge SQUASH → queue confirmation → landing probe (the prose scan reaching content/docs/** and the shared grammar on re-fetched origin/main; control: the gate still green over the source transport) → LANDED here and on #7995, which closes via Fixes.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 7, 2026 15:01
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 7, 2026
@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Armed: PR #8370 flipped ready at 2026-09-07T15:01:24Z on head 072c20e58 after CI converged all green (28 checks, read 15:01:16Z); post-flip Governed Surface Queue Guard completed/success at 15:01:46Z; auto-merge SQUASH enabled; queued (timestamp in the REST timeline). Landing stroke on merge: probe on re-fetched origin/main — the shared memberCitationsIn grammar and the prose transport reaching content/docs/**; control: the gate's summary still counting the source transport (1353 sources) — then LANDED here and on #7995, which closes via Fixes. — domain:devx @ objectui seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr.


Generated by Claude Code

Merged via the queue into main with commit ab03bff Sep 7, 2026
31 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-7995-spec-symbol-derivation-scan-surface branch September 7, 2026 15:16
@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

LANDED: merged at 2026-09-07T15:16:54Z as ab03bffae (squash — one parent, via the merge queue). Content probe on re-fetched origin/main (read 15:17Z): the shared memberCitationsIn grammar present (5 occurrences), the prose transport reaching content/docs (4), and the control — the source transport still counted in the gate's summary (3). Merge commit is an ancestor of origin/main. #7995 closes via Fixes; its pm:dispatched is stripped in the same stroke. — domain:devx @ objectui seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr.


Generated by Claude Code

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

Labels

Projects

None yet

2 participants