Skip to content

Commit 48f5200

Browse files
os-warrenclaude
andauthored
docs(spec): record that ADR-0087 entry prose is scanned as source twice (#19676)
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.com/claude-code) https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx --- _Generated by [Claude Code](https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 408ca2e commit 48f5200

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
`src/migrations/entries/README.md` — the ADR-0087 entry authoring rules now record that an entry's prose is scanned as source **twice**, and that the rule is never to spell a shape a live textual ratchet matches (#15130).
6+
7+
This file ships inside the package (`files[]` carries `README.md`, which matches at every depth — measured with `npm pack --dry-run`: 277 files, this one among them), so the rules an entry author reads are these.
8+
9+
- **The mechanism is the counter-intuitive part.** Every string an entry declares — `surface`, `replacement`, `reason`, `acceptanceCriteria` — is concatenated verbatim into the generated `src/migrations/registry.ts`, which is ordinary `.ts`. A repo-wide textual scan therefore reads the same sentence once in the entry file and once in the registry. The tree's one code/prose separator masks **comments** and leaves **string literals** intact by design, so a quoted example is code to every scan built on it, and prose in this package can turn **another package's** test red.
10+
- **The rule is the broad one, and the parenthesis is its instance.** A rule worded as "quote a retired call site without its parentheses" would make counter-examples of entries that spell a parenthesised call and are green — they go unmatched only because no live ratchet enumerates *those* methods, which is a fact about today's ratchets rather than a licence. What an author controls is not spelling a shape some ratchet matches; the guidance is to name the surface rather than spell a call of it.
11+
12+
No schema, no export and no authorable key moves; the three existing rules in that section are unchanged and no entry was edited.

‎packages/spec/src/migrations/entries/README.md‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export const entry: SemanticMigration = {
6363
The copyright header and the type import are file scaffolding and are **not** carried
6464
into the registry; the run of `//` comments immediately above `export const entry` is.
6565

66-
## Three rules that are not style
66+
## Four rules that are not style
6767

6868
- **Touch no other file, and never edit inside the markers.** `registry.ts`'s
6969
`<os-generated …>` regions are output. A hand edit there is reverted by the next
@@ -76,6 +76,23 @@ into the registry; the run of `//` comments immediately above `export const entr
7676
the *same* entry collide in git — on a registry where a dropped entry produces no
7777
error anywhere, a layout in which the second case merges quietly is a layout that
7878
loses one of the two edits.
79+
- **Entry prose is scanned as source, twice over — ⛔ never spell a shape a live
80+
textual ratchet matches.** Every string an entry declares is concatenated verbatim
81+
into `registry.ts`, which is ordinary `.ts`, so a repo-wide scan reads the same
82+
sentence once here and once there. This tree's one code/prose separator masks
83+
**comments** and leaves **string literals** intact on purpose, so to every scan built
84+
on it a quoted example is code — which is how prose in `packages/spec` turns
85+
**another package's** test red. Measured on #14526: the new entry named four retired
86+
call sites in their call spelling — the method with its opening parenthesis —
87+
`packages/client/src/envelope-caller-census.test.ts` counted 46 against the 28 it
88+
pins (nine mentions, each counted twice) and `Test Core` went red; respelling them
89+
without the parenthesis restored 28 and changed nothing the entry meant. Name the
90+
surface, ⛔ don't spell a call of it.
91+
92+
⚠️ The parenthesis is the instance, not the rule. An older entry that does spell a
93+
call is not thereby wrong — it goes unmatched only because no live ratchet
94+
enumerates *that* method, which is a fact about today's ratchets and not a licence;
95+
the pattern that catches you is whichever one exists when your entry lands.
7996

8097
## What this does not fix
8198

0 commit comments

Comments
 (0)