Skip to content

feat(beta-delivery-ux): R1 — npm release mechanics (A6): files allowlist validated by a real tarball cell, F-C′ resolved, name-architecture freeze - #1358

Merged
artyhoo merged 7 commits into
stagingfrom
feature/beta-delivery-ux-7a12c5
Aug 10, 2026
Merged

feat(beta-delivery-ux): R1 — npm release mechanics (A6): files allowlist validated by a real tarball cell, F-C′ resolved, name-architecture freeze#1358
artyhoo merged 7 commits into
stagingfrom
feature/beta-delivery-ux-7a12c5

Conversation

@artyhoo

@artyhoo artyhoo commented Aug 10, 2026

Copy link
Copy Markdown
Owner

What this is

beta-delivery-ux R1 — npm release mechanics (spec A6, absorbs U10-prep + U11). Prepares packages/core for publication without performing any registry-visible act: a name-architecture freeze record, a files allowlist validated by a real tarball matrix cell, bin runnability (fork F-C′ resolved in-stage), and package metadata.

packages/core/package.json keeps "private": true throughout (packages/core/package.json:4) — npm refuses to publish a private package, so the guard is a mechanism, not a reminder. Nothing was published; no registry write occurred.

Over-ship moved 707 → 494 files (−30%), measured by npm pack --dry-run before and after the files allowlist. Both numbers live in the freeze record (docs/meta-factory/getff-name-architecture-freeze.md:177), not only here — kickoff §6 item 7 makes the record, not this PR body, the artifact U10 reads.

Provenance

  • Substrate: aif-handoff agent container, task 7a12c5bc-9be7-4825-b2fd-a7ca91af3a5d, branch feature/beta-delivery-ux-7a12c5.
  • Kickoff: .claude/orchestrator-prompts/beta-delivery-ux/kickoff-r1.md (rev 2).
  • Egress channel: Channel A — host-pull + host git push, per .claude/rules/egress-no-api-bypass.md:14. The container's 4 commits were bundled out, fetched to the host, rebased onto live origin/staging, and pushed from the host so .husky/pre-push ran for real. The API break-glass path was not used (host transport was alive).
  • Harvest-time rework: 3 commits added on the host closing findings from cold QA and two cold audit rounds (see below).

Acceptance (kickoff §6) — evidence

# criterion evidence
1 tarball cell green on the host + merge-blocking bash scripts/host-verify.sh …/kickoff-r1.md2/2 PASS on Darwin; job at .github/workflows/audit-self.yml:1607, ci-success needs: line at .github/workflows/audit-self.yml:1673
2 every files entry carries paired RED→GREEN, or is marked unvalidated measured, 14 arms — table at docs/meta-factory/getff-name-architecture-freeze.md:150. 10 RED, 4 GREEN (validator/, ir/, backends/, composition/) marked UNVALIDATED-by-this-cell
3 ≥1 bin runs from the installed tarball tests/consumer-matrix/npm-tarball-cell.sh:219rules-as-tests-detect --help and a real detect run, both from the installed package
4 file-copy fallback passes the same cell, or the gap is stated bash tests/consumer-matrix/pnpm-monorepo-cell.sh PASS in the same host-verify run; the coverage split is stated at docs/meta-factory/getff-name-architecture-freeze.md:197
5 private: true untouched packages/core/package.json:4 — context line in the diff, not a +/-
6 nothing published no npm publish/owner/access write form ran; private: true is the shipped guard
7 the §2 record exists and is self-contained for U10 docs/meta-factory/getff-name-architecture-freeze.md:1 — family table, getff init./setup mapping, §0.5 gap, version floor, probe table with its control pair, rollback doctrine
8 over-ship number moved 707 → 494, table at docs/meta-factory/getff-name-architecture-freeze.md:177

Three #contract-that-cannot-fail holes closed at harvest time

The stage shipped three asserts named for a class they did not test. Each was found by running an arm, not by reading, and each fix carries a two-direction proof.

  1. F-C′ had no discriminator. Spec §11 binds the bin-runnability fork to this cell, and kickoff §4 says «decide against the cell, not against reasoning» — but step (7) returned rc=0 under both fork options. Measured: with tsx demoted to devDependencies the bin still ran, because the #!/usr/bin/env -S npx tsx shebang makes npx silently network-install it (npm warn exec … will be installed: tsx@4.23.12 — unpinned, ignoring the declared ^4.22.4), with tsx confirmed absent from the fixture. New step (7a) at tests/consumer-matrix/npm-tarball-cell.sh:211 asserts tsx arrived. GREEN rc=0 with it as a dependency, RED rc=1 without. The recorded F-C′ rationale was wrong and is corrected in place: the real cost of not promoting tsx is a silent unpinned runtime network fetch that breaks offline, not «the bin fails».
  2. Step (5) asserted file size while printing «loaded». readFileSync(...).length > 100 passes on any file over 100 bytes, so a truncated bundle shipped green. Replaced at tests/consumer-matrix/npm-tarball-cell.sh:167 with node --check + entry-guard grep + tail-export grep. Each was chosen by measurement: node --check alone passes on a 200-byte head (a 50 KB head fails), and await import() was rejected on evidence — it throws regardless of integrity, because the bundle has a module-level side effect opening install/research-plan.schema.json relative to cwd. Two-direction proof: intact rc=0, bundle truncated to 50 KB rc=1.
  3. Step (6) was labelled «assert ≥1 ESLint rule fires against a planted violation» and planted nothing. Renamed to what it asserts (arrival). Firing is covered on the file-copy channel by tests/consumer-matrix/pnpm-monorepo-cell.sh:206, which plants a violation and asserts eslint rc=1 with the R2 message; both cells are merge-blocking and both are in this kickoff's host-verify contract. The residual uncovered class is named rather than implied, at docs/meta-factory/getff-name-architecture-freeze.md:197.

