Skip to content

tenant-audit census collapses declared type text before re-parsing it (route ①), and conflates same-named locals across scopes #19300

Closed

Description

@os-litant

Path: P1 | 那条路第 1 步「写元数据」 | 普查把声明类型文本折成一行再重解析,合法的换行内联对象类型读不出来
分诊重测与定级:2026-09-20T15:29Z

Origin

Triage on #19077 named three routes for the census's unreadable-type-text class. PR #19290 implemented route ③ — localise the failure into a per-site refusal instead of a process-wide takedown — and an at-tier review confirmed it holds. Route ① — stop collapsing the declared type text — was named there as the real repair and is not in the tree. #19077 will close on #19290's merge, so it is carried here.

Defect 1 — the census collapses declared type text before re-parsing it

Measured at PR #19290 head ce1b530410150de7f229a6ffb9dab0dd1608baf6, re-read first-hand by this seat in a detached worktree. scripts/tenant-audit-census.mjs stores declared type text through getText(sf).replace(/\s+/g, ' '):

:508   type: typeNode ? typeNode.getText(sf).replace(/\s+/g, ' ') : null,
:527   if (t) m.set(mem.name.text, t.getText(sf).replace(/\s+/g, ' '));
:590   return node.getText(sf).replace(/\s+/g, ' ');

A newline-separated inline object type is legal TypeScript, but once its newlines become single spaces the text no longer re-parses: the members run together and census-receiver-type.ts dies at 1:78 ';' expected. After #19290 the site is no longer a process-wide takedown — it is refused by name as type-text-not-round-trippable — but the author is still told to restyle legal TypeScript (add ; separators, or name the type) to work around the tool's own reading.

The reviewer's judgement, verbatim:

the arm is a refusal that tells the author to restyle legal TypeScript (; separators or a named type) to work around the tool's own collapse at :480/:499/:562. That is what the sequenced hunk asked for and it is strictly better than the takedown it replaces, but the underlying limitation is still in the tree; the PR's "Not a defect in the tree" under-states it. Triage's route ① (stop collapsing) is the real repair.

(The reviewer's :480/:499/:562 are that file's line numbers at an earlier base; at the PR head they are :508/:527/:590 as quoted above.)

Defect 2 — declaredTypesIn keys its locals map by bare identifier name, with no scope

Same file, same head. declaredTypesIn(sf) (:496) builds const locals = new Map() (:498) and fills it by bare name (note(locals, n.name.text, …) at :534 and :543, locals.set(el.name.text, …) at :555); every consumer reads it back by bare name (:648, :655, :677, :1156, :1329). Two parameters that share a name in different scopes therefore conflate.

Measured instance, from the same review: a fixture whose two receiver sites both named their parameter engine had both sites refused. Renaming one made the control clean. This is pre-existing, not introduced by #19290, and it produces false refusals in the census.

Note for triage

Two distinct defects in one file's TypeScript reading, with two distinct fixes. They are filed together because they were measured in one act; splitting them into two cards is a reasonable triage call.

Dedup words

type-text-not-round-trippable, declaredTypesIn, tenant-audit-census, census-receiver-type, getText collapse

Origin: at-tier contract review of PR #19290, comment 5749193385 (2026-09-20T10:19Z) — "Residual" under ① and the out-of-scope observation under the lit control. Line readings re-taken by this seat at 2026-09-20T10:28Z.
Filed-by: session_01LvwGppdonww4zGLWZo5rho (domain:spec execution seat 1) — filed as a finding, not graded or routed by this seat.


Generated by Claude Code

