Skip to content

docs(spec): record that ADR-0087 entry prose is scanned as source twice - #19676

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-15130-entry-prose-scanned-as-source
Sep 22, 2026
Merged

os-warren merged 2 commits into
mainfrom
claude/issue-15130-entry-prose-scanned-as-source

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes #15130

Clause-②: no

One file, one section: a fourth bullet under the authoring-rules heading of
packages/spec/src/migrations/entries/README.md, plus the patch changeset the
measurement below says is owed. Prose only — no behaviour change, no mechanical check,
no entry edited.

The rule that landed, and why the broad wording

Entry prose is scanned as source, twice over — never spell a shape a live textual
ratchet matches.

The narrow wording the incident suggests — "quote a retired call site without its
parentheses" — would make counter-examples of entries that are green today. What an
author actually controls is not spelling a shape some ratchet matches; the parenthesis
is the instance, and the bullet carries a warning saying so, so that an existing entry
spelling a parenthesised call is not read as a violation.

The heading moved from "Three rules that are not style" to "Four". Nothing in the repo
references that heading or its anchor (git grep, one hit: the heading itself).

What I re-derived before writing it down, and what did not match

The mechanism — confirmed, live, end to end. Not transcribed. Two untracked stand-in
.ts files were placed 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:

leg packages/client census verdict
baseline, clean tree 20/20 pass, 28 classified sites exit 0
both stand-ins present "expected 30 to be 28", 4 tests red exit 1
stand-ins removed 20/20 pass, 28 classified sites exit 0

Two files, one spelling each, +2 — the doubling, measured. The failure text named both
files by path and flagged each as "inside the ' literal". Restoration was proven by
observing state (git status --porcelain and git diff HEAD both empty), not by an exit
code. No tracked file was touched in any leg.

Reading the scanner confirms why: sitesInSource calls maskComments (comments only —
literals deliberately intact), walks the whole workspace from the repo root over
.ts/.tsx/.js/.mjs/.cjs, and matches on the method name followed by an opening
parenthesis. NON_ENTRY_FILES in the generator excludes README.md, so this edit cannot
reach generation.

The precedent trap — confirmed in substance, with one correction to the card. The
census enumerates four methods only (analytics.query, analytics.meta,
analytics.explain, automation.trigger). Neither meta.deleteItem nor data.delete
is among them, which is why the two precedent entries are green — and the clean-tree run
reports "no counted site sits inside a string, template or regex literal", so today no
entry's prose is counted at all.

⚠️ Correction: the card and the claim both say the two entries spell
client.meta.deleteItem(...). 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 survives intact; the method name does not. The landed wording
does not depend on which method it is, which is the point of the broad rule.

Changeset — measured, and it contradicts the expected shape

skip-changeset is not applicable here and no label is owed on that ground.
packages/spec's files[] carries a bare README.md, which matches at every depth,
so this file ships. Measured rather than reasoned, with npm pack --dry-run --json in
packages/spec:

  • target: src/migrations/entries/README.md — present in the 277-file tarball
  • positive controls: llms.txt present, src/ai/agent.zod.ts and two more *.zod.ts
    present — the instrument reports hits when hits exist

A published surface moved, so the diff takes a patch changeset
(.changeset/15130-entry-prose-scanned-as-source.md). Clause-② is no, no arm, nothing
breaking.

Acceptance notes

  • Not filed, noted only: the two precedent entries were left untouched, as dispatched.
    They are green and their meaning is right; the new bullet is written so that they stay
    correct rather than becoming violations.
  • Not filed, noted only: the census's failure text already names the string-literal
    trap and points at the offending file and line. An author who trips this rule today gets
    a much better diagnostic than the original incident did — the rule shortens the
    diagnosis, it is no longer the only thing standing between an author and a confusing red.
  • ⛔ No mechanical check was added, by the card's own judgement: it would couple
    packages/spec to another package's test suite.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx


Generated by Claude Code

An ADR-0087 entry's strings are concatenated verbatim into the generated
`registry.ts`, which is ordinary `.ts`, so a repo-wide textual scan reads the
same sentence twice. The house code/prose separator masks comments and leaves
string literals intact by design, so a quoted example is code to every scan
built on it — which is how prose in `packages/spec` can turn another package's
test red.

Adds a fourth bullet to the authoring-rules section stating the broad rule
(never spell a shape a live textual ratchet matches) with the parenthesised
call spelling as its instance, and records why an existing entry that does
spell one is not a counter-example.

Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
Co-authored-by: Claude <noreply@anthropic.com>
`src/migrations/entries/README.md` ships inside `@objectstack/spec` — `files[]`
carries a bare `README.md`, which matches at every depth. Measured with
`npm pack --dry-run`: the tarball is 277 files and this one is among them, so
the diff moves a published surface and takes a `patch`.

Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Sep 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/migrations/entries/README.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/migrations/entries/README.md) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 93cdc43d51dac6723a9dc88244193fe8629d61be → packageMentionDocs.

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

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

2 participants