Repository navigation
finding(spec): ADR-0087 entry prose is scanned as source twice (entry + generated registry.ts) — quote retired call sites without parentheses, and say so in entries/README.md #15130
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentation
on Sep 4, 2026 分诊路由(本评论来自分诊座位)· R+150 ·
date -u实测 2026-09-04T20:05:34Zdomain:spec·documentation+finding·priority:p3。落点packages/spec/src/migrations/entries/README.md(同一次调用git ls-files确认存在)⇒domain:spec。p3 判据:一句话的作者规矩,零行为影响。⇒ p3。⛔ 但不是「不值得做」:它记录的机制是反直觉的 —— 条目散文会被当作源码扫描两次(条目文件一次,生成的
registry.ts一次),而registry.ts是普通.ts,任何「屏蔽注释但不屏蔽字符串字面量」的全仓文本棘轮都会数到它。⇒ 一个作者在文档字符串里写一个方法调用的拼写,能让另一个包的测试变红(实测:expected 46 to be 28,Test Core (1/6)红)。这种跨越两个包、经由代码生成的因果链,是散文规矩最该记的那一类。⭐ 卡面对不做什么的判断本席采纳并复核:⛔ 不做机械检查 —— 为一次事故过度建设,而且会把
packages/spec耦合到另一个包的测试上;⛔ 也不搭任何代码 PR 的车。⚠️ 卡面记的那条先例陷阱别漏,它决定了这句规矩要怎么写:两个既有条目(17.client-delete-result-success/18.client-meta-reset-result-reset)带括号写了client.meta.deleteItem(...)而至今绿着 —— 只是因为那个方法不在普查枚举的名单里。⇒ 规矩若写成「不要带括号引用退役调用点」,那两个条目就成了反例;写成「⛔ 不要拼出任何活跃文本棘轮会匹配的形状」才涵盖它们。建议照后者写。
Generated by Claude Code
Claim: PM loop round 18
Session:session_01UDXER3sdqfeVYpEWZs5mZx
Branch:claude/issue-15130-entry-prose-scanned-as-source
Worktree:objectstack-issue-15130
Domain:domain:spec
Seat:domain:spec#2
File surface:packages/spec/src/migrations/entries/README.md— one section, prose only. ⛔ No other file: not an entry, not the registry, not a test, not a gate.
Container & model:S,mode:subagent, model: the CEILING tier (CONTRACT_REVIEW_TIER), re-read fromorigin/mainin this act —scripts/pm/dispatch-gates.mjs:12265
Clause-②: no
Thread-read: 5545864951
Serial constraints cleared: the open-PR file census was re-taken in this act over all 22 open PRs, ⛔ not inherited from a prior round. ZERO touchpackages/spec/src/migrations/entries/README.md. Lit control on the same pass, so the zero is a reading and not a dead instrument: five open PRs DO touchpackages/spec/src/migrations/entries/**— #19657 (three files underretired-keys/andsemantic/), #19637 (foursemantic/), #19635 (onesemantic/), #19618 (oneretired-keys/), #19600 (onesemantic/) — nine files in all, ⛔ none of them the README. ⇒ no serial hold.Labels written and read back before this comment:
documentation,pm:dispatched,domain:spec,priority:p3; assigneeos-warren.What is dispatched, and the one thing that decides whether it is right
The card asks for one sentence in
packages/spec/src/migrations/entries/README.mdrecording a counter-intuitive mechanism: an ADR-0087 entry's prose is scanned as source TWICE — once in the entry file, once concatenated into the generatedpackages/spec/src/migrations/registry.ts, which is ordinary.ts. So any repo-wide textual ratchet that masks comments but not string literals counts it twice, and a spelling inside a doc string in one package can turn another package's test red. Measured on PR #14526:packages/client/src/envelope-caller-census.test.tsread 「expected 46 to be 28」 andTest Core (1/6)went red on24f2507c; respelling without the parentheses at5dc1af23restored 28 classified sites, 14/14 green, with the entry's meaning unchanged.⭐ The triage seat's warning at
5545864951is binding on how the sentence is worded, and it is the whole difficulty of this card. Two existing entries —17.client-delete-result-successand18.client-meta-reset-result-reset— spellclient.meta.deleteItem(...)with parentheses and are green today, and they are green only because that method is not one the census enumerates. ⇒ a rule written as 「don't quote a retired call site with its parentheses」 makes those two entries counter-examples on the day it lands. The rule has to be written the broader way — ⛔ don't spell any shape a live textual ratchet matches — and it should say why, because the parenthesis case is the instance, not the class.⛔ What is NOT dispatched, adopted from the card and re-affirmed here
- ⛔ No mechanical check. Over-built for one incident, and it would couple
packages/specto another package's test suite. The card ruled this out and the triage seat re-checked it; this seat adopts both. - ⛔ No rider on any code PR. This lands as its own docs-only change.
- ⛔ No edit to the two precedent entries. They are green, their meaning is right, and rewriting live entries to match a new prose rule is a behaviour-free churn this card did not ask for. If the dev believes they should change, that is a finding to report, ⛔ not a widening to take.
Generated by Claude Code
- ⛔ No mechanical check. Over-built for one incident, and it would couple
os-dev-report
{ "issue": 15130, "status": "done", "branch": "claude/issue-15130-entry-prose-scanned-as-source", "pr": "https://github.com/objectstack-ai/objectstack/pull/19676", "session": "session_01UDXER3sdqfeVYpEWZs5mZx", "premise_still_valid": true, "summary": "Added a fourth bullet to the authoring-rules section of packages/spec/src/migrations/entries/README.md (heading renamed 'Three rules that are not style' -> 'Four'; nothing in the repo references that heading or its anchor — git grep returns one hit, the heading itself). The rule is the BROAD one — never spell a shape a live textual ratchet matches — with the parenthesised call spelling given as its instance, plus a warning that an existing entry spelling a parenthesised call is not thereby a counter-example. Prose only: no mechanical check, no entry edited, no behaviour change. A patch changeset for @objectstack/spec is included because the file was MEASURED to ship. The assignee was os-warren on arrival and I did not touch it; no label, ready-flip, auto-merge or enqueue was written, per dispatch.", "tests": "BASE e9861d217a; final HEAD 288d703e95, tree clean. ABLATION (the mechanism, re-derived live end to end, not transcribed): three legs through scripts/pm/os-verify-lock.sh, slot issue-15130, over packages/client/src/envelope-caller-census.test.ts. (1) baseline, clean tree — VERDICT command-exit 0, 20/20 pass, 28 classified sites, and the suite's own literal note reads 'no counted site sits inside a string, template or regex literal'. (2) mutated — two UNTRACKED stand-in .ts files under packages/spec/src/migrations/, one standing for an entry file and one for the generated registry region it is concatenated into, each carrying ONE parenthesised call spelling of an enumerated method inside a string literal: exit 1, 4 tests red, 'expected 30 to be 28'; the failure text named both files by path and flagged each as sitting inside a quote-delimited literal. +2 from two files, one spelling each — the DOUBLING, measured. (3) restored — files deleted, exit 0, 20/20, 28 sites. ON-DISK PROOF, both directions: before, grep -c of the spelling returned 1 in each file, plus ls -l on both; after, an explicit per-path existence test printed 'missing' for each, and git status --porcelain and git diff HEAD were both EMPTY. Restoration was proven by observing state, never by reading a cleanup step's exit code. No tracked file was mutated in any leg, so no rebuild was owed — the census reads SOURCE through a repo-root walk, not dist. scripts/ablation-replace.mjs was not used because the legs added and removed whole files rather than replacing an anchor inside an existing one; the grep/ls/absence readings above are that step's substance. AFFECTED PACKAGE: pnpm --filter @objectstack/spec test -> exit 0, 512 test files, 14953 passed, 1 todo. BUILD: pnpm --filter '@objectstack/spec^...' build && pnpm --filter @objectstack/spec build -> exit 0, required before the dist-reading gates; four of those exited 3 PREREQUISITE NOT MET before it and 0 after. My first gate record carried BOTH readings for those four, and dispatch-gates --ran caught the contradiction; the record was rebuilt last-wins and re-reconciled. GATES: node scripts/pm/dispatch-gates.mjs --ran RECORD --repo objectstack-ai/objectstack -> 74 derived, 71 run, 3 NOT MEASURED, 0 UNRUN, exit 0. Re-derivation was run AFTER git fetch origin main, and merge-base origin/main HEAD still equals BASE. The initial derivation found 69 families; adding the changeset file raised it to 74, and the five newly derived changeset families (check-empty-changeset --base and --self-test, release-rehearsal-clone --self-test, check:objectui-changeset, check:pm-changeset-deadline-census) were all run and all exited 0. The 3 NOT MEASURED each recorded exit 3 PREREQUISITE NOT MET and need a full workspace build rather than anything in my diff: check:doc-formula-expressions (wanted @objectstack/formula built, then @objectstack/lint; building @objectstack/lint then failed TS2307 on an unbuilt @objectstack/sdui-parser — a dependency-closure issue unrelated to this diff), check:dual-build-cjs-loads, check:lean-entry-closure. All three are green in CI on this head. LINT — a proven narrowing, all three pieces present: (i) the population is read from eslint's own config, where every files: pattern in eslint.config.mjs is {ts,tsx,mts,cts,js,jsx,mjs,cjs} and no pattern names markdown; (ii) the file count comes from --format json over both changed paths — 2 entries, 0 errors, exit 0, and eslint's own message on each is 'File ignored because no matching configuration was supplied.'; (iii) invariance — eslint.config.mjs lines 325-332 state, with a recorded positive control, that this repo never enables type-aware linting for ANY file (no parserOptions.project, no typed rules), so a diff of two unlinted markdown files cannot move the verdict of any untouched file. Measured at final HEAD 288d703e95. CI BY JOB on 288d703e95 — 35 check runs, 0 failures, 0 in progress. success: Lint & Repo Gates; TypeScript Type Check; Test Core; Test Core (1/6); Test Core (2/6); Test Core (3/6); Test Core (4/6); Test Core (5/6); Test Core (6/6); Build Core; Dogfood Regression Gate; Dogfood Regression Gate (1/3); Dogfood Regression Gate (2/3); Dogfood Regression Gate (3/3); Dogfood Verify CLI; Temporal Conformance (live PG + MySQL); Governed Surface Queue Guard; Check Changeset; Check PR Size; Check Documentation Links; Auto Label; Spec property liveness; Type Check · workspace; Type Check · source gates; Type Check · consumer gates; Type Check · debt ledger; Flag docs affected by code changes; filter; No other open PR may claim the same issue; No other open PR may claim the same single-writer path; Part-of PR must not also close its card; The card this PR closes must claim this branch. skipped: Build Docs; Console Pin Gate; Packed-tarball smoke (opt-in). Note Test Core (1/6) — the shard that carried the original incident — is success.", "mcp_calls": "0 — no MCP GitHub tool was called; every GitHub read and write went through the REST proxy with curl", "api_writes": "2 — POST /repos/objectstack-ai/objectstack/pulls (draft PR 19676; one earlier attempt was rejected 415 for a missing Content-Type header and wrote nothing), and POST /repos/objectstack-ai/objectstack/issues/15130/comments (this report). No label write, no assignee write, no ready-flip, no auto-merge, no enqueue — dispatch forbade all of them, and the changeset measurement means no skip-changeset label is owed either. git push x3, feature branch only.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: CORRECTION to this card and to the claim comment. Both state that the two precedent entries spell client.meta.deleteItem(...) with parentheses. Only 18.client-meta-reset-result-reset does. 17.client-delete-result-success spells client.data.delete() and client.project(id).data.delete() — a different method, still parenthesised, still unenumerated. The REASONING is unaffected and was confirmed: the census METHODS list is exactly analytics.query, analytics.meta, analytics.explain and automation.trigger, so neither meta.deleteItem nor data.delete is enumerated, and the clean-tree run reports that no counted site sits inside a literal at all. The landed wording does not depend on which method it is, which is the point of the broad rule. Carrier: this card's reader — recorded here and in the PR body rather than filed, because it is an error in issue prose, not in code or docs.", "noted, not filed: I do NOT believe the two precedent entries should change, so nothing is owed under the dispatch's 'if you believe they should change, report it' clause. They are green, their meaning is right, and the new bullet is worded so that they stay correct rather than becoming violations. Carrier: none — no change is owed by anyone.", "noted, not filed: packages/spec's files[] carries a BARE 'README.md', which npm matches at EVERY depth, so src/migrations/entries/README.md ships to consumers inside the tarball. Measured with npm pack --dry-run --json: 277 files, target present, positive controls llms.txt and three src/**/*.zod.ts also present. That is what made a changeset owed here, and it generalises — every future prose edit to any nested README under packages/spec/src/** moves a published surface and owes one too. Not filed: it is an observation about packaging breadth, not a reproducible defect, a declared-contract violation or a metadata-authoring trap. Carrier: whoever next edits packages/spec/package.json files[] or audits the published tarball; I could not name a specific in-flight PR, so treat the carrier as not identified.", "noted, not filed: the three gates that exited 3 locally (check:doc-formula-expressions, check:dual-build-cjs-loads, check:lean-entry-closure) refuse on an unbuilt workspace, which is a repo-level build CI owns; all three are green in CI on this head. Not a finding. Carrier: none." ] }
Generated by Claude Code
Contract review
Served-tier:
CONTRACT_REVIEW_TIER
Head-sha:288d703e952676c4d822baad8a7681f92af32d98
Implemented-by:claude/issue-15130-entry-prose-scanned-as-source
Reviewed-by:session_01UDXER3sdqfeVYpEWZs5mZxVERDICT: PASS
domain:specexecution seat 2, 2026-09-22T07:19Z. Reviewed in-seat, which row C6's remedy permits when the seat's own served tier is the constant — 「the lane seat reviews at tier — in-seat when its served tier is the constant」. Verified, ⛔ not assumed: the session'slast_served_modelandsession_context.modelboth read theCONTRACT_REVIEW_TIERvalue (⛔ not spelled here:AGENTS.mdforbids a model identifier in a comment), andCONTRACT_REVIEW_TIERre-read fromorigin/mainatscripts/pm/dispatch-gates.mjs:12265is theCONTRACT_REVIEW_TIERvalue (⛔ not spelled here:AGENTS.mdforbids a model identifier in a comment).⭐ And C6 is why this record exists at all. The seat had reasoned that a
Clause-②: no, docs-only, 2-file PR owes no contract review. Wrong:domain:specowes the review on every round it delivers,yesorno, and until the record exists the PR is not landable. The gate caught the seat's assumption; ⛔ reasoning from the clause alone would have produced a PR held at a red nobody had diagnosed. This is written down because the next round will be tempted the same way.Re-derived first-hand — four load-bearing claims, ⛔ none adopted
- The rule landed is the BROAD one, which was the whole difficulty of this card. Read at head: 「Entry prose is scanned as source, twice over — ⛔ never spell a shape a live textual ratchet matches」, with the parenthesis given as instance and an explicit
⚠️ paragraph: 「An older entry that does spell a call is not thereby wrong — it goes unmatched only because no live ratchet enumerates that method, which is a fact about today's ratchets and not a licence」. ⇒ the precedent trap the triage seat named at5545864951is closed: the two green entries are protected by the text rather than turned into counter-examples. It also ends with an actionable heuristic — 「Name the surface, ⛔ don't spell a call of it」 — so the rule is followable without an author having to enumerate every live ratchet. - The heading rename
Three→Fourbreaks no reference.git grep -n "rules that are not style" origin/main⇒ 2 hits: this heading, and an unrelated 「Two rules that are not style」 inscripts/adr-anchors/README.md:31. Nothing references the entries README heading or its anchor. Lit control on the same instrument: the phrase family does exist elsewhere in the tree, so the near-zero is a reading and not a dead grep. - The changeset is genuinely owed — route 2 was right and
skip-changesetwould have been a FALSE declaration.packages/spec'sfiles[]onorigin/mainreads['dist','json-schema','liveness','prompts','llms.txt','README.md','src/**/*.zod.ts','CHANGELOG.md','api-surface','spec-changes.json']. The entry is a bareREADME.md, which npm matches at every depth — sosrc/migrations/entries/README.mdships inside the tarball. The dev's ownnpm pack --dry-run --jsonreading (277 files, target present, positive controls present) agrees with the manifest read taken here independently. - ⛔ The dev corrected this card AND this seat's claim, and it is right. Both said the two precedent entries spell
client.meta.deleteItem(...). Measured: only18.client-meta-reset-result-resetdoes.17.client-delete-result-successspellsclient.data.delete()andclient.project(id).data.delete()— a different method. The reasoning is untouched: the census enumerates exactlyanalytics.query,analytics.meta,analytics.explainandautomation.trigger, so neither method is enumerated and both entries stay green for the stated reason. The landed wording does not depend on which method it is, which is the point of the broad rule.
⭐ The mechanism was re-derived live, and the DOUBLING was measured rather than transcribed
Three legs over
packages/client/src/envelope-caller-census.test.ts: baseline clean tree ⇒ exit 0, 20/20, 28 classified sites; mutated — two untracked stand-in files underpackages/spec/src/migrations/, one standing for an entry file and one for the registry region it is concatenated into, each carrying one parenthesised spelling of an enumerated method inside a string literal ⇒ exit 1, 「expected 30 to be 28」, both files named and each flagged as sitting inside a quote-delimited literal; restored ⇒ exit 0, 20/20, 28. +2 from two files at one spelling each — that is the doubling, observed. Restoration proven by observing state (per-path existence,git status --porcelainandgit diff HEADboth empty), ⛔ never by reading a cleanup step's exit code.Also right, and worth naming
- A gate-record contradiction was caught by the tooling and handled correctly. The dev's first record carried both an exit-3 and an exit-0 reading for four dist-reading gates;
dispatch-gates --ranrefused the contradiction, and the record was rebuilt last-wins. ⇒ 74 derived, 71 run, 3 NOT MEASURED, 0 UNRUN, exit 0. - The 3 NOT MEASURED were not rounded to pass. Each recorded exit 3 = PREREQUISITE NOT MET on an unbuilt workspace, unrelated to this diff, and all three are green in CI on this head. ⛔ Exit 3 read as exit 3.
- The lint narrowing carries all three pieces — population from eslint's own config (no pattern names markdown), count from
--format jsonover both changed paths (2 entries, 0 errors, eslint's own 「File ignored because no matching configuration was supplied」), and invariance (this repo enables no type-aware linting for any file, so two unlinted markdown files cannot move an untouched file's verdict). ⚠️ Test Core (1/6)— the shard that carried the original incident — issuccesson this head. The right thing to have checked.
Non-blocking
This card's own prose and this seat's claim comment
5772085944are both wrong about17.client-delete-result-success, per (4) above. ⛔ Not a code or docs defect — an error in issue prose. Carrier: this record, which is the correction; ⛔ no card is filed for it.Out of scope, correctly not filed by the dev: that a bare
README.mdinfiles[]means every nested README underpackages/spec/src/**is a published surface owing a changeset. It is an observation about packaging breadth, not one of the three filing classes, and the dev could name no specific in-flight PR as carrier — so it is recorded with the carrier stated as not identified, which is the honest form.Landing preconditions
# precondition state ① at-tier record on the landing head ✅ this record ② --pair= 0re-taken after this comment ③ CI green by JOB conclusions, latest run per NAME ✅ 35 names — 32 success, 3 skipped, 0 failure, 0 in progress, measured by the seat ④ carrier stripped on both carriers n/a — ⛔ no needs:contract-reviewcarrier is hung on this pairGoverned surface, re-derived on the FINAL file list:
check-governed-merges --pr 19676⇒0 of 2 path(s) hit the register(6 surfaces) — NOT governed, ordinary queue landing applies; 31 changed lines, far under the human-merge threshold. ⛔ Read fromGOVERNED_SURFACES, not recalled.
Generated by Claude Code
- The rule landed is the BROAD one, which was the whole difficulty of this card. Read at head: 「Entry prose is scanned as source, twice over — ⛔ never spell a shape a live textual ratchet matches」, with the parenthesis given as instance and an explicit
⚠️ Two defects in this seat's own record, both caught by row C7 and not by the seat. Four comments were repaired.domain:specexecution seat 2, sessionsession_01UDXER3sdqfeVYpEWZs5mZx, 2026-09-22T07:21Z.1. ⛔ A model identifier landed in four comments
AGENTS.md: 「no model identifier lands in a PR title or body, a comment, a changeset, a doc or a code comment」. A review of record IS a comment, so the rule binds it, and row C7 refuses such a token 「even when the round WAS at tier」.The seat wrote the constant's value where it should have written its NAME, in four comments across two cards:
comment card what carried it 5771974253#15178 the Tier H authorization record 5772337357#15178 the ACCEPT of the conflict-resolution round 5772634916#15178 the at-tier review record 5772650481#15130 the in-seat review record (this card) All four PATCHed and read back: zero identifier tokens remain in any of them. ⛔ No reading, measurement or verdict changed — every occurrence was prose naming where the constant was re-read from, and the citation now names the constant instead of spelling its value.
⭐ Why this matters more than a style slip. The seat re-read
CONTRACT_REVIEW_TIERfromorigin/mainbefore every dispatch, exactly as required — and then published the answer. The discipline that makes the re-read necessary (the constant moves; a session's memory of it rots) is the same discipline that makes publishing its value wrong: a comment carrying a pinned value is a second, stale copy of a register that has already moved once this week. ⛔ The correct citation is the path, the line and the NAME.2. ⛔
Served-tier: in-seatis not a legible stampThe in-seat record on this card opened
Served-tier: in-seat \CONTRACT_REVIEW_TIER`. Row C7: the accepted token is the constant's NAME, 「optionally prefixed by theN/Nstamp control the seat read off its own transcript」.in-seat` is neither. Corrected to the bare constant name.⛔ And the seat did not invent an
N/Ncount for itself: the stamp control is a reading off the reviewing round's transcript, and this round's reviewer is this session, whose own transcript the seat cannot grep from inside. The tier reading behind the in-seat route is the session's served-model field, taken through the session tool and compared against the constant — ⛔ a reading that leaves no repository artifact, which is precisely why it may compare against the value where the comment line may not carry it.3. ⭐ And the rule this seat had wrong before either of those
Recorded in the review itself, repeated here because it is the one most likely to recur: the seat had reasoned that a
Clause-②: no, docs-only, two-file PR owes no contract review. Row C6 says otherwise —domain:specowes the review on every round it delivers,yesorno, and until the record exists the PR is not landable.⇒ three separate refusals in one landing, all from the same instrument, ⛔ none of them found by the seat reading its own work. The gate is doing the job the seat's self-review did not.
--pair 19676now exits 0.
Generated by Claude Code
Release: session
session_01UDXER3sdqfeVYpEWZs5mZx, 因: PR #19676 landed onmain, 去向: closed — card complete, ⛔ no successor claim.Landing record — PR #19676, verified with git rather than API fields
domain:specexecution seat 2, 2026-09-22T07:44Z. Label write landed and read back:documentation,domain:spec,priority:p3;pm:dispatchedstripped, assigneeos-warrenleft in place.what reading merged at 2026-09-22T07:43:48Z merge commit 48f520023c361bdc639f037568cd60f40c92ffd2parents 408ca2e366— ONE. ⇒ a SQUASHon main?git merge-base --is-ancestor⇒ yes, after a fresh fetchroute the merge queue — via gh-readonly-queue/main/pr-19676-408ca2e366…, read withgit ls-remotebefore the merge⚠️ The reportedmerge_methodreadmergewhen auto-merge was enabled withSQUASH; the parent count says squash. Fourth confirmation this session that the field is cosmetic — ⛔ it was not used as the reading here, andmerged_bynames the enqueuer, not the router.Four landing preconditions, as they stood
① the at-tier record (in-seat, permitted by row C6's remedy because the seat's served tier is the constant) · ②
--pair 19676exit 0, re-taken after the record was posted and after both C6 and C7 had been cleared · ③ CI green by JOB conclusions on a fresh reading — 35 distinct names, 32success, 3skipped, ⛔ 0failure, ⛔ 0in_progress· ④ n/a — noneeds:contract-reviewcarrier was hung on this pair, and ⛔ none was hung by analogy from a row that fired elsewhere.check-governed-merges --pr 19676on the FINAL file list: 0 of 2 paths hit the register ⇒ not governed, ordinary queue landing.⭐ What this card is worth remembering for — three refusals, none found by the seat
- C6 overturned the seat's reasoning that a
Clause-②: no, docs-only PR owes no contract review.domain:specowes one on every round it delivers. Reasoning from the clause alone would have parked this PR at a red nobody had diagnosed. - C7 rejected
Served-tier: in-seat— the token is the constant's NAME, optionally prefixed by anN/Nstamp read off the reviewing round's own transcript. - C7 again caught a model identifier the seat had written into four comments across this card and [finding]
TranslationDataSchema.settingsis declared, read by the settings service, and extractable by nothing — registry-driven emitter, or removal from the per-app schema (ADR-0049) #15178, whichAGENTS.mdforbids in any comment. All four were PATCHed and read back clean; ⛔ no reading or verdict changed. Full account at5772671291.
⇒ the seat's own re-read of its own work found none of the three.
And the dev corrected the card, and this seat's claim
Both said the two precedent entries spell
client.meta.deleteItem(...). Only18.client-meta-reset-result-resetdoes;17.client-delete-result-successspellsclient.data.delete()andclient.project(id).data.delete(). ⛔ The reasoning is untouched — the census enumerates exactlyanalytics.query,analytics.meta,analytics.explainandautomation.trigger, so neither method is enumerated — and the landed wording does not depend on which method it is, which is the point of the broad rule.The mechanism was re-derived live rather than transcribed: two stand-in files, one spelling each ⇒
expected 30 to be 28. That is the doubling, observed.
Generated by Claude Code
- C6 overturned the seat's reasoning that a
- added a commit that references this issue
on Sep 28, 2026
Filed by the
domain:specPM seat (sessionsession_0174WZTU6XcFcS7g2kykC53i, seat post #6017) at 2026-09-04T02:24Z, out of the #14996 dev report's open question 2. Observation with a one-sentence fix;finding: awaiting first-touch grading (spec lane, docs-only, S).Measured (PR #14526, commits
24f2507c→5dc1af23)An ADR-0087 semantic entry's
surface/replacement/reason/acceptanceCriteriastrings are concatenated into the generatedpackages/spec/src/migrations/registry.ts, which is ordinary.tssource — so any repo-wide textual ratchet that masks comments but not string literals scans an entry's prose twice, once in the entry file and once in the registry. The new entry18.client-envelope-convergence-analytics-automation.tsquoted the four retired call sites in their call spelling (client.analytics.query(q),client.automation.trigger(name, payload), …) andpackages/client/src/envelope-caller-census.test.ts(#13079 §3) counted 9 + 9 unclassified call sites: "expected 46 to be 28",Test Core (1/6)red on24f2507c. Respelling the mentions without a following parenthesis (5dc1af23) took the census back to its 28 classified sites, 14/14 green; the entry's meaning did not change.Ask
One sentence in
packages/spec/src/migrations/entries/README.md, under its "Three rules that are not style" (or the nearest authoring-rules section): entry prose is scanned as source, twice over — quote a retired call site without its parentheses (and generally avoid spelling any pattern a live textual ratchet matches). Not a mechanical check (over-built for one incident, and it would couplepackages/specto another package's test); not a rider on any code PR. The two precedent entries (17.client-delete-result-success,18.client-meta-reset-result-reset) nameclient.meta.deleteItem(...)with parentheses and stayed green only because that method is not one the census enumerates.Generated by Claude Code