Skip to content

Commit 0c2ec44

Browse files
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_01Rvv6qysks2dRLaGfpTdgEy
1 parent b41f401 commit 0c2ec44

3 files changed

Lines changed: 626 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ jobs:
7575
- name: Install dependencies
7676
run: pnpm install --frozen-lockfile
7777

78+
# A local type/const declared under a `@objectstack/spec` export's NAME reads
79+
# to the next agent as the spec's own definition — four such symbols had
80+
# already drifted from the spec they claimed to be (objectstack#4115). Needs
81+
# the install (it reads the spec's own `.d.ts`) but not the build, so it runs
82+
# before the expensive steps.
83+
- name: Verify spec-named symbols are derived, not hand-written
84+
run: pnpm check:spec-symbols
85+
7886
- name: Turbo Cache
7987
uses: actions/cache@v6
8088
with:

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"lint:coverage": "node scripts/check-lint-coverage.mjs",
3434
"type-check": "turbo run type-check",
3535
"type-check:coverage": "node scripts/check-type-check-coverage.mjs",
36+
"check:spec-symbols": "node scripts/check-spec-symbol-derivation.mjs",
3637
"cli": "node packages/cli/dist/cli.js",
3738
"objectui": "node packages/cli/dist/cli.js",
3839
"create-plugin": "node packages/create-plugin/dist/index.js",

0 commit comments

Comments
 (0)