Commit 0c2ec44
committed
ci(spec): a spec-named symbol must be derived, not hand-written (objectstack#4115)
objectui declares local types and consts under the same names as
`@objectstack/spec` exports, with doc comments claiming spec canonicity. Every
audited instance had either drifted or was one spec release away from it:
`ActionType` (hand union, missing `form`), `ChartType` (7 of 19 members),
`ActionLocation` + 2 (re-declared union + tuple + `z.enum` under a comment that
said "re-export here"), `ActionParamFieldType` (16 of 49 members).
The cost is specific to agent-driven development: an agent reads the comment,
takes it as ground truth, and builds on it — #2901 was filed with a backwards
premise because the fork carried the spec's own symbol name. A wrong
canonical-claim is not stale documentation, it is a planted premise.
Lands as the check rather than an AGENTS.md paragraph, which is the same
"declared != enforced" landmine the thread is about.
- Enumerates all 4231 `@objectstack/spec` export names (types AND values, via the
compiler's view of each subpath's `.d.ts` — a runtime `import()` sees only
values, and the drifted symbols are mostly types).
- Flags an exported declaration whose name the spec owns unless it is derived:
re-exported from the spec, or declared with a spec import in a STRUCTURAL
position. A spec name merely mentioned inside a members block does not count —
that is what a fork looks like. Consts do not descend into array/object
literals, because a faithful copy passes every value comparison and only
reference identity distinguishes a re-export from a fork.
- Ledger of the 156 pre-existing collisions names each symbol rather than
counting per package: a count is a budget that lets the next fork land as long
as an unrelated one was fixed, and it makes the failure point at whichever
symbol sorts first instead of the one just written. `--ledger` regenerates it.
- Shrink-only both ways: a fresh collision fails by name, and a fixed symbol
fails until its ledger entry is deleted.
Barrel re-exports through a relative path or a `@object-ui/*` sibling are not
counted twice; the declaration site they point at is judged on its own turn.
Discrimination proof — three ways it fails, run before landing:
1. reverting `ActionType` to the pre-#2231 hand union names it exactly:
"type `ActionType` packages/types/src/ui-action.ts:80 (exported by
`@objectstack/spec/ui`)"
2. a new fork in a package with an empty ledger fails on the PR that writes it
3. renaming a ledger symbol to a dialect fails until its entry is deleted
Refs objectstack#4115, objectstack#4074, #3003, #3009, #2944, #2911
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvv6qysks2dRLaGfpTdgEy1 parent b41f401 commit 0c2ec44
3 files changed
Lines changed: 626 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
78 | 86 | | |
79 | 87 | | |
80 | 88 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
| |||
0 commit comments