Repository navigation
Commit 7e40d1d
fix(devx): check the published skill fence against the scaffold emission policy (#19210)
Part of #16767 — the `--check` leg only. The `gen:` write-back leg named
on the card stays open and unaddressed here; it needs its own ruling, so
no closing keyword.
## The gap
`check:scaffold-emission-policy` pinned `SCAFFOLD_TYPESCRIPT_RANGE` on
**one** of its two hand-written restatements — the bundled template
`packages/create-objectstack/src/templates/blank/package.json`. The
other restatement, the "Complete Working Example" `package.json` fence
in `skills/objectstack-platform/SKILL.md`, had no reader at all, and it
is the one that drifted longest (to a retired major).
Measured on `origin/main` at `6e4024c76` before the change:
```
scripts/sync-scaffold-emission-policy.mjs:116 constant: 'SCAFFOLD_TYPESCRIPT_RANGE',
occurrences of `objectstack-platform` in that gate: 0 (it cannot reach the fence)
firing control, same grep for a term it DOES contain: 5 (SCAFFOLD_PNPM_RANGE)
```
## What this adds
A **check-only** second carrier:
- `PUBLISHED_RESTATEMENTS` — a hand-kept table of (file, fence, keys).
One row today: the skill's fence, checking `devDependencies.typescript`.
- `readRestatementFence()` — locates the fence by **content** (heading,
then the label line inside that section, then the first fence, whose
language tag must match), never by line number: this carrier's line
number is already measured moving (608 the day the gap was filed, 613 on
the card). Every step demands exactly one match and throws otherwise, so
a renamed section reds instead of degrading into "nothing found, all
green".
- `checkRestatement()` — parses the fence and compares against the same
`SCAFFOLD_*` constants. A fence that drops a checked key is a hard
failure naming the path, matching the templates' contract.
## What this deliberately does NOT do
- **No `gen:` write-back.** `skills/**` is a governed surface (Prime
Directive #14) and a generator writing into a governed published file
needs its own ruling. The restatement pass runs under `--check` only;
the write run — the one `packages/create-objectstack`'s `build` invokes
— neither reads nor writes the carrier, which also keeps the on-ramp's
build free of a dependency on a published skill file.
- **No byte under `skills/**` is touched.** Reading a governed file is
not governing it, so this stays an ordinary devx PR.
`check-skills-token-ratchet` is green and the skill's token count is
unmoved.
- **No prose pointer in the skill.** Already measured dead: that file
sits at 12980 tokens against a 12984 ceiling and the shortest conforming
sentence is 85 bytes, 17 tokens over.
- The failure text for a drifted restatement **refuses to point at `pnpm
gen:scaffold-emission-policy`** — it says so explicitly, because that
command does not and must not fix this carrier.
## Verification — both directions driven
**GREEN**, after the change, on the real tree:
```
packages/create-objectstack/src/templates/blank/package.json already emits the shared policy
skills/objectstack-platform/SKILL.md (## Complete Working Example) already restates the shared policy
✓ check:scaffold-emission-policy: 2 carrier(s) emit the shared policy — 1 bundled template(s) + 1 published restatement(s) (SCAFFOLD_PNPM_RANGE=>=10.15, SCAFFOLD_TYPESCRIPT_RANGE=^5.3.0).
```
Before the change the same line read `1 bundled template(s) emit the
shared policy`.
**RED**, by ablation in the worktree (never committed). Landing proof
read BEFORE any verdict — a substitution whose anchor matched nothing
exits 0, leaves the blob unchanged and reads exactly like "the gate did
not notice":
```
LANDING-PROOF BEFORE: head_blob=713cb7503e2a9cd6f884fd94a3b74617e275ee3f working_hash=713cb7503e2a9cd6f884fd94a3b74617e275ee3f grep(^5.3.0)=1 grep(^4.4.4)=0
LANDING-PROOF AFTER: working_hash=9eaa632e945f513f2e53384ff145dcf3c1cc18d0 grep(^5.3.0)=0 grep(^4.4.4)=1
✗ a published restatement of the shared emission policy has drifted.
Policy source: packages/cli/src/commands/init.ts (SCAFFOLD_PNPM_RANGE=>=10.15, SCAFFOLD_TYPESCRIPT_RANGE=^5.3.0)
skills/objectstack-platform/SKILL.md — the `## Complete Working Example` **`package.json`**: fence: devDependencies.typescript is "^4.4.4" but SCAFFOLD_TYPESCRIPT_RANGE declares "^5.3.0"
MUTATED_CHECK_EXIT=1
```
The write run was driven under the same mutation and stayed green with
the carrier byte-identical — the check-only contract, observed rather
than asserted:
```
WRITE_EXIT=0 carrier_hash_unchanged=yes
```
**RESTORE**, byte-identical:
```
LANDING-PROOF RESTORE: working_hash=713cb7503e2a9cd6f884fd94a3b74617e275ee3f head_blob=713cb7503e2a9cd6f884fd94a3b74617e275ee3f equal=yes
grep(^5.3.0)=1 grep(^4.4.4)=0
DIFF_EMPTY=yes
RESTORED_CHECK_EXIT=0
```
The ablation is also permanent as fixture-driven self-test batteries `H`
(16 assertions) and `I` (4), so the leg cannot go mute again without a
red: the carrier is observed counted in the verdict, reddening on fence
drift and naming that fence, refusing a dropped key and a renamed
heading, and left byte-identical and unreached by the write run.
`--self-test` now reaches 49 assertions.
## Gates
`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` derived 29 commands from the committed change set; all 29
ran green (`check:pm-dispatch-gates` timed separately — it is a band,
not a point). Additionally run, because their rosters live under
`scripts/` where this diff is: `check-skills-token-ratchet` (green,
skill token count unmoved), `check-published-list-mirrors`,
`check:console-injection`, `check:engine-double-contract`,
`check:i18n-stale-fill`, `check:pm-governed-prose` — all green.
`check:dts-closure` and `check:published-readme-exports` returned exit
**3 = PREREQUISITE NOT MET** (no package `dist/` in this worktree);
their subject is built `.d.ts` output, which this diff does not touch.
Recorded as NOT MEASURED locally, left to CI, not as a pass.
Lint: the repo-wide `eslint . --no-inline-config` run completed here
over **6906** files with 0 errors and 0 warnings, so no narrowing was
needed.
Type-check lane: not applicable. The only changed file is a root-level
`.mjs` with no TypeScript; no package `tsconfig` reaches `scripts/` at
the repo root, and `turbo run typecheck` is per-package.
## Changeset
None — `skip-changeset`. Nothing already published moves: the sole
changed path is `scripts/sync-scaffold-emission-policy.mjs`, the root
package is private, and no published package's `files[]` ships anything
under `scripts/` (scanned every non-private workspace manifest; zero
hits).
## Acceptance notes
Noted, not filed:
- The same fence restates four `@objectstack/*` ranges by hand. The card
and triage both ruled these out for now: this gate does not know the
release range, and introducing one here would create the second version
authority that causes this whole defect family. Carrier for that:
whoever next touches `sync-template-versions.mjs`, which already owns
`@objectstack/*` ranges for the bundled templates.
- The `gen:` write-back half of the card remains genuinely open and is
what keeps this PR from closing #16767.
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
---
_Generated by [Claude
Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 6e4024c commit 7e40d1d
1 file changed
Lines changed: 352 additions & 5 deletions
0 commit comments