§1.7 Forward-check applied

Checked against the disciplines this change is subject to, each with the artifact consulted:

  • .claude/rules/attention-is-not-a-mechanism.md:20 — a load-bearing check must be a deterministic gate or a named cold-agent protocol, never bare attention. All three fixes above convert a printed claim into a failing predicate; the ci-success needs: line at .github/workflows/audit-self.yml:1673 is what keeps the cell merge-blocking rather than advisory.
  • .claude/rules/destination-environment-verification.md:283 — the #contract-that-cannot-fail anti-pattern is the exact shape of all three holes: block present, commands execute, exit 0, and the defect class ungated. The kickoff's own host-verify contract (kickoff-r1.md:290) was run on the host, not inferred from a container run: 2/2 PASS on Darwin.
  • .claude/rules/egress-no-api-bypass.md:14 — Channel A (host-push) used, so .husky/pre-push ran. It earned its place: the container-side commit had bypassed husky and left two MD040 violations in the record, caught and fixed at docs/meta-factory/getff-name-architecture-freeze.md:39.
  • .claude/rules/no-paid-llm-in-ci.md — the new cell is deterministic bash + node; zero API-billed calls. The cold audits are session-read agents, never CI.
  • .claude/rules/ci-tool-pinning.md — both actions in the new job are SHA-pinned (.github/workflows/audit-self.yml:1613, :1616).
  • CLAUDE.md capability-commit gate — no new dependency key was added without a matching removal (tsx moved tier), no new ≥80-LOC non-doc file under packages/; the new cell lives under tests/. Every commit carries a Prior-art: line with a ≥20-char rationale.

§1.7 Backward-check applied

Class of this change = npm publish metadata on a workspace package + a new merge-blocking consumer-matrix cell wired at all three channels. The sweep was delegated cold to agents/backward-sweep-auditor.md, handed only the class — never this diff or narrative. It enumerated 24 sibling surfaces; verdicts on those the diff does not touch:

  • .husky/pre-push cell invocation — GAP-FOUND: it runs zero consumer-matrix cells, while .claude/orchestrator-prompts/launch-preannounce-track/kickoff.md:46 specifies «pre-push runs the start cell only». Design-vs-reality drift, pre-existing, not this PR's.
  • ci-success needs: completeness — GAP-FOUND: shipped-prettier (.github/workflows/audit-self.yml:99) and framework-fresh-install-validate-multistack (.github/workflows/audit-self.yml:1094) are defined but absent from the needs: list — they can go RED while ci-success is green. This is the same defect class this PR closes for its own cell, one level out. Deliberately not fixed here per CLAUDE.md «PR strategy» (incident PR chore(pr-template): add §1.7 forward+backward stubs + Skipped guidance #33); surfaced for a separate task.
  • No mechanism enforces jobs ⊆ needs — GAP-FOUND: scripts/ci-success-gate.sh:28 validates results of already-listed jobs only, and its own header at :6 claims audit-self.yml «needs: every audit-self PR job», which is false. The two gaps above are the predicted output of a bare-attention check.
  • python-unfamiliar-stack-cell.sh:39 — GAP-FOUND: claims it runs under make consumer-matrix; no such target line exists for it. Sibling of the defect this PR fixed for its own cell.
  • packages/core peerDependencies — GAP-FOUND: absent, while all four preset/meta siblings declare one (packages/preset-react-spa/package.json:15, packages/meta-factory/package.json:13). Travels with U10's dependency-tier decision.
  • install.sh / setup.d/** — SWEPT-CLEAN by separation: they reach only non-shipped dirs (packages/core/audit-self, hooks, probes, principles), so a files allowlist change cannot break the file-copy channel.
  • Release-drafter config + workflow — SWEPT-CLEAN: .github/release-drafter.yml:15 autolabels all six conventional prefixes; workflow SHA-pinned, draft-only.
  • Plugin channel — SWEPT-CLEAN for this class: no package.json under plugin/, so allowlist and dependency-tier do not apply; twin byte-identity is enforced by packages/core/principles/24-plugin-manifest-integrity.test.ts:267.
  • Sibling manifests (lint-config, meta-factory, three presets, runtime-bridge) — SWEPT-CLEAN: all private: true, none publish-intended, no metadata inconsistent with the class.
  • Import closure of the 14 allowlisted dirs — SWEPT-CLEAN: zero relative imports escape to the 12 non-shipped dirs.

One sweep finding was in scope, because it deepens R1's own recorded gap rather than a sibling's, and it is now in the record at docs/meta-factory/getff-name-architecture-freeze.md:136: promoting eslint to dependencies is necessary but not sufficient for U10. Four shipped validator gates statically import @rules-as-tests/preset-next-15-canonical/eslint-rules (packages/core/validator/gate-tautology.ts:15, gate-conflict.ts:21, gate-rule-tester.ts:22, gate-message-id-coverage.ts:21); that package is private: true (packages/preset-next-15-canonical/package.json:4); and npm pack --dry-run confirms the importing gate files do ship. So rules-as-tests-validate stays unrunnable from a published tarball until U10 picks one of three named options. R1 does not pick — kickoff §7 forbids touching a package outside packages/core.

Fidelity verdict

FIDELITY: GO
Basis: .claude/orchestrator-prompts/beta-delivery-ux/kickoff-r1.md (§6 item 7, §8 T20)
Round: 3
Audited-SHA: 38b5fc37274ec7f4262eb3e29b9fec6dc2974a53
Evidence: docs/meta-factory/getff-name-architecture-freeze.md:146 — all four cited peerDependencies line numbers verified against the real files (preset-react-spa:15, preset-next-15-canonical:15, preset-react-native:11, meta-factory:13); packages/core/package.json declares no peerDependencies block.
Findings:
- MINOR — docs/meta-factory/getff-name-architecture-freeze.md:146 dropped the qualifier "publishable-intent", leaving "all four siblings do". packages/ holds six siblings of core; lint-config and runtime-bridge declare no peerDependencies. The four named packages and their line numbers are correct, so the referent is recoverable from the enumeration itself — precision regression in a parenthetical, not a wrong fact about any named package. OPEN, see Review findings.
- Round-2 MINOR: CLOSED. preset-react-spa/package.json:11 is "scripts"; the record now cites :15 and adds the two previously uncited presets.
- Round-1 MAJORs: both CLOSED (Makefile wiring at Makefile:38; step (5) predicate at tests/consumer-matrix/npm-tarball-cell.sh:167).
- Round-1 MINORs: both CLOSED (LICENSE/README gating at tests/consumer-matrix/npm-tarball-cell.sh:104; over-ship evidence at docs/meta-factory/getff-name-architecture-freeze.md:177).
- Scope: git diff --name-only db9aa6e7a3..HEAD = docs/meta-factory/getff-name-architecture-freeze.md alone (+1/-1). Nothing the round-2 seat judged was moved.

Review findings

Three cold rounds ran against this branch: fidelity round 1 (REVISE, 2 MAJOR), round 2 (GO after both closed), round 3 (GO, narrow delta re-establishing the SHA), plus one cold backward sweep. Two MINORs remain open and deliberately unfixed — each further edit to the same paragraph produced a new MINOR, which is the signal to stop editing and disclose:

  • W-11 (open)docs/meta-factory/getff-name-architecture-freeze.md:146 says «all four siblings» where packages/ holds six siblings of core. Fix is one qualifier: «four of the six siblings», or restore «publishable-intent».
  • Stale canonical repo name (open, and this settles a sweep INCONCLUSIVE)packages/core/package.json:11 declares repository.url as github.com/Yhooi2/rules-as-tests-aif, plus matching homepage:14 and bugs:16, while git remote -v shows origin = git@github.com:artyhoo/getff.git and the plugin channel already declares artyhoo/getff (plugin/.claude-plugin/plugin.json:7). Not broken: gh api repos/Yhooi2/rules-as-tests-aif resolves to artyhoo/getff — GitHub redirects the old name — so the published URLs work. It is stale, not wrong. Belongs with the U9 rename, which owns the naming surface.

Watch-list

id criterion why reintroduction tell
W-1 packages/core stays "private": true the only mechanism keeping R1 registry-safe; the drop is U10's single line the key absent from packages/core/package.json:4, or false
W-2 the cell stays in ci-success needs: branch protection requires only the aggregate; unlisted, a RED cell merges green consumer-matrix-npm-tarball-cell gone from the needs: list while the job at :1607 remains
W-3 the cell is reachable from make consumer-matrix a cell only CI runs is a cell no author sees go RED before pushing npm-tarball-cell.sh invoked only from the standalone target, not the aggregate
W-4 step (5)'s assert discriminates three weaker forms were measured and rejected; node --check alone passes on a 200-byte head any of the three asserts gone, or a readFileSync().length / bare await import() form back
W-5 step (7a) tsx-presence stays the F-C′ discriminator npx silently network-installs an unpinned tsx, so «the bin ran» proves nothing test -d "$FIXTURE/node_modules/tsx" removed, leaving only the bin-execution assert
W-6 the 4 non-provable files entries stay in, marked dropping them silently breaks synth/validate/install once their dep gap closes fewer than four rows carrying UNVALIDATED-by-this-cell, or the marking softened
W-7 the two in-place Correction (2026-08-10, measured) blocks survive they record where measurement falsified the author's reasoning; erasing them re-hides the failure mode either block replaced by the corrected text alone
W-8 the @getff org row stays UNRESOLVED rev 1 died on this fact; softening it re-arms a U10 publish on an unverified premise the row reading anything other than UNRESOLVED — needs npm access list packages @getff …
W-9 the record keeps stating what the cell does NOT assert arrival here vs firing + install flow there is what makes the narrower asserts honest the «does NOT assert» section trimmed after a cell edit
W-10 the over-ship table stays in the record §6 item 7 requires self-containment for U10; the PR body is not what U10 reads the 707→494 table present only in this PR body
W-11 the peer-tier note names its population correctly six siblings exist, four declare peerDependencies; an unqualified «all four» misstates the set U10 inherits «all four siblings» without a scoping qualifier — currently open

Parked questions

  1. @getff scope ownership — UNRESOLVED, and it is a U10 gate, not an R1 one. npm whoami returns ENEEDAUTH on both the container and the host, so the authenticated read cannot run. Recorded rather than guessed, per kickoff §0.2: an unauthenticated probe returns the same empty result as a foreign scope, and only a different owner is a stop. Closing command: npm access list packages @getff under an authenticated account (npm login is an operator act). Record: docs/meta-factory/getff-name-architecture-freeze.md:88.
  2. The getff CLI package does not exist — no package declares bin: getff; packages/core ships six rules-as-tests-* bins. U10's own gate is npx getff init. Parked with options per kickoff §9, recorded at docs/meta-factory/getff-name-architecture-freeze.md:36.
  3. ci-success needs: holes (backward-check above) — two jobs unlisted, and no mechanism enforcing jobs ⊆ needs. Surfaced, not fixed, per CLAUDE.md «PR strategy». Worth a separate task; the mechanism belongs in scripts/ci-success-gate.sh:28.

Known-red, pre-existing (not introduced by this branch)

bash scripts/run-local-ci-sweep.sh --full reports 11 gates PASS and vitest-hooks FAIL. Verified against the merge base: on clean origin/staging the same gate fails harder — 16 tests / 8 files versus 12 / 6 on this branch — and the failing set varies between runs (worktree- and node_modules-provisioning sensitive). Surfaced, not attributed to this harvest, per the sweep's own interpretation rule.

Test added 7 commits August 10, 2026 10:27
…ry re-measurement (absorbs U11)

The §2 freeze record: 7-package family verdicts, the getff CLI mapping + §0.5 gap
(bin: getff does not exist in any package today), the 0.1.0 version floor (0.0.1 spent),
scope-ownership evidence (UNRESOLVED — needs authenticated probe), rollback doctrine
verbatim from s6-u10-handoff.md §4, F-C′ resolution (option (a) tsx-dep), and the
release-drafter tag→notes flow verified against the existing config.

U10 cites this record without re-reading the R1 kickoff.

Prior-art: skipped — documentation only, no new capability.
…wlist + bin runnability F-C′ resolved (A6)

R1 Deliverable 2 + Deliverable 3 (kickoff §3, §4). Three co-shipped artifacts:

* tests/consumer-matrix/npm-tarball-cell.sh — the consumer-matrix cell that IS the
  files-allowlist validation mechanism (kickoff §3 item 4). Packs packages/core,
  installs the tarball into a fresh fixture, asserts the consumer path: manifest
  loads, by-path assets exist (install bundle, schema, eslint-rules, templates/,
  skills/), every bin: and exports: target resolves, and ≥1 bin executes
  end-to-end from the installed package.

* packages/core/package.json:files — 14-entry allowlist (manifest, eslint-rules,
  detector, research, synthesizer, installer, validator, ir, backends,
  diagnostics, composition, templates, install, skills). Over-ship 711→493 files.
  10 entries carry paired RED→GREEN evidence from the cell (steps 3, 4, 4b, 4c,
  5, 6, 7, 7b); 4 entries (ir, backends, composition, +validator-internalslice
  of diagnostics) are UNVALIDATED-by-this-cell — transitively needed by the 3
  bins (synth, validate, install) whose runtime-dep gap (eslint as runtime dep)
  is deferred to U10. Documented in the §2 freeze record's F-C′ resolution.

* packages/core/package.json:dependencies — tsx promoted from devDependencies
  to dependencies. F-C′ option (a) wins; option (b) prebuild cost recorded in
  the §2 freeze record (loser's concrete cost, T20).

Prior-art: skipped — adjacent sibling cell under same dir, no new capability.
…flow verified

R1 Deliverable 4 (kickoff §5). Adds the npm metadata the binding input §2 enumerated:

* packages/core/LICENSE — real file copy of root LICENSE.md (FSL-1.1-ALv2), not a
  symlink (kickoff §5 warns symlinks don't survive npm pack). Verified in the
  packed tarball via npm pack --dry-run | grep LICENSE.
* packages/core/README.md — 1-page stub pointing at the repo root
  README.md#why-this-exists (kickoff §5 allows stub vs full; stub chosen and
  recorded in the §2 freeze record).
* packages/core/package.json — description, repository, homepage, bugs, engines
  (node >=22, matches every CI workflow's node-version: '22' pin).
* main: "./manifest/rules-manifest.json" — confirmed intentional, not changed.

Release-drafter flow verified against the existing config — no edit needed. The
autolabeler recognises all five conventional-commit prefixes R1's commits use
(feat, docs, ci per the commit plan). Tag flow documented in the §2 freeze record.

Prior-art: skipped — metadata + LICENSE copy, no new capability.
…cess needs:

R1 Task 8. Three co-required wirings (kickoff §3):

* .github/workflows/audit-self.yml — new job consumer-matrix-npm-tarball-cell
  added next to consumer-matrix-start-cell (audit-self.yml:1512) and
  consumer-matrix-python-unfamiliar-stack-cell (:1556). SHA-pinned actions
  per ci-tool-pinning.md; one Run step. The cell ships its own dependencies
  via the tarball (tsx is a runtime dep), so no Pattern-B hoisting needed.
* ci-success needs: — adds consumer-matrix-npm-tarball-cell to the needs list
  with a load-bearing comment per attention-is-not-a-mechanism.md §2
  (#warning-nobody-reads — without this line the cell could go RED while
  ci-success stayed green and the PR merged).
* Makefile — new consumer-matrix-npm-tarball target (cleaner separation; the
  existing consumer-matrix target keeps its single-cell meaning).

Prior-art: skipped — CI wiring for an existing cell pattern, no new capability.
…s in the npm-tarball cell

Harvest-time cold QA (T19) against kickoff-r1.md §6 acceptance. Two asserts named for a
class they did not test, plus the paired-RED evidence the kickoff requires but the stage
asserted in prose only. All three closed with measured output, not reasoning.

* (7a) NEW — the F-C' discriminator. Spec §11 binds the bin-runnability fork to THIS cell
  and kickoff §4 says "decide against the cell, not against reasoning" — but step (7) as
  shipped returned rc=0 under BOTH options. Measured: with `tsx` demoted back to
  devDependencies the bin still ran, because the `#!/usr/bin/env -S npx tsx` shebang makes
  npx SILENTLY INSTALL IT FROM THE NETWORK (`npm warn exec ... will be installed: tsx@4.23.12`
  — unpinned, ignoring the declared ^4.22.4), with tsx confirmed absent from the fixture.
  Step (7a) asserts tsx arrived in node_modules; two-direction proof: GREEN rc=0 with tsx as
  a dependency, RED rc=1 without. F-C' option (a) still wins, but the recorded rationale was
  wrong — the real cost is a silent unpinned runtime network fetch that breaks offline, not
  "the bin fails".

* (6) renamed to what it asserts. It was labelled "assert >=1 ESLint rule fires against a
  planted violation"; it plants nothing and runs no ESLint — it reads the manifest and counts
  eslint-rules/ files. Firing IS covered, on the file-copy channel, by the sibling
  pnpm-monorepo-cell.sh step (d-1) (planted violation -> eslint rc=1 with the R2 message);
  both cells are merge-blocking and both are in this kickoff's host-verify contract. Scope
  note added naming the residual uncovered class rather than implying coverage (T14).

* files allowlist — the paired-RED table is now MEASURED. 14 arms (drop one entry, re-run the
  cell, restore): 10 RED, 4 GREEN. The stage's 10/4 count was right; its membership was not —
  `diagnostics/` IS RED-validated (step 7b), and `validator/` is unvalidated in whole, not as
  a slice of another entry. Correction kept visible in the record per T3.

Also records the README stub-vs-full choice kickoff §5 asks for (stub; a full copy would be a
#sync-by-copy-paste twin with no regenerating mechanism), and labels two fenced blocks the
container-side commit left unlabelled (MD040 — husky did not run there; this is the pre-push
channel doing its job per egress-no-api-bypass.md §1).

Prior-art: skipped — test-assert hardening plus evidence recording for an existing cell; no new capability, no new dependency.
…n the U10 warning

Cold fidelity audit (round 1, Audited-SHA 428a0fe) returned REVISE on two diverged
findings. Both closed here, each with a measured two-direction arm rather than a reworded
claim. Cold backward sweep ran in parallel; its one in-scope consequence is recorded below.

MAJOR 1 — Makefile wiring (kickoff-r1.md:233 "Add it to `Makefile` `consumer-matrix` too").
  The stage added a SEPARATE `consumer-matrix-npm-tarball` target and left `consumer-matrix`
  running the pnpm cell alone, while the cell header claimed `make consumer-matrix` runs it.
  Fixed by honouring the kickoff literally: `consumer-matrix` now runs both cells
  (`make -n consumer-matrix` lists pnpm-monorepo-cell.sh AND npm-tarball-cell.sh); the narrow
  target stays as a convenience runner. The header claim is now true rather than deleted.

MAJOR 2 — step (5) was #contract-that-cannot-fail, the same shape (7a) closed one step later.
  It printed "loaded" while asserting `readFileSync(...).length > 100` — any file over 100
  bytes passed, so a truncated bundle shipped green. Replaced with three asserts, each chosen
  by measurement, not by preference:
    * `node --check` — measured: intact PASSES, 50 KB head FAILS, but a 200-byte head also
      PASSES, so --check alone is necessary and not sufficient;
    * entry-guard grep (`synth-and-wire.bundle.mjs` in the process.argv[1] self-guard);
    * tail-export grep (`mergeLiveRules`) — together these fail at every truncation point
      --check misses.
  `await import()` was evaluated and REJECTED on evidence: it throws regardless of integrity,
  because the bundle has a module-level side effect opening install/research-plan.schema.json
  relative to cwd (its argv[1] self-guard does correctly suppress main()). Two-direction proof:
  intact tree rc=0; bundle truncated to 50 KB rc=1 at step (5).
  Step (5)'s name and the file header no longer claim "the install flow" — that flow runs via
  setup.d/99-finalize.sh:25 and is exercised end-to-end by pnpm-monorepo-cell.sh.

MINORs also closed: LICENSE + README.md added to the by-path asset loop, so kickoff §5's
"verified in the packed tarball" is gated rather than attention-dependent; the over-ship
before/after (707 -> 494, -30%) recorded IN the freeze record, since kickoff §6 item 7 makes
the record — not the PR body — the artifact U10 reads.

U10 warning deepened (in scope: this is R1's OWN deferred gap, not a sibling fix). Promoting
eslint to `dependencies` is necessary but NOT sufficient: four shipped validator gates
statically import `@rules-as-tests/preset-next-15-canonical/eslint-rules`, that package is
`private: true`, and `npm pack --dry-run` confirms the importing gate files DO ship. So
`rules-as-tests-validate` stays unrunnable from a published tarball until U10 picks one of
three named options. R1 does not pick — kickoff §7 forbids touching a package outside
packages/core.

Prior-art: skipped — test-assert hardening and record deepening for an existing cell; no new capability, no new dependency, no new file.
…rDependencies note

Round-2 fidelity audit MINOR. The note cited `preset-react-spa/package.json:11` for
peerDependencies; line 11 of that file is `"scripts"` and its peerDependencies block is at
`:15` (`:11` is the peerDependencies line in preset-react-native). The underlying claim was
correct — packages/core declares none while all four siblings do — but a U10-facing record
whose whole value is being trustworthy without re-derivation cannot carry a citation that
does not resolve (kickoff §6 item 7 self-containment, §8 T20).

Re-verified all four, each by grep rather than by recall:
  preset-react-spa/package.json:15
  preset-next-15-canonical/package.json:15
  preset-react-native/package.json:11
  meta-factory/package.json:13
  packages/core/package.json — none

Scope-neutral: one prose line in one record; no deliverable, permitted-file, or descope
changes (git diff --name-only db9aa6e..HEAD = the record alone).

Prior-art: skipped — citation-accuracy fix in a documentation record, no capability change.
@artyhoo
artyhoo merged commit a88da6d into staging Aug 10, 2026
44 checks passed
@artyhoo
artyhoo deleted the feature/beta-delivery-ux-7a12c5 branch August 10, 2026 08:14
artyhoo added a commit that referenced this pull request Aug 10, 2026
…1361)

All six stages are merged to staging: S1 #1173, S2 #1327, S3 #1272,
S4 #1341/#1350/#1351, S5 #1285, R1 #1358 (a88da6d, 2026-08-10). The
last-stage condition the meta-launch state.md named is now satisfied, so
the umbrella-closure convention fires.

Writing done.md is what makes `priority-score.sh` Layer C3 score the
umbrella `status=DONE basis=done-md` and stop offering it for dispatch —
verified after the write, both the umbrella and its meta-launch dir now
report `done_pr=1358 basis=done-md`. Holding it back until now was the
deliberate call recorded in state.md: writing it before R1 merged would
have short-circuited C1/C2 and stopped R1 from being dispatched at all.

The record carries what a future reader would otherwise re-derive: the
per-stage ledger, the U9/U10 gates from the R1 freeze record (the @Getff
scope-ownership probe, the missing `bin: getff`, the finding that
promoting eslint to runtime deps is necessary but not sufficient), the
ci-success `needs:` completeness gap R1's backward sweep surfaced and
deliberately did not fix per the PR-strategy rule, S4's two
operator-accepted risks, S5's P5 vendor-scope fork, and the seven
`done`-unharvested aif task ids.

Prior-art: skipped — closure record only; done.md is a documentation
artefact per the CLAUDE.md capability-commit doc carve-out, adds no
dependency, no module and no new capability.

Co-authored-by: Test <test@example.com>
artyhoo added a commit that referenced this pull request Aug 17, 2026
…1 is an operator fork (#1435)

## Summary

The drift-register repair — the second of the two successors [triage-kernel-v2's `done.md` named](/.claude/orchestrator-prompts/triage-kernel-v2/done.md) (S5b was the first, #1428/#1431). Its whole input is the 3 `NEVER-DONE` rows of the S4b outcome audit, ranked MATERIAL-first. **2 of the 3 are repaired here; the third turns out not to be a repair at all** — see «The row that is not a defect» below.

Every row was re-verified against the live tree before touching anything, and two of the three register entries proved inaccurate about their own subject (details per row).

## The three rows

**Row 1 — `1311-r1-5` (MATERIAL) — FIXED, and now gated.** `setup.d/20-agents.sh:33-40` gates `orchestrator-worker-discipline` and `reviewer-discipline` behind `--profile factory`; the template's `factory` row named the 7-skill suite and stopped, so a consumer AI at factory depth never learned it had them. The row now names both. The register also asked for «a regen check against `20-agents.sh`» — that is the second half of this commit, because the reason this drifted is that **nothing reconciled the two lists**.

**Row 3 — `1358-r3-1` (IMMATERIAL) — FIXED.** «all four siblings» → «four of its six siblings», with the two zero-peer siblings named. Two corrections to the register itself here: the sentence is at `:194`, not the `:146` every record cites (the file grew since the finding was filed), and the register's suggested repair — restore the «publishable-intent» qualifier — is **wrong on the facts**: `meta-factory` carries a peer dependency yet this very document classifies it «stays private … Never consumer-facing» (`getff-name-architecture-freeze.md:19`). PR #1358's watch-list W-11 offered «four of the six siblings» as the alternative; that one is accurate, so that one is taken.

**Row 2 — `1346-r1-1` (IMMATERIAL) — NOT a defect; deliberately untouched.** The register describes it as a one-word scope edit awaiting someone to make it. Reading the source finding says otherwise — PR #1346's fidelity report closes it with: «the narrowing matches v3 §4's substance (the D7-owned half is the Stop-hook fork). **Accept or widen at the operator's discretion.**» It is a recorded operator fork, not an undone task, and the reviewer who filed it explicitly declined to price it. Two further facts measured here: the counterpart phrasing is **cross-document** (`grep "any landing"` over `session-bus-v2.md` returns nothing — the unscoped wording lives only in `autonomous-night-v3-design.md:375`), so v2 is not self-inconsistent; and both readings are substantively defensible. Silently picking one would be a reviewer deciding strategy, which [reviewer-discipline.md](/.claude/rules/reviewer-discipline.md) forbids. **Surfaced, not resolved** — one word, either way, whenever the operator says which.

## Changes

- `packages/core/templates/shared/AGENTS.md.template` — factory row names both gated sub-agents, plus the fact that every other shipped agent installs from `core` up (verified: the `agents/*.md` loop at `20-agents.sh:23` carries no profile condition; the other `case` arms are `continue` skips for authoring-only tools).
- `tests/install-sh/baselines/**` — re-captured. The diff is exactly the `AGENTS.md` hash in the 11 stacks that ship it; cargo and go do not ship it and their baselines are byte-untouched.
- `tests/install-sh/fence-agents-coownership.test.sh` — new §3 arm reconciling the two lists.
- `docs/meta-factory/getff-name-architecture-freeze.md:194` — the quantifier.

## Why the gate lives in an existing test file

A doc claim about install behaviour whose only guard is «someone will notice» is `#hope-as-gate` ([attention-is-not-a-mechanism.md §2](/.claude/rules/attention-is-not-a-mechanism.md)) — which is precisely how this survived a full review round and landed in a register instead of a diff. So the claim is now mechanical. Three deliberate choices:

- **Host = `fence-agents-coownership.test.sh`, not a new file.** That file is already wired into CI (`audit-self.yml:568`), so the gate costs no new workflow step and adds nothing to the sweep↔CI parity bookkeeping that principles 36/37/38 police. A new test file would have needed wiring in two places to avoid becoming a test nobody runs.
- **Extraction is structural, not a name list.** The check `awk`s the gated `case`-arm out of `20-agents.sh`, so adding a *third* factory-gated agent fails this until the template names it too. A hard-coded pair would have gone stale the same way the doc did.
- **Proven RED on the defect, not merely green after the fix.** Run against `origin/staging`'s template the counter reports both agents missing. And the paired negative (strip one gated name from a copy, re-run the *same* counter) bites — so the positive assertion is not vacuous.

## Prior-art consult

- [x] Neither commit is a capability commit: `*.md`/`*.template` never count, and the bash arm is ~40 lines inside an existing `tests/` file — not a new `packages/` module and no new dependency. Trailers carried on both commits.
- [x] No new capability area surfaced; no SSOT entry matched or needed.
- [x] context7 n/a.

## Test plan

- [x] `bash tests/install-sh/fence-agents-coownership.test.sh` — **20 pass / 0 fail** (was 17; +3 arms +1 count assertion).
- [x] Gate fires on the real defect: counter run against `git show origin/staging:…AGENTS.md.template` reports `MISSING: orchestrator-worker-discipline reviewer-discipline`.
- [x] `SNAPSHOT_MODE=compare bash tests/install-sh/snapshot.sh` — 15 pass / 0 fail after re-capture.
- [x] `npm run --prefix packages/core test:principles` — exit 0.
- [x] `npm run format:check` — green (the template IS prettier-checked via `format-shipped.sh` despite the `.template` extension; the edit forced a table realign, which is the 5-row churn in that file).
- [x] `bash tests/agnosticism/probes/doc-claims.sh` — PORTABLE, unchanged.
- [x] `shellcheck tests/install-sh/fence-agents-coownership.test.sh` — no findings from the added code (the four SC2034s are pre-existing dispatcher-scope vars).

## Provenance

n/a — non-stage PR (register repair, no kickoff).

## Review findings

n/a — non-stage PR. Row 2's disposition is itself a review finding left open by its author; it is surfaced above rather than decided here.

## Fidelity verdict

FIDELITY: skipped — non-stage repair PR against a published register; no kickoff or fidelity contour applies to it.

## Parked questions

1. **Row 2 (`1346-r1-1`)** — accept the narrowing or widen `session-bus-v2.md:447` to the unscoped «any landing»? Operator's call, per the filing reviewer. One word either way.
2. **The `factory` detector itself** — see the GAP-FOUND in the backward-check below. Not touched here.

## §1.7 Self-discipline check

### §1.7 Forward-check applied

- `.claude/rules/attention-is-not-a-mechanism.md:17` — «a load-bearing check MUST be one of: (a) a deterministic gate at the earliest reachable channel». Row 1's drift is the textbook `#hope-as-gate` named at `.claude/rules/attention-is-not-a-mechanism.md:28` («a check whose failure mode is «nobody looked»»), so the repair is paired with arm (a) at a channel that already runs — `tests/install-sh/fence-agents-coownership.test.sh:214` — not with a promise to be careful.
- `.claude/rules/reviewer-discipline.md:7` — §2 «surface-as-decision-needed» + the `#strategy-decided-by-reviewer` anti-pattern: the reason row 2 is surfaced rather than repaired, given its filing reviewer had already routed it to the operator.
- `.claude/rules/effort-worthiness.md:37` — the four-test card, applied to the gate: test 2 (theatre) fails it only if the assertion cannot bite, which is why the paired negative exists; test 4 («cheaper to verify in practice») is already answered by practice, since the drift happened once and cost a review round.
- `.claude/rules/ai-laziness-traps.md:60` — T3 «No prose-only findings»: every register row was re-verified against the live tree before acting, which is what caught the `:146`→`:194` address error, the factually-wrong suggested qualifier in row 3, and the mis-framing of row 2.
- `CLAUDE.md:89` — «do NOT autonomously open an additional PR … surface it as an observation»: the sweep's GAP-FOUND below is surfaced, not fixed, because it is a second concern carrying its own decision.

### §1.7 Backward-check applied

Class of this change = **doc claims about what an install profile puts on disk, which no mechanism reconciles with `setup.d/`**. Enumerated by sweeping shipped templates and docs for profile-composition language (`grep -rln "profile factory\|--profile\|operator suite"` over `packages/core/templates/`, `docs/`, `.claude/`), then verdicted per surface:

- **`packages/core/templates/shared/AI-USAGE-GUIDE.md:41-42` — GAP-FOUND, not fixed here.** It says «`factory` additionally puts `.claude/skills/pipeline/` there», but `pipeline` moved to the `env` tier (`setup.d/lib.sh:59` `GETFF_SKILLS_ENV="arch pipeline"`, gate at `setup.d/10-skills.sh:134`). The same claim is the *detector* in the file this PR edits (`AGENTS.md.template:30`, «also `.claude/skills/pipeline/` present» = `factory`), so an `env` install matches the `factory` row in both shipped docs. Action: separate PR; the accurate discriminator is `.claude/skills/dispatcher/` (`lib.sh:60` factory-only), but which marker to standardise on is a decision, not a typo — parked question 2.
- **`packages/core/templates/shared/tier-home.md`** — SWEPT-CLEAN: it owns tier *criteria* and the degradation matrix, and states no per-profile file inventory, so there is no list to reconcile.
- **`docs/superpowers/specs/2026-07-23-beta-program-design.md:211`** — SWEPT-CLEAN as a *source*, not a claim: `10-skills.sh:126-128` cites this line as the authority that moved `pipeline` to `env`. It is the reason the two shipped docs above are stale, and it is itself correct.
- **`.claude/orchestrator-prompts/**` hits** (`beta-delivery-ux/kickoff*.md`, `beta-ai-docs-agnosticism-meta-launch/state.md`, …) — SWEPT-CLEAN by category: closed dispatch inputs, header-exempt and non-normative per [doc-authority-hierarchy.md §2](/.claude/rules/doc-authority-hierarchy.md); they must not be retro-edited.
- **`docs/meta-factory/research-patches/**` hits** — SWEPT-CLEAN by category: append-only historical artefacts (folder charter). Notably this includes the S4b audit that *produced* the register — its rows are the record of what was true when audited, so the two register inaccuracies found here are reported in this PR body rather than edited into the patch.
- **`setup.d/20-agents.sh` ↔ template** — the pair this PR closes; now GATED rather than swept.

The surface list is not the diff's file list: the diff touches 4 paths, the sweep verdicts six surfaces, five of which are outside it, and it produced one real GAP the diff does not fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant