page:docs-specs-platform-gaps-phase0-pr-body
Phase 0 (M0): hard split, private registry, publish guards, Effect baseline reference
Branch phase0/hard-split-registry-guards → staging. 17 commits (git log --oneline origin/staging..HEAD), executing roadmap Phase 0 (docs/specs/platform-gaps/50-roadmap-workpackages.md §5): WP-RN-01, WP-RN-02, WP-RN-03, WP-RN-04, WP-RN-08 (spec 15) and WP-T3-32 stage 1 (spec 10), plus the owner-decided scope re-lock @agentfabric → @fabricstack.
Phase 0 (M0): hard split, private registry, publish guards, Effect baseline
Branch phase0/hard-split-registry-guards → staging. 17 commits (git log --oneline origin/staging..HEAD), executing roadmap Phase 0 (docs/specs/platform-gaps/50-roadmap-workpackages.md §5): WP-RN-01, WP-RN-02, WP-RN-03, WP-RN-04, WP-RN-08 (spec 15) and WP-T3-32 stage 1 (spec 10), plus the owner-decided scope re-lock @agentfabric → @fabricstack.
Invariants held throughout and re-verified at review time (2026-09-28): the public repository a5c-ai/babysitter was never written to (gh api repos/a5c-ai/babysitter --jq .pushed_at → 2026-09-16T19:59:50Z, unchanged since before cutover; the upstream remote's push URL is no_push); nothing was published to registry.npmjs.org (@fabricstack/smoke → HTTP 404 there); the only registry write is the throwaway smoke package in Artifact Registry.
Per-WP summary
| WP | Commits | What landed | Record |
|---|---|---|---|
| WP-RN-01 two-repo hard split | 9e2957a06, 7271ac146, b1146c911, 49db231a9 | Cutover baseline (public main feb68abe3, 341 public heads, 7 fabric refs, snapshots + watch-baseline.json); read-only API evidence that no hosting-level transport exists (fabric is not a fork, no mirror_url, no webhooks or deploy keys on either repo); branch ruleset protect-main-staging (id 24103129, active, deletion + non_fast_forward + pull_request on main/staging); docs/DIVERGENCE-POLICY.md with spec 15 §1.2 invariants 1–4 verbatim; publish.yml header no longer self-describes as a mirror; README repo-role statement; scheduled read-only propagation watch (upstream-propagation-watch.yml + scripts/ci/upstream-propagation-watch.sh) | watch-baseline.md |
| WP-RN-02 intake process + public-push guard | 030920ec6, 4ea32666c | scripts/ci/public-push-guard.cjs (8 rules, 13 negative + 9 positive fixtures, --self-test, node:test suite); CI job public-push-guard in ci.yml; docs/UPSTREAM-INTAKE.md (fetch-only remote, labeling, path map, fabric-wins conflict policy); label upstream-intake; PR template .github/PULL_REQUEST_TEMPLATE/upstream-intake.md; two inherited workflows fixed to stop targeting the public repo; rehearsal cherry-pick of public 76434c235 opened as PR #2 | intake-rehearsal.md |
| WP-RN-03 scope + Artifact Registry provisioning | 393bf1f03, 984e39bde, 9ef6e7767 | AR npm repository fabric-npm (boot-464019/us-central1, standard, created 2026-09-28T15:06:04Z); repository-scoped IAM only (writer = github-deployer@boot-464019.iam.gserviceaccount.com, reader = user:tal@a5c.ai); root .npmrc scope routing line, zero credentials; .github/workflows/ar-smoke.yml (WIF auth, ping, dry-run-asserted publish, read-back); docs/user-guide/private-install.md | ar-provisioning.md |
| WP-RN-04 publish-isolation guards G1–G6 | f87f8d2e5, 23b4a0f46 | Single constant fabric.{privateScope,privateRegistry,legacyPublicScope} in root package.json (scripts/lib/registry-pin.cjs); scripts/ci/publish-isolation-guard.cjs (G1 manifest pinning, G2 CI rules, G3 credential starvation, G4 informational status probe); scripts/ci/bin-collision-guard.cjs inside test:binary-renames (G5); scope step in scripts/verify-release-artifacts.mjs + self-test in release-artifact-verifier.yml (G6); CI job publish-isolation; negative fixtures under scripts/ci/fixtures/publish-guards/{g1,g2,g3,g5,g6}; read-only secret audit; FABRIC_PRIVATE_TRAIN flag for the RN-1 end-state | publish-guards.md, g4-npmjs-org-runbook.md, g3-npm-token-restriction.md |
Scope re-lock (owner decision, breakpoint phase0.npmjs-org) | b4b3fa403, c9bfec2aa | @agentfabric found third-party-owned on npmjs; re-locked to @fabricstack (free: org/user 404, unscoped 404, search total 0); spec 15 §2 rewritten with a dated amendment; specs 10/20/30/40/50, docs, workflows, all guard fixtures and the smoke package swept; AR smoke re-run under the new scope (run 36454074285, @fabricstack/smoke@0.0.0-smoke.4) | scope-relock.md |
| WP-RN-08 no-double-rename gate | efbd9de3f, 80cd53a48 | scripts/ci/no-double-rename.cjs (9 rules, no waiver; name matrix parsed from spec 15 by scripts/lib/rename-spec.cjs; 59-entry baseline pinned to spec 15's current-name column); scripts/ci/check-spec10-names.cjs (spec 10 references only §3.3 names: 0 violations); 7 negative + 2 positive fixtures; wired into the publish-isolation job; naming rules in docs/UPSTREAM-INTAKE.md §10 and the intake PR template | no-double-rename.md |
| WP-T3-32 stage 1 Effect baseline | 5d16b830e, c56d71ee3 | Workspace-wide pin effect@4.0.0-rc.115 + @effect/vitest@4.0.0-rc.115 (root overrides + devDependencies); patch-package --error-on-fail on postinstall; ported patches/effect+4.0.0-rc.115.patch (5 files, 23 hunks, bodies byte-identical to upstream); MIT attribution in third_party/t3code-patches/; scripts/ci/effect-baseline.test.mjs (13 cases, in the CI test job); docs/EFFECT-BASELINE.md (pin owner, provenance, unstable-namespace register, rc→stable criteria, upgrade playbook); inventory of all 23 upstream patches | effect-baseline.md, t3code-patches-inventory.md |
M0 exit criteria (roadmap §5, Phase 0)
| # | Criterion | Status | Evidence (re-run at review) |
|---|---|---|---|
| 1 | Hard split proven strictly read-only: mirror transport confirmed disabled with API evidence; ≥7-day propagation watch shows no upstream commit entering fabric outside upstream-intake PRs; publish.yml no longer self-describes as a mirror (WP-RN-01 AC1/AC2/AC4) | **Met, watch window open** | gh api repos/a5c-ai/fabric → fork:false, mirror_url:null, parent:null; 0 hooks on both repos; ruleset 24103129 active; public pushed_at unchanged; bash scripts/ci/upstream-propagation-watch.sh → verdict: PASS (341 heads, 0 post-baseline upstream commits, 9 fabric refs, 0 unexplained); CI watch runs 36392536804, 36392593360, 36392751222, 36392886290 all success. Watch **started 2026-09-28T07:29:35Z**; the ≥7-day window (which must contain a post-cutover public commit) is not yet closed — see "Pending owner actions" |
| 2 | npm ping against fabric-npm succeeds from CI via WIF; @fabricstack/smoke publish+install rehearsal passes from the written doc alone (WP-RN-03 AC1–2) | **Met** | ar-smoke runs 36441575117 and 36454074285 success with every step green (WIF auth, ping, dry-run, publish, read-back); reviewer install in a fresh directory following only docs/user-guide/private-install.md: npm install @fabricstack/smoke → {"ok":true} @fabricstack/smoke 0.0.0-smoke.4, lockfile resolved on us-central1-npm.pkg.dev; local npm ping → PONG |
| 3 | Guards G1–G6 each demonstrated red-until-green on their negative fixtures; repo secret audit shows no npmjs credential (WP-RN-04) | **Met** | publish-isolation-guard --root on g1/negative/public-npmjs, g2/negative/npmjs-publish-private-scope, g3/negative/npmrc-token → exit 1 each; bin-collision-guard --root g5/negative/duplicate-bin → exit 1; verify-release-artifacts.mjs on g6/negative/private-scope-npmjs → scope: failed, exit 1, and on g6/positive/pinned → passed; --self-test → 14/14, tree clean (122 files, 0 findings); npm run test:publish-guards → 17/17; npm run test:binary-renames → 32 bin names, 0 errors; gh secret list -R a5c-ai/fabric → only KRADLE_TEST_AUTH_SECRET; .npmrc and its full history carry no _authToken/password/token= |
| 4 | The no-double-rename gate trips on a fixture @t3code/x manifest (WP-RN-08 AC2) | **Met** | no-double-rename.cjs --root scripts/ci/fixtures/no-double-rename/negative/t3code-scope → packages/x/package.json:2: [t3code-scope-name] @t3code/x is a t3code-scoped name; ..., exit 1; --self-test → 10/10; npm run test:no-double-rename → 16/16; check-spec10-names.cjs → 0 violations |
| 5 | npm ls effect resolves a single effect@4.0.0-rc.115 workspace-wide and the patch-regression test passes (WP-T3-32 AC1–2) | **Met** | npm ls effect --all → effect@4.0.0-rc.115 overridden (+ one deduped under @effect/vitest@4.0.0-rc.115); npm run test:effect-baseline → 13/13 (cases 5–8 are the patched-behaviour detectors, proven red with the patch reversed) |
WP acceptance criteria not fully closable inside this branch (accepted by design; listed, not hidden)
- **WP-RN-01 AC1(b)** — the ≥7-day passive propagation watch is only **started** (2026-09-28T07:29:35Z): cutover recorded, scheduled read-only watch installed, first runs green. The roadmap's wall-clock note keeps this off Phase 1's critical path; only the formal M0 gate record waits for the window (it must also contain at least one post-cutover public commit, and the public repo has not moved since 2026-09-16).
- **WP-RN-02 AC3 / test requirement "rehearsal PR merged"** — PR #2 (
intake/76434c2, labelupstream-intake, path map recorded) is **open, not merged**; merging is the owner's decision. - **WP-RN-04 G1 selector switch** (spec 15 §4.3: "must change in the same commit" as the manifests are pinned) — no manifest is renamed yet, so the switch is implemented behind
FABRIC_PRIVATE_TRAIN=1(default off, byte-identical to the old inventory: 43@a5c-ai/*entries, pinned by test) and flips in the RN-1 train (WP-RN-05/07). G2(b), G2(d), the G5 legacy-collision tolerance and the whole-tree npmjs rules follow the same flag. - **WP-RN-04 G4** — npmjs organization
fabricstackis **not yet created** (pending owner action; the scope is free as of 2026-09-28, probe--g4-status→unregistered). - **WP-RN-04 G3 at organization level** — org secret
NPM_TOKENstill has visibilityall(readable by this repo through two inherited legacy@a5c-aitrain workflows, reported by the guard as 8 legacy-train notices); restriction is a pending owner UI action. - **WP-RN-08 AC3** (first real landed fork passes the gate un-waived) — not yet exercisable; no fork has landed. Owned by WP-T3-01/02 (M2 exit criterion 1).
- **WP-T3-32 AC3–AC5** (stage 2: build matrix, Vite/vitest re-platform, engine floors) — out of scope by the roadmap's staging; deferred in
docs/EFFECT-BASELINE.md§7.
Pending owner actions (outside the repository)
1. **G4 — register the fabricstack org on npmjs** per g4-npmjs-org-runbook.md: company account, publish nothing, 2FA required, sole owner; then record the org URL in publish-guards.md (G4 row). Verify with node scripts/ci/publish-isolation-guard.cjs --g4-status → registered-no-packages. 2. **G3 — restrict org secret NPM_TOKEN** to selected repositories excluding a5c-ai/fabric (GitHub UI; the API path needs the secret value) per g3-npm-token-restriction.md. Verify: gh api orgs/a5c-ai/actions/secrets/NPM_TOKEN --jq .visibility → selected. 3. **Close the ≥7-day propagation watch** once the window has elapsed and contains a post-cutover public commit: record the last green run, the git for-each-ref after-snapshot and the ref-by-ref diff against fabric-refs-baseline.txt in watch-baseline.md ("Closing procedure"). Until the workflow is on main, the schedule does not fire; dispatch with gh workflow run upstream-propagation-watch.yml -R a5c-ai/fabric --ref <branch>. 4. **Decide PR #2** (merge or close the intake rehearsal). 5. Optional cleanup: delete the old-scope @agentfabric/smoke@0.0.0-smoke.2 from fabric-npm (gcloud artifacts packages delete @agentfabric/smoke --repository=fabric-npm --location=us-central1 --project=boot-464019 --quiet) and update the AR repository description (still "agentfabric private npm packages (fabric)").
Deferred items
- **G1 selector switch** to
publishConfig.registry-keyed selection: RN-1 train (WP-RN-05/07), behindFABRIC_PRIVATE_TRAIN. - **WP-T3-32 stage 2**: apps re-platform on stock Vite/vitest/tsc (
@voidzero-dev/*grep gate), workspaceengines≥22.16 with the sub-floor CI run, tracked-fork build matrix; depends on WP-T3-01. Stage-2 input recorded:@effect/vitest@4.0.0-rc.115peers onvitest >=5 <6while fabric runs 4.1.6, so it is pinned and locked but not yet usable as a harness. - **
@effect/vitestupstream patch dropped (DROPPED-toolchain)**, not ported: its only effect isvitest→vite-plus/testimport rewriting, which is wrong under stock vitest; test 10 pins the unpatched imports. The WP objective text says "patched@effect/vitest"; the ACs (1), (2), (6) do not require it and the decision is recorded indocs/EFFECT-BASELINE.md§3. - **Patch inventory count**: 17 mobile-only (not the 19 the WP brief assumed) because
@legendapp/listand@pierre/diffsare also consumed byt3:apps/web; those two plus@ff-labs/fff-nodeanddbus-nextare DEFERRED-stage-2 to the WPs that land their consumers. - **Retargeting the 43
@a5c-aimanifests and the release workflows** (WP-RN-05/06/07); the two legacy workflows referencingsecrets.NPM_TOKENbecome hard findings under--private-train.
Review observations (not failures)
git grep -l "@agentfabric/"lists four files: the two historical records (g4-npmjs-org-runbook.md,scope-relock.md) and two WP-RN-08 negative fixtures (scripts/ci/fixtures/no-double-rename/negative/rejected-scope/packages/x/package.json,scripts/ci/fixtures/no-double-rename/spec10/negative.md) that deliberately use the rejected scope to prove theforeign-scope-new-unit/rejected-scope-namerules trip.scope-relock.md's "only two files" statement predates those fixtures.ci.ymltriggers onpull_requestandworkflow_dispatchonly, so thepublish-isolationjob (WP-RN-04/08) and thetestjob's "Effect baseline" step have not yet executed in GitHub Actions; this PR's CI run is their first. The oneci.ymlrun tied to this branch (PR #2, run 36395354707) ran thepublic-push-guardjob green; its four red jobs (Gateway Node engine floor×2 at "Build adapters gateway",Lint, Tests, Packageat "Release tooling tests",Docs QAat "Docs quality gates") fail on exactly the same steps in the pre-Phase-0 run 36332996215 (c0dd9722c, PR #1) and are unrelated to this branch.npx markdownlint-cli2 docs/specs/platform-gaps/phase0/watch-baseline.mdreports MD010 hard tabs at lines 385–386 (inside the verbatimgit ls-remotedump). The repo's markdownlint scope (.markdownlint-cli2.mjsglobs) does not include the phase0 records; rootnpx markdownlint-cli2→ 0 errors.
How to verify
Run from the repository root on this branch (node_modules installed with npm install --legacy-peer-deps).
# Guards: self-tests (fixtures red, tree green)
node scripts/ci/public-push-guard.cjs --self-test
node scripts/ci/publish-isolation-guard.cjs --self-test
node scripts/ci/no-double-rename.cjs --self-test
node scripts/ci/check-spec10-names.cjs --self-test
# Guards: node:test suites
npm run test:public-push-guard
npm run test:publish-guards
npm run test:binary-renames
npm run test:no-double-rename
npm run test:effect-baseline
# Negative fixtures trip (each exits 1)
node scripts/ci/public-push-guard.cjs scripts/ci/fixtures/public-push-guard/negative/git-push-https.workflow.yml
node scripts/ci/publish-isolation-guard.cjs --root scripts/ci/fixtures/publish-guards/g1/negative/public-npmjs
node scripts/ci/publish-isolation-guard.cjs --root scripts/ci/fixtures/publish-guards/g2/negative/npmjs-publish-private-scope
node scripts/ci/publish-isolation-guard.cjs --root scripts/ci/fixtures/publish-guards/g3/negative/npmrc-token
node scripts/ci/bin-collision-guard.cjs --root scripts/ci/fixtures/publish-guards/g5/negative/duplicate-bin
node scripts/verify-release-artifacts.mjs --package-dir scripts/ci/fixtures/publish-guards/g6/negative/private-scope-npmjs --skip-build --report-dir "$TMPDIR/g6"
node scripts/ci/no-double-rename.cjs --root scripts/ci/fixtures/no-double-rename/negative/t3code-scope
# Effect singleton
npm ls effect --all
# Scope constant, routing, credentials
node -e "console.log(require('./package.json').fabric)"
grep "@fabricstack:registry=" .npmrc
grep -iE "_authToken|password|token=" .npmrc # must print nothing (exit 1)
git log --all -p -- .npmrc | grep -iE "_authToken|password|token=" # must print nothing
# Artifact Registry (needs gcloud auth as a reader of fabric-npm)
npx -y google-artifactregistry-auth --repo-config=.npmrc --credential-config="$HOME/.npmrc"
npm ping --registry https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/
npm view @fabricstack/smoke versions --registry https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/
node scripts/ci/publish-isolation-guard.cjs --g4-status # informational: unregistered until G4 is done
# Hard split invariants (read-only)
git remote -v # upstream ... no_push (push)
bash scripts/ci/upstream-propagation-watch.sh # verdict: PASS
gh api repos/a5c-ai/babysitter --jq .pushed_at # 2026-09-16T19:59:50Z
gh api repos/a5c-ai/fabric/rulesets # id 24103129 active
gh pr view 2 -R a5c-ai/fabric --json state,labels
gh run list -R a5c-ai/fabric --workflow ar-smoke.yml --branch phase0/hard-split-registry-guards --limit 3
gh run list -R a5c-ai/fabric --workflow upstream-propagation-watch.yml --branch phase0/hard-split-registry-guards --limit 3🤖 Generated with Claude Code