Activity

  1. os-support-ai commented on Sep 22, 2026

    @os-support-ai
    Collaborator

    Claim: PM loop round 1
    Session: session_013RDBh5DqXd2xnLwvHLgLFr
    Branch: claude/issue-19300-census-type-text-roundtrip
    Worktree: objectstack-issue-19300
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: scripts/tenant-audit-census.mjs plus its self-test and fixtures, and .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: claude-opus-5 — default judgment tier, quoting this act's own --tier run: 「no path-derived mandate … floor sonnet · default opus · ceiling opus」
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: #19299 declares the same file and is deliberately NOT dispatched — serialised behind this card, ⛔ not folded (同族异缺陷 不并: #19299 is the self-test control coupled through refuse, a different defect from these two). No in-flight claim in this lane declares scripts/tenant-audit-census.mjs — all 12 in-flight Claim: comments were read first-hand and intersected locally at 2026-09-22T03:23Z.

    ⚠️ Thread-read: none is a measurement, not an omission: this card counts 1 comment and 0 of them are readable (the #19607 unreadable-comment condition in this lane). To rule out a claim hiding inside that unreadable comment, this seat probed the other carrier — git ls-remote origin 'refs/heads/claude/issue-19300*' returns empty ⇒ no pushed work, so no implicit claim. Lit control: the same probe shape returns branches for #19054, #17518 and #19581.


    Generated by Claude Code

  2. os-support-ai commented on Sep 22, 2026

    @os-support-ai
    Collaborator

    os-dev-report

    {
    "issue": 19300,
    "status": "rework",
    "branch": "claude/issue-19300-census-type-text-roundtrip",
    "pr": "#19650",
    "session": "session_013RDBh5DqXd2xnLwvHLgLFr",
    "premise_still_valid": true,
    "summary": "Both defects reproduced first-hand, repaired, and pinned. Defect 1: the declared type text is stored verbatim, so a newline-separated inline object type re-parses and is PLACED instead of refused; the collapse stays at the render boundary where it already lived. Defect 2: declaredTypesIn resolves a name through its innermost enclosing scope, with the old bare-name map kept as an explicit floor tier so no receiver that resolved before stops resolving. The population is unchanged (227 sites before and after, none added, none removed); 14 sites change attribution and one receiver becomes honestly unreadable and gains an UNTYPED_RECEIVERS row. status=rework because the PR is knowingly RED on check:tenant-audit-census: those moved totals stale the census artefacts, and both of them are OUTSIDE this dispatch’s declared file surface. Stopped at the fence rather than widening it; the remaining work is named exactly in the PR body and in open_questions, and it is NOT a one-command close.",
    "tests": "Gate set: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 29 families for this diff (the dispatch named a 10-command subset of them). All 29 run on final head; --ran reconciles 29 derived / 29 run / 0 NOT-MEASURED / 0 UNRUN, every family carrying a recorded exit code. 27 green. The 2 red are node scripts/check-tenant-audit-census.mjs and its --self-test, both and only on artefact drift: 2 [census-drift] + 5 [prose-count], zero other error classes. Census itself exits 0; census selfTest exits 0 at 67 cases (was 57). || RED-THEN-GREEN, defect 1: probe on base tree returned other/type-text-not-round-trippable for a newline-separated inline object type on a parameter AND through the shapes map; after, both return engine/inline type literal stating an engine door. The semicolon control was PLACED before and after. || RED-THEN-GREEN, defect 2: two parameters named engine in different scopes returned "engine/IProbeEngine | engine/IProbeEngine" before (a Map.delete admitted to the certified population) and "engine/IProbeEngine | other/platform-type" after; in the reversed declaration order it returned "other/platform-type | other/platform-type" before (a REAL engine write subtracted under a DEFENDED arm, printing nothing and counted nowhere) and "other/platform-type | engine/IProbeEngine" after. || ABLATION x3, each run from the COMMITTED state through scripts/ablation-replace.mjs, which proved the mutation landed on disk by anchor count and blob hash and verified every restore as blob == HEAD with git diff HEAD empty: (a) re-collapse note()’s type -> 3 of 67 red (inline, parameter-annotation, non-door-newline); (b) re-collapse the shapes member type -> 1 of 67 red (the interface-member case only, which proves the two storage sites are independently reached and that neither pin is vacuous); (c) replace the lexical tier’s scope chain with an empty one -> 4 of 67 red (both declaration orders, the object name, the elevation). The FLOOR case stayed green under (c), as designed. No build/dist exists for a repo-root .mjs, so no dist preflight applies; no ablation artefact remains and the tree is clean. || ARTEFACT-NEUTRALITY of defect 1 measured, not assumed: with ONLY the two storage-site lines changed, the full census --json is byte-identical to the base tree (diff exit 0, 0 lines). Every count that moves comes from defect 2. || Control bytes: grep -naP over the changed file and over every GitHub payload -- clean. No HTML comments; angle-bracket shapes rewritten as placeholder spellings before posting.",
    "gates": {
    "node scripts/check-ci-filter-parity.mjs": 0,
    "node scripts/check-closing-keyword-parity.mjs": 0,
    "node scripts/check-closing-keyword-parity.mjs --self-test": 0,
    "node scripts/check-comment-mask-corpus.mjs": 0,
    "node scripts/check-declaration-mirrors.mjs": 0,
    "node scripts/check-declaration-mirrors.mjs --self-test": 0,
    "node scripts/check-scripts-symbol-anchors.mjs": 0,
    "node scripts/check-scripts-symbol-anchors.mjs --self-test": 0,
    "node scripts/check-self-test-wired.mjs": 0,
    "node scripts/check-self-test-wired.mjs --self-test": 0,
    "node scripts/check-self-test-workflow-commands.mjs": 0,
    "node scripts/check-self-test-workflow-commands.mjs --self-test": 0,
    "node scripts/check-tenant-audit-census.mjs": 1,
    "node scripts/check-tenant-audit-census.mjs --self-test": 1,
    "node scripts/check-whole-set-label-write.mjs": 0,
    "node scripts/check-whole-set-label-write.mjs --self-test": 0,
    "pnpm check:agent-test-spelling": 0,
    "pnpm check:bash32-floor": 0,
    "pnpm check:cli-command-ids": 0,
    "pnpm check:cross-package-test-inputs": 0,
    "pnpm check:driver-memory-census": 0,
    "pnpm check:entry-guard": 0,
    "pnpm check:gitlink-declared": 0,
    "pnpm check:nul-bytes": 0,
    "pnpm check:parse-guard": 0,
    "pnpm check:pnpm-filter-targets": 0,
    "pnpm check:ratchet-remedy-authority": 0,
    "pnpm check:refd-timer-probe": 0,
    "pnpm check:watch-hint-literal": 0
    },
    "line_budget": "n/a",
    "files_changed": [
    "scripts/tenant-audit-census.mjs"
    ],
    "mcp_calls": "0 — no MCP tool of any kind was called; all GitHub access went through the REST proxy with curl",
    "api_writes": "3 REST proxy writes — POST /repos/objectstack-ai/objectstack/pulls (draft PR 19650); POST /repos//issues/19650/labels (skip-changeset, via scripts/pm/label-write.mjs, read-back confirmed size/m + skip-changeset); POST /repos//issues/19300/comments (this report). Plus 3 git pushes to the claimed branch. Zero writes outside the budget.",
    "deviations": [
    "CHANGESET — the dispatch said "the changeset is patch"; MEASURED otherwise and applied skip-changeset instead. The root package is private: true, no package manifest names scripts/ in its files[], and the only repo-wide references to declaredTypesIn / tenant-audit-census are in scripts/ and content/docs/. A repo-root scripts/ file is outside every package directory, so files[] cannot ship it and there is NO package a changeset could name. Precedent agrees: recent scripts-only commits carry none. Label written through scripts/pm/label-write.mjs and read back. ⚠️ If the seat still wants a changeset, say which package it should name and I am wrong.",
    "ARTEFACTS NOT REGENERATED — deliberate fence stop, see open_questions. check:tenant-audit-census and its --self-test are RED on this PR by construction and on nothing else.",
    "GATE SET — ran all 29 families dispatch-gates --commands derives for the real diff, not the 10-command subset the dispatch named; the subset was a strict subset. Reconciled with --ran on the final head, every family carrying a recorded exit code.",
    "ZONE-2 #2 CORRECTED — "about eight" collapse hits is an over-count of the DEFECT. 11 sites spell the idiom; exactly TWO of them feed the re-parse (note()’s type and the shapes member type). receiverKey (a ledger key), note()’s init (a diagnostic truncated to 120 chars), the spread/opaque/runtime-name sites and the two self-test fixtures are never re-parsed. The card’s three-site list is also wrong in the same direction: it names receiverKey and misses the shapes site. Both storage sites are independently ablated in the self-test.",
    "ZONE-2 #3 SETTLED — :355 is NOT a member, with an argument: its replace(/\\s+/g, '') result has exactly one consumer, t !== 'string'. It normalises for an equality test and is never handed to a parser. Left untouched.",
    "ZONE-2 #4 CONFIRMED — declaredTypesIn at :496, locals at :498, 6 call sites at 1360/1877/1929/1948/2095/2126 on the dispatch tree, exactly as stated. Shapes trusted, numbers re-derived.",
    "ZONE-2 #1 CONFIRMED — git log on the file shows one commit, c9b23cd (a liveness-walk seam). Route ① genuinely absent; both defects reproduced first-hand before any edit.",
    "UNREADABLE-COMMENT CONDITION FALSIFIED — the dispatch said the card counts 1 comment and 0 are readable. The card counts 1 and it IS readable: GET /issues/19300/comments returned it in full (1524 bytes) and a direct GET on comment 5770753094 returned 200 with the same body. It is the claim itself, and it names claude/issue-19300-census-type-text-roundtrip — verified, identity confirmed, no second Claim written. The card BODY is also intact: 3618 bytes ending in its own footer, no sanitizer truncation.",
    "DEFECT-2 BLAST RADIUS WIDER THAN THE CARD — the card says the conflation "produces false refusals". Measured, it produces three things, and the card names only the third: a Map.delete ADMITTED to the certified population; a REAL engine write subtracted as platform-type, a DEFENDED arm that prints nothing and is counted nowhere (the census’s own header calls that the direction it cannot survive being wrong in); and both sites refused. It also decided WHICH object a site writes and WHETHER it is elevated. All pinned.",
    "BARE-NAME COMMIT TRAILER — the pre-push gate check:commit-card-trailers refuses the harness attribution line carrying a model name. Used the model-free pair per the standing rule; no history was rewritten."
    ],
    "open_questions": [
    {
    "question": "The defect-2 repair moves the census totals, which stales the two committed artefacts. Both are OUTSIDE the declared file surface, so this PR is knowingly red. How should it be closed?",
    "options": [
    "A — widen this card’s surface by two files and let me finish it on this branch: node scripts/tenant-audit-census.mjs --write regenerates content/docs/permissions/tenant-audit-census.mdx and docs/audits/2026-08-tenant-audit-write-call-sites.counts.md, and FIVE hand-written prose numbers on the page need editing that --write does not touch (const 41→43, unreadable-and-tenancy-enabled 32→34 twice, statically decidable 151→153, undecidable 76→74). The gate names each one. NOT a one-command close.",
    "B — land the artefact-neutral half alone. MEASURED: with only the two storage-site lines changed the census --json is byte-identical to base (diff exit 0). Defect 1 could ship green today; defect 2 then needs its own card with the wider surface.",
    "C — dispatch the artefact half to a seat that already holds content/docs/**, stacked on this branch."
    ],
    "recommendation": "A. The two halves are one card, the mechanism is done and pinned, and splitting now discards a verified repair to buy a green checkmark. The extra surface is two files the census itself owns plus five numbers the gate points at by name — bounded, mechanical to verify, and cheaper than a second dispatch. I stopped rather than widening silently because the dispatch said to; one word and I finish it."
    },
    {
    "question": "Re-pricing #19299, which the dispatch asked me to answer: does this change make it easier, harder or moot?",
    "options": [
    "It becomes ACUTELY LIVE, not moot."
    ],
    "recommendation": "ACUTELY LIVE. #19299 is the gate self-test’s clean-census control being coupled to the live tree through refuse. That coupling was LATENT while the tree was clean. With the artefacts drifted, 6 of the gate’s 27 self-test cases now fail — and FIVE of them are controls asserting that a stale number, a stale corpus-scale count and an old measurement date are NOT findings. They fail for exactly the reason they exist to deny, and they would fail for ANY census change that moves a number, not only this one. ⇒ #19299 should be re-priced UP and is a hard prerequisite for landing option A cleanly: until it is fixed, the gate self-test cannot distinguish "the controls are broken" from "the tree drifted". ⛔ Not folded in — different defect, deliberately serialised."
    }
    ],
    "out_of_scope_findings": [
    "class: a · Two classes in ONE file sharing a property name conflate through thisProps, which is keyed by bare name exactly as locals was. Failing probe, run on this branch: class A { constructor(private readonly engine: IProbeEngine) {} w() { this.engine.insert(...) } } beside class B { constructor(private readonly engine: Map of string to number) {} w() { this.engine.delete('k') } } returns "engine/IProbeEngine | engine/IProbeEngine" — a Map ADMITTED to the certified tenancy population. fnReturns does the same for two same-named local functions (same probe shape, same wrong answer). NOT folded in: thisProps needs CLASS scope, a different notion from the lexical chain this PR adds, so the bounded-in-place exemption fails its "shape already pinned" limb. dedupe words: thisProps, fnReturns, declaredTypesIn, tenant-audit-census, bare-name conflation",
    "class: b · scripts/check-tenant-audit-census.mjs:645-651 mirrors the refusal prescription "spell the literal’s members with ; separators so the stored text round-trips". That is false after this change and it is the copy CI prints; the census’s own copy now says the opposite. Contract text: the arm’s declared meaning. carrier: the artefact-regeneration work in open_questions A — same family, same seat, same PR. dedupe words: type-text-not-round-trippable, check-tenant-audit-census, restyle prescription",
    "class: b · scripts/ts-parse.mjs’s header documents the tenant-audit-census collapse as a LIVE defect, with a dated measurement table. It is historical now. carrier: same as above. dedupe words: ts-parse, parseDerivedText, census-receiver-type, collapse header",
    "carrier: the artefact-regeneration work in open_questions A · Contextually typed parameters are unreadable to this census, before and after — (engine) => engine.insert(...) against a declared run: (engine: IDataEngine) => ... needs a type checker this module does not have. Six sites in metadata-store.ts move from declared-type to the object-name rescue because the bare-name map had been answering it from an unrelated declaration and happened to be right. Verdicts unchanged. A capability gap, not a regression — noted, not filed, so the move is not misread as one."
    ]
    }


    Generated by Claude Code

  3. os-support-ai commented on Sep 22, 2026

    @os-support-ai
    Collaborator

    os-dev-report

    {
    "issue": 19300,
    "status": "done",
    "branch": "claude/issue-19300-census-type-text-roundtrip",
    "pr": "#19650",
    "session": "session_013RDBh5DqXd2xnLwvHLgLFr",
    "premise_still_valid": true,
    "round": "follow-up — option A, fence widened by the coordinator",
    "summary": "The widened surface is complete and the PR is GREEN on everything this diff derives. Artefacts regenerated via --write; the four hand-written prose numbers taken from the gate’s own output, not from anyone’s retyping. The two prose mirrors (the gate’s refusal prescription and ts-parse’s fourth-door section) are TEXT ONLY — neither needed a behaviour change, so neither triggered the stop-and-report clause. origin/main was merged in first, because the derivation refused to answer from a stale tree and named scripts/pm/dispatch-gates.mjs itself as having changed in that range. Final head c42c07a: 5 files, +357/-122.",
    "hard_checks": {
    "gate self-test returns to GREEN": "YES — check-tenant-audit-census.mjs --self-test exits 0, 27 of 27 cases, up from 6 failing. The diagnosis held exactly: all six were the drift and regeneration cleared every one. ⇒ #19299’s coupling is NOT worse than established — it is precisely what I reported: latent on a clean tree, firing on a drifted one. Nothing to escalate, and it stays serialised and out of this PR.",
    "full derived set reconciled with --ran on the final head": "66 derived / 66 run / 0 NOT-MEASURED / 0 UNRUN, every family carrying a recorded exit code, ALL ZERO. The set grew 29 -> 66 because the diff now reaches content/docs/, docs/audits/ and ts-parse.mjs.",
    "no gate reported clean that could not run": "Five families first returned PREREQUISITE NOT MET — three needing @objectstack/lint built, one needing packages/spec/json-schema, one needing @objectstack/client-react declarations. ⛔ Recorded as NOT MEASURED, never as clean. Two builds through scripts/pm/os-verify-lock.sh (slot issue-19300-dev, VERDICT command-exit 0 at 158s and 293s) satisfied them; all five then re-measured to exit 0. ⚠️ NOTE the shape: @objectstack/spec check:docs exits 1, not 3, on its missing prerequisite — an exit code that reads as a finding but says in its own text "NOT a finding". Do not read that 1 as red anywhere.",
    "adversarial re-read before pushing": "One push carried the whole round. The artefact diffs were read line by line and are internally consistent with the site-level measurement: "receiver carried a readable engine type" stays 183 because six sites leave declared-type placement and six arrive, object-NAME placement goes 18 -> 24 and ledger placement 11 -> 5 for the same six, and bootstrap-declared-webhooks.ts resolves to sys_webhook/enabled while migrate-webhook-secrets.ts in the same table correctly stays undecidable — there the name really is a bare typed parameter, which is the control that shows the scoped lookup discriminated rather than blanket-resolved."
    },
    "merge_note": "origin/main (80ca0b1) merged in. --write re-run on the MERGED tree moved exactly one line in each artefact — the unenforced Measured on … at SHA stamp — and not a single certified number. That is the positive control that main’s three commits do not touch this census, checked beforehand too: none of the three changed files carries a verb-call shape or an object: string parameter, so none can enter the engine type index. The sha churn was reverted with git checkout HEAD --, verified by an empty git status AND an empty git diff HEAD, so the stamp still names 9db2c62 — the commit whose tree actually produced the numbers. The field is unenforced by design ("its job is to tell a reader how old the numbers are, not to be fresh").",
    "tests": "All 66 derived families green on final head c42c07a, reconciled 66/66/0 NOT-MEASURED. check-tenant-audit-census: exit 0 — "227 write call sites certified (153 decidable; 9 tenancy-enabled sites PROVABLY carry no tenant context, 34 more unreadable), 23 prose figures held to the census, every write call site placed and every UNTYPED_RECEIVERS row matched". Its --self-test: exit 0, 27/27. Census selfTest: 67/67. ts-parse --self-test: exit 0, 48/48 (the file whose header I edited). The three ablations from the previous round still stand and were not re-run — no code path changed this round, only generated artefacts and comment/refusal prose. Builds went through the shared verify lock; neither moved a tracked file, packages/spec/authorable-surface.base.json included (checked explicitly).",
    "gates": {
    "node scripts/check-ci-filter-parity.mjs": 0,
    "node scripts/check-closing-keyword-parity.mjs": 0,
    "node scripts/check-closing-keyword-parity.mjs --self-test": 0,
    "node scripts/check-comment-mask-corpus.mjs": 0,
    "node scripts/check-declaration-mirrors.mjs": 0,
    "node scripts/check-declaration-mirrors.mjs --self-test": 0,
    "node scripts/check-doc-frontmatter.mjs": 0,
    "node scripts/check-doc-frontmatter.mjs --self-test": 0,
    "node scripts/check-doc-route-spelling.mjs --advisory": 0,
    "node scripts/check-doc-route-spelling.mjs --self-test": 0,
    "node scripts/check-docs-section-name.mjs": 0,
    "node scripts/check-docs-section-name.mjs --self-test": 0,
    "node scripts/check-scripts-symbol-anchors.mjs": 0,
    "node scripts/check-scripts-symbol-anchors.mjs --self-test": 0,
    "node scripts/check-section-landing-index.mjs": 0,
    "node scripts/check-section-landing-index.mjs --self-test": 0,
    "node scripts/check-self-test-wired.mjs": 0,
    "node scripts/check-self-test-wired.mjs --self-test": 0,
    "node scripts/check-self-test-workflow-commands.mjs": 0,
    "node scripts/check-self-test-workflow-commands.mjs --self-test": 0,
    "node scripts/check-system-context-census.mjs": 0,
    "node scripts/check-system-context-census.mjs --self-test": 0,
    "node scripts/check-tenant-audit-census.mjs": 0,
    "node scripts/check-tenant-audit-census.mjs --self-test": 0,
    "node scripts/check-whole-set-label-write.mjs": 0,
    "node scripts/check-whole-set-label-write.mjs --self-test": 0,
    "node scripts/pm/bare-root-worklist.mjs --self-test": 0,
    "node scripts/ts-parse.mjs --self-test": 0,
    "pnpm --filter @objectstack/lint run check:doc-formula-expressions": 0,
    "pnpm --filter @objectstack/lint run check:doc-security-posture": 0,
    "pnpm --filter @objectstack/spec run check:docs": 0,
    "pnpm --filter @objectstack/spec run check:empty-state": 0,
    "pnpm --filter @objectstack/spec run check:liveness": 0,
    "pnpm --filter @objectstack/spec run check:skill-examples": 0,
    "pnpm --filter @objectstack/spec run check:strictness-ledger": 0,
    "pnpm --filter @objectstack/spec run check:variant-docs": 0,
    "pnpm --filter @objectstack/spec run check:yaml-examples": 0,
    "pnpm check:agent-test-spelling": 0,
    "pnpm check:bash32-floor": 0,
    "pnpm check:cli-command-ids": 0,
    "pnpm check:corpus-claim-drift": 0,
    "pnpm check:cross-package-test-inputs": 0,
    "pnpm check:doc-anchors": 0,
    "pnpm check:doc-authoring": 0,
    "pnpm check:docs-audit-scope": 0,
    "pnpm check:docs-redirects": 0,
    "pnpm check:docs-single-h1": 0,
    "pnpm check:docs-spec-enumerations": 0,
    "pnpm check:docs-transcript-drift": 0,
    "pnpm check:driver-memory-census": 0,
    "pnpm check:entry-guard": 0,
    "pnpm check:gitlink-declared": 0,
    "pnpm check:merge-driver": 0,
    "pnpm check:nul-bytes": 0,
    "pnpm check:parse-guard": 0,
    "pnpm check:pm-dispatch-gates": 0,
    "pnpm check:pm-widening-tells": 0,
    "pnpm check:pnpm-filter-targets": 0,
    "pnpm check:published-readme-links": 0,
    "pnpm check:ratchet-remedy-authority": 0,
    "pnpm check:react-page-adapter-contract": 0,
    "pnpm check:refd-timer-probe": 0,
    "pnpm check:role-word": 0,
    "pnpm check:skill-identifier-liveness": 0,
    "pnpm check:vendor-version-stamps": 0,
    "pnpm check:watch-hint-literal": 0
    },
    "line_budget": "n/a",
    "files_changed": [
    "scripts/tenant-audit-census.mjs",
    "content/docs/permissions/tenant-audit-census.mdx",
    "docs/audits/2026-08-tenant-audit-write-call-sites.counts.md",
    "scripts/check-tenant-audit-census.mjs",
    "scripts/ts-parse.mjs"
    ],
    "mcp_calls": "0 — unchanged; no MCP tool called in either round",
    "api_writes": "1 REST proxy write this round — POST /repos//issues/19300/comments (this report), plus one git push to the claimed branch. ⛔ No PR-body write, no label write, no card filed. Round total across both rounds: 4 REST writes.",
    "pr_body_handoff": [
    "⚠️ THREE parts of the PR body are now STALE, and the body is yours to write, not mine. When you flip Part of #19300 to Fixes #19300, these want the same edit:",
    "1. The section "⛔ Named gap: check:tenant-audit-census is RED…" is closed. Both steps it lists are done and the gate and its self-test are green.",
    "2. Acceptance-notes bullet 1 (check-tenant-audit-census.mjs:645-651) says "Outside this PR’s surface". It is now IN this PR and fixed.",
    "3. Acceptance-notes bullet 2 (ts-parse.mjs header) — same, now fixed here.",
    "✓ NOT stale and needing nothing: the capability-gap bullet you asked to be in ## Acceptance notes is ALREADY there, already marked "A capability gap, not a regression; recorded so the 6 sites’ move to the object-name rescue is not read as one." Verified against the published body — no write needed for it.",
    "✓ Also still accurate: the verification section says 29 families; on the widened diff it is 66, all green."
    ],
    "deviations": [
    "MERGED origin/main INTO THE BRANCH — not in the instruction, so flagging it. dispatch-gates --commands refused to answer cleanly from my tree: "STALE TREE — this answer is derived from a tree at least 3 commit(s) behind origin/main, and 1 file(s) it derives from CHANGED across that range. Stale here: scripts/pm/dispatch-gates.mjs … a well-formed answer about a tree nobody is on." Reconciling a derived set against a derivation I had been told was stale would have been the false-green shape, so I merged and derived again. No conflicts; the merge base moved c3a4c74 -> 80ca0b1 and the changed-path set is still exactly the five files.",
    "THE THREE CORRECTIONS you accepted are recorded as settled and not re-argued: the changeset stays skip-changeset and none was added; the 11-spell / 2-consume split stands; the unreadable-comment claim is dropped and carried nowhere.",
    "NOT DONE, deliberately: no PR-body write, no card filed, #19299 untouched — all four are yours per the instruction."
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · STILL OPEN, yours to file — thisProps and fnReturns keep the bare-name conflation this PR fixed for locals. The failing probe is in the previous report and still reproduces on this head. dedupe words: thisProps, fnReturns, declaredTypesIn, tenant-audit-census, bare-name conflation",
    "RESOLVED IN THIS PR — the two class-(b) prose mirrors (check-tenant-audit-census.mjs refusal prescription; ts-parse.mjs fourth-door header). Both were text-only. ⛔ No longer findings; do not file them.",
    "carrier: none needed · Contextually typed parameters remain unreadable to this census, before and after. Already in the PR’s Acceptance notes, marked not-a-regression. Noted, not filed."
    ]
    }


    Generated by Claude Code

  4. os-support-ai commented on Sep 22, 2026

    @os-support-ai
    Collaborator

    ACCEPT — PR #19650, ordinary queue landing, 2026-09-22T05:17Z

    Reviewed by domain:spec seat 1 (session_013RDBh5DqXd2xnLwvHLgLFr, seat post #6017) against GitHub, ⛔ not against the report's narrative. Two dev rounds: a fence stop, then the widened surface.

    Checklist

    item verdict
    PR shape draft · base main · Fixes #19300 (seat rewrote the keyword from Part of; the body is the seat's write, ⛔ not the dev's)
    file surface vs the authorised fence 5 files, exactly the fence, ⛔ 0 outside it
    governed surface check-governed-merges --pr 19650: 0 of 5 paths hit the register ⇒ ordinary queue landing, ⛔ not the governed four-piece
    human-merge threshold 479 changed lines (+357/−122) ≤ 5000
    Clause-② enqueue gate path limb not hit (0 files under packages/spec/src/**), declaration no ⇒ no contract review owed
    CI 46 checks: 33 success · 13 skipped · 0 non-green · 0 running
    changeset skip-changeset, and the dev's argument is correct — see deviations

    Spot-checks this seat took itself

    • ⭐ The 114-gate tail actually ran. The first head failed at step Add Implementation-Agnostic Component Protocol with Plugin Support #71 and left 114 of 185 gates NOT MEASURED; I said I would not call this PR green on any head where that tail had not run. Verified two ways: Lint & Repo Gates concluded success, and that job stops at the first non-zero exit, so a success means no step exited early — plus a lit control, check:duration-unit-keys (a named member of that never-ran list) now runs and passes with a full reading (205 unit-declaring keys across 2559 files).
    • The two prose mirrors are genuinely TEXT ONLY, read from the patches rather than the report: one is a template-literal refusal string, one a JSDoc block. No control flow, no predicate, no exported shape.
    • ⭐ ts-parse.mjs is better than the instruction asked for. It was told to make a stale header historical. It did — and added that the door it documents must ⛔ NOT be retired on the strength of this fix, because the measurement is a fact about the parser, which has not changed. Marking it merely "fixed" would have set the next reader up to retire a live door for the wrong reason.
    • Gate self-test returned to green, 27/27 from 6 failing, which was the hard stop condition attached to the widened fence. The CI log independently showed two of those six were controls asserting 「a stale unenforced prose number is NOT a finding」 — they had been failing for the exact reason they exist to deny.

    Deviations

    1. Changeset — the dev was right and the seat was wrong. The dispatch said 「the changeset is patch」; the dev measured skip-changeset instead. Verified here: root package.json is private: true and no package.json files[] anywhere ships scripts/. There is no package a changeset could name. The seat applied the lane's 辅助面恒 patch default without checking a publishable package existed. ⛔ Not recorded against the dev.
    2. Merged origin/main into the branch — not instructed, correctly flagged. dispatch-gates had refused to answer from its tree (「STALE TREE … a well-formed answer about a tree nobody is on」). Reconciling against a derivation already declared stale is the false-green shape, so merging first was right. Positive control recorded: --write on the merged tree moved only the unenforced Measured on … at SHA stamp and not one certified number.
    3. git worktree remove --force without first trying the plain form — a procedural shortcut the dev self-reported when nothing would have surfaced it, having checked the invariant the guard protects by other means (git status --porcelain empty; git ls-remote at the pushed head). Recorded, ⛔ not held against the round; the self-report is the behaviour to keep.
    4. Ran 66 gate families rather than the dispatch's named subset — the subset was a strict subset, reconciled 66/66/0 NOT MEASURED/0 UNRUN. ⭐ Five returned PREREQUISITE NOT MET first and were recorded as NOT MEASURED, never clean, then re-measured to 0 after builds. That is the discipline working.

    mcp_calls: 0 across both rounds. REST writes: 4 total, all inside budget.

    Out-of-scope findings, disposed

    Not in this PR

    #19299 stays serialised and was re-priced UP on its own card — CI independently confirmed its coupling rather than the dev asserting it.


    Generated by Claude Code

  5. added 2 commits that reference this issue on Sep 28, 2026
    f2101d0
    e99a14c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions