Merge remote-tracking branch 'origin/main' into docs-rehearsal-dev-uk-bootstrap
16 files changed,
+355
−0
docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-2.md+25 −0docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-3.md+33 −0docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-4.md+35 −0docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-5.md+31 −0docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-6.md+27 −0docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-2.md+20 −0docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-3.md+30 −0docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-4.md+25 −0docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-5.md+20 −0docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-6.md+18 −0docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF.md+22 −0docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP.md+27 −0internal/hostfiles/fix5_linux_test.go+26 −0internal/hostfiles/publisher.go+8 −0internal/provision/plan14_root_reads_test.go+6 −0internal/provision/testdata/plan14-root-reads-hostfiles-cap-236.tsv+2 −0
CI passed — run details
CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok amadan.net/carlos/platform.git/launchctx 0.011s
? amadan.net/carlos/platform.git/launchctx/controlname [no test files]
? amadan.net/carlos/platform.git/launchctx/wireid [no test files]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok github.com/carlosframework/platform 281.879s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 2.903s
ok github.com/carlosframework/platform/cmd/moveprobe 93.326s
ok github.com/carlosframework/platform/cmd/zddprobe 0.704s
ok github.com/carlosframework/platform/internal/activator 249.018s
ok github.com/carlosframework/platform/internal/agentsock 0.014s
ok github.com/carlosframework/platform/internal/alerting 0.003s
ok github.com/carlosframework/platform/internal/analytics 0.018s
ok github.com/carlosframework/platform/internal/appconfig 2.482s
ok github.com/carlosframework/platform/internal/apperr 0.070s
ok github.com/carlosframework/platform/internal/applog 0.119s
ok github.com/carlosframework/platform/internal/awsinv 0.003s
ok github.com/carlosframework/platform/internal/boxchoice 0.004s
ok github.com/carlosframework/platform/internal/boxid 0.853s
? github.com/carlosframework/platform/internal/boxid/boxidtest [no test files]
? github.com/carlosframework/platform/internal/boxid/internal/signtest [no test files]
ok github.com/carlosframework/platform/internal/boxvolumes 0.008s
ok github.com/carlosframework/platform/internal/caps 0.011s
ok github.com/carlosframework/platform/internal/certwarm 0.002s
ok github.com/carlosframework/platform/internal/cgroup 0.706s
ok github.com/carlosframework/platform/internal/changelog 0.300s
ok github.com/carlosframework/platform/internal/clamd 0.054s
? github.com/carlosframework/platform/internal/cliflag [no test files]
ok github.com/carlosframework/platform/internal/console 386.713s
? github.com/carlosframework/platform/internal/controlname [no test files]
? github.com/carlosframework/platform/internal/controlsock [no test files]
ok github.com/carlosframework/platform/internal/dbdoor 6.030s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.009s
ok github.com/carlosframework/platform/internal/dbverify 0.005s
ok github.com/carlosframework/platform/internal/dnsrec 0.002s
ok github.com/carlosframework/platform/internal/docsite 0.005s
ok github.com/carlosframework/platform/internal/domains 2.396s
ok github.com/carlosframework/platform/internal/doorkey 0.006s
ok github.com/carlosframework/platform/internal/economics 0.004s
ok github.com/carlosframework/platform/internal/edge 26.503s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.008s
ok github.com/carlosframework/platform/internal/fleetapi 0.129s
ok github.com/carlosframework/platform/internal/gate 1.657s
ok github.com/carlosframework/platform/internal/genselect 0.014s
ok github.com/carlosframework/platform/internal/geo 0.016s
ok github.com/carlosframework/platform/internal/geoip 0.029s
ok github.com/carlosframework/platform/internal/host 134.713s
ok github.com/carlosframework/platform/internal/hostactors 0.374s
ok github.com/carlosframework/platform/internal/hostbox 2.284s
ok github.com/carlosframework/platform/internal/hostclaim 0.484s
ok github.com/carlosframework/platform/internal/hostevidence 0.008s
ok github.com/carlosframework/platform/internal/hostexec 4.880s
ok github.com/carlosframework/platform/internal/hostfence 0.031s
ok github.com/carlosframework/platform/internal/hostfiles 10.578s
ok github.com/carlosframework/platform/internal/hostfloor 0.012s
ok github.com/carlosframework/platform/internal/hostguard 1.001s
ok github.com/carlosframework/platform/internal/hostops 1.562s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.008s
ok github.com/carlosframework/platform/internal/hostquarantine 0.008s
ok github.com/carlosframework/platform/internal/hostrelocate 0.452s
ok github.com/carlosframework/platform/internal/hostresolve 0.132s
ok github.com/carlosframework/platform/internal/hostwrite 0.008s
ok github.com/carlosframework/platform/internal/infraview 0.009s
ok github.com/carlosframework/platform/internal/instancectl 4.182s
ok github.com/carlosframework/platform/internal/instanceexec 2.732s
ok github.com/carlosframework/platform/internal/instancefs 0.008s
ok github.com/carlosframework/platform/internal/instancekey 0.012s
ok github.com/carlosframework/platform/internal/instanceupdates 0.016s
ok github.com/carlosframework/platform/internal/layout 1.485s
ok github.com/carlosframework/platform/internal/lease 9.778s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.005s
ok github.com/carlosframework/platform/internal/ledger 0.023s
ok github.com/carlosframework/platform/internal/mail 0.162s
ok github.com/carlosframework/platform/internal/maintenance 0.086s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.510s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.066s
ok github.com/carlosframework/platform/internal/metrics 0.005s
ok github.com/carlosframework/platform/internal/moves 20.947s
ok github.com/carlosframework/platform/internal/moves/archive 0.798s
ok github.com/carlosframework/platform/internal/objstore 0.185s
ok github.com/carlosframework/platform/internal/ops 0.176s
ok github.com/carlosframework/platform/internal/park 0.191s
ok github.com/carlosframework/platform/internal/privatefile 0.006s
ok github.com/carlosframework/platform/internal/provision 16.705s
ok github.com/carlosframework/platform/internal/publicrelease 0.007s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 4.865s
ok github.com/carlosframework/platform/internal/relay 7.253s
ok github.com/carlosframework/platform/internal/release 19.680s
ok github.com/carlosframework/platform/internal/rollout 3.741s
ok github.com/carlosframework/platform/internal/rolloutwords 0.003s
ok github.com/carlosframework/platform/internal/rootaws 0.006s
ok github.com/carlosframework/platform/internal/rootparticipation 0.007s
ok github.com/carlosframework/platform/internal/rootstate 0.025s
ok github.com/carlosframework/platform/internal/runs 0.568s
ok github.com/carlosframework/platform/internal/s3compat 0.005s
ok github.com/carlosframework/platform/internal/safefile 0.004s
ok github.com/carlosframework/platform/internal/schedule 2.490s
ok github.com/carlosframework/platform/internal/seeds 0.010s
ok github.com/carlosframework/platform/internal/sim/hostlife 9.509s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 4.323s
ok github.com/carlosframework/platform/internal/status 0.037s
ok github.com/carlosframework/platform/internal/steering 0.006s
ok github.com/carlosframework/platform/internal/stopevidence 0.225s
ok github.com/carlosframework/platform/internal/svcrole 0.002s
ok github.com/carlosframework/platform/internal/telemetry 0.004s
ok github.com/carlosframework/platform/internal/termfence 0.004s
ok github.com/carlosframework/platform/internal/tunnelclient 0.004s
ok github.com/carlosframework/platform/internal/unixdial 0.002s
ok github.com/carlosframework/platform/internal/updatewords 0.004s
ok github.com/carlosframework/platform/internal/usage 0.004s
ok github.com/carlosframework/platform/internal/version 0.001s
ok github.com/carlosframework/platform/internal/vet 1.554s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
linux/arm64 carlos binary: 46399650 bytes (ceiling 52000000)
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
minio: downloading https://github.com/minio/minio/releases/download/RELEASE.2025-09-07T16-13-09Z/minio.linux-arm64.RELEASE.2025-09-07T16-13-09Z
minio: downloaded, digest verified
mc: downloading https://github.com/minio/mc/releases/download/RELEASE.2025-08-13T08-35-41Z/mc.linux-arm64.RELEASE.2025-08-13T08-35-41Z
mc: downloaded, digest verified
minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
buckets and scoped user ready
=== RUN TestDeliveredContractMinio
=== RUN TestDeliveredContractMinio/put
=== RUN TestDeliveredContractMinio/get_round-trips_the_body
=== RUN TestDeliveredContractMinio/list_shows_the_key
=== RUN TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN TestDeliveredContractMinio/delete_removes_it
=== RUN TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
--- PASS: TestDeliveredContractMinio/put (0.00s)
--- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
--- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
--- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
--- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok github.com/carlosframework/platform/internal/objstore 0.022s
=== RUN TestT4StorageProbeMinio
2026/10/15 12:09:46 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DEB2B395DD3CB4, HostID: 7c420053e0fbcd7f41653fdd92ffa2802c903d05303b1aa15adc2e8bb65d183f, api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
--- PASS: TestT4StorageProbeMinio (0.00s)
PASS
ok github.com/carlosframework/platform/internal/host 0.019s
minio contract: PASS
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-2.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-2.md| new file mode 100644 |
| index 000000000..913aa76c6 |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-2.md |
| @@ -0,0 +1,25 @@ |
| +--- |
| + |
| +**FIRST PROMPT:** |
| +Review `hostfiles-cap-live-entries` at `4411ffff` against the round-2 brief, read-only. |
| + |
| +**WHAT:** |
| +Check both previous findings, downstream recovery behavior, and readiness to merge. |
| + |
| +**STATUS:** |
| +**Request changes: one P2 remains.** |
| + |
| +**P2 — Skipping deep tombstones can strand the subsequent flush** at [restore.go:110](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/restore.go:110). When the tombstoned file still exists at the destination, Restore now reports success without removing it. The new regression fixture demonstrates the exact boundary: its parent directory is 4095 bytes, but the remaining file is 4097 bytes. A subsequent scan returns `ErrTooDeep`; [ReleaseFlushFiles.Flush:137](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/host/relocationflush.go:137) therefore never commits. Relocation’s `EndProbe` requires that flush before stopping the probe, so retries cannot progress and the deadline ultimately leaves the operation stuck. `Establish` itself succeeds because it copies the manifest without scanning disk. |
| + |
| +The first round-1 P2 is closed. The second fix removes the unbounded tombstone traversal, but leaves this downstream failure. Cap-free `StageDelete` introduces no reader incompatibility: it only replaces an existing entry with an accepted tombstone. A scan encountering a live over-cap path fails before its deletion phase, so it cannot wrongly retire that file. |
| + |
| +No other findings. The census envelope hash matches; the legacy seal is unchanged. This was static review; no tests were run, including the full `internal/host` suite. |
| + |
| +**NEEDED:** |
| +Nothing from you for this review. |
| + |
| +**RECOMMENDED:** |
| +Resolve existing deep tombstones without unbounded traversal, and test restore → scan → commit plus the relocation flush. Not ready to merge after the freeze until that path is addressed. |
| + |
| +**SESSION:** |
| +Safe to close. HEAD and working tree are unchanged; no files or background processes were created. |
| \ No newline at end of file |
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-3.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-3.md| new file mode 100644 |
| index 000000000..795bb3e8c |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-3.md |
| @@ -0,0 +1,33 @@ |
| +**Request changes: two P2 findings. Round 2’s P2 is only partially closed; this HEAD is not ready to merge.** |
| + |
| +1. **P2 — Over-cap parent directories still strand the flush.** At [restore.go:153](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/restore.go:153), removal deletes only the tombstoned file and leaves every directory standing. With **2049** one-character directories, the parent path is 4097 bytes. Restore succeeds, but [walk_unix.go:63](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/walk_unix.go:63) rejects that now-empty directory with `ErrTooDeep`, so relocation’s flush still cannot commit. Both new regression fixtures use 2048 directories, keeping the parent exactly within the cap. Add the one-level-deeper case and resolve its restore → scan → flush behavior. |
| + |
| +2. **P2 — An ancestor rename can redirect deletion outside the tombstone’s path.** At [restore_unix.go:72](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/restore_unix.go:72), `unlinkat` uses the parent descriptor obtained during descent without checking its continuing relationship to the restore root. After the first directory opens, a concurrent actor can rename that subtree elsewhere; subsequent opens and deletion continue inside the moved subtree. This can delete at an unlisted path, including outside the restore root when the actor can move the subtree there. `O_NOFOLLOW` prevents following a replacement symlink but does not prevent this case. Add a deterministic ancestor-rename regression and establish the required confinement or enforced exclusion of concurrent renames. |
| + |
| +The other requested checks: |
| + |
| +- **Durability:** syncing the immediate parent is sufficient for this unlink when ancestor entries remain unchanged. Syncing even after `ENOENT` correctly covers retry after an earlier unlink whose sync failed. |
| +- **Five ruling conditions:** the implementation has descriptor-relative `O_NOFOLLOW|O_DIRECTORY` descent, plain-component validation, final-name-only `unlinkat`, cancellation checks between components, and no path-based fallback. Expected absence/type errors become no-ops; other errors propagate. Those mechanical conditions do **not** establish confinement against ancestor renames. |
| +- **Symlinks and directories:** symlinks are not followed, including at the final name. `unlinkat` with flags `0` cannot remove a directory. |
| + |
| +Static review only; no tests ran, including the full `internal/host` suite. HEAD remains `5065b52ae663207dd32306fbe090ad23046653d5`; the working tree is unchanged. |
| + |
| +--- |
| + |
| +**FIRST PROMPT:** |
| +Review the round-3 brief at HEAD 5065b52, read-only; avoid the full internal/host suite. |
| + |
| +**WHAT:** |
| +Assess the deep-tombstone fix, durability, ruling conditions, races, and merge readiness. |
| + |
| +**STATUS:** |
| +Review complete: two P2 findings; request changes. |
| + |
| +**NEEDED:** |
| +Nothing from you for this review. |
| + |
| +**RECOMMENDED:** |
| +Address both findings and cover the deeper-parent and ancestor-rename cases. |
| + |
| +**SESSION:** |
| +Safe to close. No files or background processes were created. |
| \ No newline at end of file |
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-4.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-4.md| new file mode 100644 |
| index 000000000..1b9d1fece |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-4.md |
| @@ -0,0 +1,35 @@ |
| +**Request changes: two P2 findings. HEAD `c6dc57d0` is not ready to merge.** |
| + |
| +1. **P2 — Exempt skipped paths still cause unbounded ancestor processing.** The exemption at [publisher.go:74](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/publisher.go:74) admits arbitrarily deep skipped paths. Both Restore and Present call [superseded():226](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/restore.go:226), which walks every ancestor, including those of skipped entries. Each `path.Dir` cleans the remaining prefix, making this quadratic in depth, with no cancellation check. A valid skipped entry at `strings.Repeat("d/", 100000)+"f"` therefore causes substantial uncancellable work even on an empty destination. Bound or redesign overlap processing while preserving skipped entries’ sequence semantics. |
| + |
| +2. **P2 — A skipped flag bypasses the cap on a tombstone.** At [publisher.go:74](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/publisher.go:74), an over-cap entry containing both `Deleted: true` and `Skipped: "too-large"` passes validation. Neither decoding nor `checkEntry` rejects that combination. Restore gives `Deleted` precedence at [restore.go:109](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/restore.go:109), then performs the ancestor marking and removal that the revised design intends to forbid. This requires a malformed or crafted manifest; normal writers do not produce it. Exempt only `!e.Deleted && e.Skipped != ""`, or reject contradictory states consistently. |
| + |
| +The remaining requested checks: |
| + |
| +- Round 3’s findings against `removePastCap` are moot: that implementation is gone. |
| +- For ordinary skipped entries, the StageDelete no-op is safe: it preserves an unpublished entry and cannot resurrect content. Restore → scan → commit succeeds when the deep file and over-cap directories are absent. |
| +- An existing deep file or directory still makes scanning—and therefore flushing—fail. That is the existing scan-cap limitation described in the brief as #279-class. |
| +- CopyManifest, NewFromCopy, and moves’ Establish consistently carry ordinary skipped entries through the shared validation. |
| +- The census overlay is envelope-only; its hash matches exactly. Plan 9 and Plan 14 legacy units and the legacy seal are unchanged. |
| + |
| +Static review only; no tests ran, including the full `internal/host` suite. |
| + |
| +--- |
| + |
| +**FIRST PROMPT:** |
| +Review the round-4 brief at `c6dc57d0`, read-only; avoid the full internal/host suite. |
| + |
| +**WHAT:** |
| +Assess the revised cap, downstream recovery behavior, census, and merge readiness. |
| + |
| +**STATUS:** |
| +Complete: two P2 findings; request changes. |
| + |
| +**NEEDED:** |
| +Nothing from you for this review. |
| + |
| +**RECOMMENDED:** |
| +Address both findings and add focused regressions before merging. |
| + |
| +**SESSION:** |
| +Safe to close. HEAD and working tree are unchanged; no files or background processes were created. |
| \ No newline at end of file |
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-5.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-5.md| new file mode 100644 |
| index 000000000..6461aa27f |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-5.md |
| @@ -0,0 +1,31 @@ |
| +**Request changes: two P2 findings. HEAD `c29cc49f` is not ready to merge.** |
| + |
| +1. **P2 — Ignoring deep skipped entries can restore an obsolete ancestor file.** At [restore.go:226](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/restore.go:226), the new guard drops over-cap entries from overlap resolution entirely. Consider a valid live entry `"a"` at sequence 1 and a skipped entry `"a/"+strings.Repeat("d/", 2048)+"f"` at sequence 2. Previously, the newer descendant superseded `"a"`; now Restore writes the obsolete file. If `"a"` is an existing directory, `writeInside` removes its contents first. Present also starts requiring that obsolete file. Preserve skipped descendants’ precedence over in-cap ancestors without walking their entire depth. |
| + |
| +2. **P2 — Scanning still walks an exempt skipped path’s ancestors without a bound.** At [sync.go:165](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/sync.go:165), inherited skipped paths reach `skipped()` before StageDelete’s cap guard. With a non-nil callback returning false, [sync.go:50](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/sync.go:50) repeatedly calls `path.Dir`, retaining the quadratic, uncancellable processing identified in round 4. This happens even on an empty restored tree; no deep directories need exist. Skip over-cap inherited entries before exclusion processing. Add a scan regression with a non-nil callback—the existing regression passes `nil`. |
| + |
| +Round 4’s deleted-plus-skipped bypass is closed. Its `superseded()` slowdown is removed, but finding 1 prevents treating that fix as complete. |
| + |
| +Other checks: validation and StageDelete still perform linear `CleanPath` processing; I found no additional ancestor walks in moves or relocation’s manifest-copy paths. The census envelope hash matches exactly; legacy units and seals remain unchanged. |
| + |
| +Static review only. No tests or builds ran, including the full `internal/host` suite. |
| + |
| +--- |
| + |
| +**FIRST PROMPT:** |
| +Review brief 5 at `c29cc49f`, read-only; avoid the full internal/host suite. |
| + |
| +**WHAT:** |
| +Check the round-4 fixes, remaining path processing, and merge readiness. |
| + |
| +**STATUS:** |
| +Review complete: two P2 findings; request changes. |
| + |
| +**NEEDED:** |
| +Nothing from you for this review. |
| + |
| +**RECOMMENDED:** |
| +Fix both findings and add focused regressions before merging. |
| + |
| +**SESSION:** |
| +Safe to close. HEAD and working tree are unchanged; no files or background processes were created. |
| \ No newline at end of file |
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-6.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-6.md| new file mode 100644 |
| index 000000000..edb1a9b36 |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-6.md |
| @@ -0,0 +1,27 @@ |
| +**No P1/P2/P3 findings. Ready to merge from this review, subject to required CI passing on the final tip.** |
| + |
| +1. **Diff scope:** Against `f97898f1`, the code changes are exactly the exported helper, its test, and the Plan 14 overlay. There are also eleven review briefs/reports. Existing readers, writers, Restore, and scanning behavior are unchanged. |
| +2. **Cap equivalence:** `RestorableWithinCap` delegates directly to `manifestWithinCap`. Both it and `checkManifest` apply `withinTreeCap` to every `m.Entries` key, regardless of entry state. Passing the helper prevents a cap rejection by `checkManifest`; other validation can still fail. |
| +3. **Census:** The overlay changes only the `internal/hostfiles` source envelope. I independently recomputed its hash and the legacy seal; both match. No existing root-read units changed. |
| + |
| +Static review at `4c8665519f9e9ecc725d76e0e61b91245a43626e`. No builds or tests ran, including the full `internal/host` suite. `git diff --check` passed. |
| + |
| +--- |
| + |
| +**FIRST PROMPT:** |
| +Review brief 6 at HEAD `4c866551`, read-only, without the full internal/host suite. |
| + |
| +**WHAT:** |
| +Assess the minimal cap helper, census overlay, and merge readiness. |
| + |
| +**STATUS:** |
| +Review complete; no findings. |
| + |
| +**NEEDED:** |
| +Nothing from you for this review. |
| + |
| +**RECOMMENDED:** |
| +Land through amadan once required CI passes on the final tip. |
| + |
| +**SESSION:** |
| +Safe to close. HEAD and worktree unchanged; no files or background processes created. |
| \ No newline at end of file |
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-2.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-2.md| new file mode 100644 |
| index 000000000..dad69d37e |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-2.md |
| @@ -0,0 +1,20 @@ |
| +# Astra: hostfiles path cap, round 2 |
| + |
| +Round 1 (`CODEX-REVIEW-HOSTFILES-CAP.md`) raised two P2s. Both are fixed in 826a7a9f; read its commit message. |
| + |
| +1. **StageDelete takes no cap.** It only tombstones an entry the manifest already holds, so an inherited over-cap skipped entry whose file is gone is retired by the next scan. |
| +2. **Restore's partition drops a tombstone past the cap.** Restore never walks it. Anything standing at that path is left for the next walk, which refuses it with ErrTooDeep. |
| + |
| +Tests: |
| +- `TestAScanRetiresAnInheritedSkippedEntryPastTheCap` covers restore, then scan, then commit. |
| +- `TestRestoreNeverWalksATombstonePastTheCap` builds a real 2049-level tree at the destination and checks it is left untouched. With the walk restored, it is still running at go test's 10-minute timeout. |
| +- `TestRunStopsOnAManifestItCannotCommit` now skips its deep path, so it still reaches run's stop branch. |
| + |
| +**Judge:** |
| +1. Are both round-1 P2s closed? |
| +2. Can StageDelete without a cap now admit anything a reader refuses, or retire something it shouldn't, including a live over-cap entry? |
| +3. Is leaving an over-cap tombstone's file on disk safe? It means the next walk refuses the tree. Is that a recovery-stranding path again, through moves' `Establish`, relocation's flush or the release flush? |
| +4. Anything new? |
| +5. **Verdict:** is it ready to merge, after the release freeze? |
| + |
| +Give findings P1/P2/P3 with file:line. Read-only; run nothing heavy. |
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-3.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-3.md| new file mode 100644 |
| index 000000000..ada71370b |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-3.md |
| @@ -0,0 +1,30 @@ |
| +# Astra: hostfiles path cap, round 3 |
| + |
| +Round 2 (`CODEX-REVIEW-HOSTFILES-CAP-2.md`) raised one P2. When Restore skipped an over-cap tombstone, it left the deep file on disk, so the next scan refused the tree and relocation's flush stuck. |
| + |
| +Fixed in 3ab48ff7 and cc1c3d24, then merged with main in b3fa7427: |
| +- `removePastCap` (`internal/hostfiles/restore_unix.go`) removes an over-cap tombstone's file in one linear walk: |
| + - `root.Open(".")` to get a descriptor; |
| + - `descendCtx`, which is `dir_unix.go`'s `descend` with a ctx check per component, using openat O_NOFOLLOW|O_DIRECTORY per component; |
| + - a plain-final-name check, then unlinkat; |
| + - an fsync of the parent through the held fd. |
| +- Deep tombstones are never `mark()`ed. Under-cap tombstones still use `removeInside`. |
| +- `restore_other.go` is the `!unix` stub, and it errors. |
| +- platform-1e's census ruling hostfiles-cap-236 is folded in. Its conditions are in the overlay `internal/provision/testdata/plan14-root-reads-hostfiles-cap-236.tsv`. |
| + |
| +Tests (`fix5_linux_test.go`, `internal/host/relocationflush_deeptombstone_linux_test.go`): |
| +- A 2048-level removal takes about 25ms. |
| +- Cancellation stops mid-walk. |
| +- A symlink is never followed, at depths 0, 1024 and 2047. |
| +- A non-plain final name refuses. |
| +- Restore, then `ReleaseFlushFiles.Flush`, commits. |
| + |
| +**Judge:** |
| +1. Is round 2's P2 closed? |
| +2. Durability: is an fsync of only the immediate parent enough, given no ancestor's entries change? |
| +3. Do the ruling's five conditions hold in the code? |
| +4. Races: can a concurrent rename or symlink swap during the walk make it remove anything outside the tombstone's own path, or anything that isn't a non-directory? |
| +5. Anything new? |
| +6. **Verdict:** is it ready to merge? |
| + |
| +Give findings P1/P2/P3 with file:line. Read-only; run nothing heavy. |
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-4.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-4.md| new file mode 100644 |
| index 000000000..e2121c42e |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-4.md |
| @@ -0,0 +1,25 @@ |
| +# Astra: hostfiles path cap, round 4 (revised design) |
| + |
| +Round 3 (`CODEX-REVIEW-HOSTFILES-CAP-3.md`) found two P2s in the deep-tombstone removal walk: over-cap parent directories left behind, and an ancestor-rename redirect. Paul ruled to withdraw the walk and refuse over-cap tombstones up front. Commit 54196e88, then merged with main in 3fb0e576. |
| + |
| +The rule is `hostfiles.RestorableWithinCap`, which is `manifestWithinCap` under the name Open, OpenAt and Commit use: |
| +- **Refused:** live entries and tombstones past 4095 bytes are ErrTooDeep. The readers (checkManifest: Restore, CopyManifest, ValidateManifest), Open, OpenAt and Commit all apply this. |
| +- **Exempt:** skipped entries, because Restore never touches their path. |
| +- **StageDelete:** for a path past the cap it is a no-op, so an inherited over-cap skipped entry whose file is gone stays skipped. |
| +- **The walk is removed.** removePastCap, its stub, descendCtx and their tests are gone, and Restore's tombstone loop is main's again. |
| + |
| +Upstream guards, per #236: moves' cut preflight will call RestorableWithinCap, relocation's pin already calls ValidateManifest, and #250's backfill reports failing manifests per host before activation. |
| + |
| +**Judge:** |
| +1. Are round 3's P2s moot now that the walk is gone? |
| +2. Does exempting skipped entries alone leave any path that strands a recovery or a flush? Consider: |
| + - superseded(); |
| + - Present; |
| + - the scan, with an over-cap skipped entry whose file still exists on disk (the walk refuses it: is that only an existing #279-class issue, or new?); |
| + - CopyManifest and NewFromCopy; |
| + - moves' Establish. |
| +3. Is a StageDelete no-op past the cap safe? Could it hide a real deletion that a reader then misrestores? |
| +4. Census: is the overlay envelope-only, with Plan 9 and Plan 14 legacy units unchanged? |
| +5. **Verdict:** is it ready to merge? |
| + |
| +Give findings P1/P2/P3 with file:line. Read-only; run nothing heavy. |
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-5.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-5.md| new file mode 100644 |
| index 000000000..3c552027a |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-5.md |
| @@ -0,0 +1,20 @@ |
| +# Astra: hostfiles path cap, round 5 (confirming) |
| + |
| +Round 4 (`CODEX-REVIEW-HOSTFILES-CAP-4.md`) raised two P2s. Both are fixed; see the commit after c6dc57d0. |
| + |
| +1. **Deleted and skipped together.** An entry marked both no longer gets the exemption: only `Skipped != "" && !Deleted` is exempt. |
| +2. **Ancestor walk in `superseded()`.** `superseded()` now skips a path past the cap. Only a skipped entry can be one, and any live entry beneath it is refused. |
| + |
| +Tests: |
| +- `TestATombstoneMarkedSkippedPastTheCapIsRefused`. |
| +- `TestAVeryDeepSkippedEntryCostsRestoreNoAncestorWalk`: a 200,000-byte skipped path. Restore and Present complete at once; without the skip, Restore takes 15s. |
| + |
| +The census envelope is updated, with no unit or seal change. Main hasn't moved since f97898f1. |
| + |
| +**Judge:** |
| +1. Are both round-4 P2s closed? |
| +2. Is any other code still walking or cleaning an exempt skipped path's ancestors, or doing per-component work on it? Look across hostfiles and its callers in moves and relocation. |
| +3. Anything new? |
| +4. **Verdict:** is it ready to merge? |
| + |
| +Give findings P1/P2/P3 with file:line. Read-only; run nothing heavy. |
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-6.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-6.md| new file mode 100644 |
| index 000000000..c9a5843c7 |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF-6.md |
| @@ -0,0 +1,18 @@ |
| +# Astra: hostfiles path cap, round 6 (minimal design) |
| + |
| +After round 5 (`CODEX-REVIEW-HOSTFILES-CAP-5.md`), Paul ruled that no over-cap entry is exempt. Every attempt to tolerate one in the readers opened a new edge. |
| + |
| +The branch now differs from main (f97898f1) only by: |
| +- the exported `hostfiles.RestorableWithinCap(m) error`, which is `manifestWithinCap`: every entry, live, tombstoned or skipped, within the cap; |
| +- its test; |
| +- an envelope-only Plan 14 overlay. |
| + |
| +Readers, writers, Restore and the scan are main's, byte for byte. |
| + |
| +**Judge:** |
| +1. Does the diff against main contain anything beyond that? |
| +2. Is RestorableWithinCap exactly the rule every reader applies, so that a manifest passing it can't be refused by checkManifest for the cap? |
| +3. Is the census overlay correct (envelope-only, legacy seal unchanged)? |
| +4. **Verdict:** is it ready to merge? |
| + |
| +Give findings P1/P2/P3 with file:line. Read-only; run nothing heavy. |
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF.md| new file mode 100644 |
| index 000000000..3ed3ce22e |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP-BRIEF.md |
| @@ -0,0 +1,22 @@ |
| +# Astra: hostfiles path cap, live entries only |
| + |
| +Branch `hostfiles-cap-live-entries`, commit 28d086d8, from main 132cf996. Read the commit message first. |
| + |
| +**Background.** Moves' Astra review of a8cebad, posted on amadan #236, found a P2. `checkManifest` capped every entry path at 4095 bytes, tombstones and skipped entries included. A manifest written before the cap could therefore stop `Establish` with ErrTooDeep and strand a moves recovery after a cut. |
| + |
| +**The change:** |
| +- The cap now applies only to live entries. The rule is `LiveEntriesWithinCap`, exported; internally it is `manifestWithinCap`. |
| +- Readers (`checkManifest`), Open, OpenAt and Commit all apply it. |
| +- New writes still refuse over-cap paths: Stage, StageSkipped, StageDelete and the walk. |
| +- A census overlay records the Plan 14 envelope change. |
| + |
| +**Judge:** |
| +1. Is exempting tombstones and skipped entries safe in every reader and writer? |
| + - Restore's tombstone removal, `removeInside` with `realParents` and `os.Root` `Lstat`/`Remove`, now receives over-cap paths. Can that fail with ENAMETOOLONG, or cost unboundedly? Would the failure be any worse than the old ErrTooDeep refusal? |
| + - Check `superseded`, `Present`/`presentFile`, `CopyManifest`, `NewFromCopy`, and moves' `movesFilesAllowed` / `Establish`. |
| +2. Is there any reader or writer still applying the old all-entries cap that would strand the same recovery? |
| +3. Does anything still let a new over-cap live entry be written? |
| +4. Are the tests sufficient? Is the census overlay correct and envelope-only, with no root-read unit or legacy seal change? |
| +5. **Verdict:** is it ready to merge, after the release freeze? |
| + |
| +Give findings P1/P2/P3 with file:line. Read-only; run nothing heavy. |
diff --git a/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP.md b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP.md| new file mode 100644 |
| index 000000000..0d1b847d0 |
| --- /dev/null |
| +++ b/docs/superpowers/plans/CODEX-REVIEW-HOSTFILES-CAP.md |
| @@ -0,0 +1,27 @@ |
| +--- |
| + |
| +**FIRST PROMPT:** |
| +Review `hostfiles-cap-live-entries` at `7d4efe54`, read-only, following the supplied brief. |
| + |
| +**WHAT:** |
| +Assess correctness, test coverage, census changes, and readiness to merge after the freeze. |
| + |
| +**STATUS:** |
| +**Request changes: two P2 findings.** |
| + |
| +1. **P2 — Inherited skipped entries still prevent successful scans.** The exemption at [publisher.go:73](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/publisher.go:73) lets `Open` accept an over-cap skipped entry. After restore, that unpublished file is absent, so `syncRoot` calls `StageDelete`, which still returns `ErrTooDeep`. Consequently, `Publish` and `ReleaseFlushFiles.Flush` fail; `Run` repeatedly retries the scan. The new continuation test stages a healthy file directly, bypassing this failure. Handle deletion of an inherited skipped entry without allowing new over-cap live entries, and test restore → scan → commit. |
| + |
| +2. **P2 — Exempt tombstones reintroduce unbounded, non-cancellable restore work.** The relaxed validation at [restore.go:270](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/hostfiles-cap-live-entries/internal/hostfiles/restore.go:270) admits arbitrarily deep tombstones. Removal checks every ancestor with `realParents`; directory syncing repeats those checks for every ancestor again. On this Linux toolchain, `os.Root` resolves each prefix component by component, making the surviving-parent case cubic in depth, without context checks. This can occupy installation indefinitely instead of returning the previous immediate refusal. The new test uses an empty destination, so it never exercises existing deep parents or actual tombstone removal. Make traversal efficient and cancellable while preserving deletion durability. |
| + |
| +The shared validation path covers `OpenAt`, `CopyManifest`, `NewFromCopy`, and moves’ `Establish`; I found no new route for writing an over-cap live entry. `Present` skips exempt entries, though skipped paths still participate in `superseded`. The census envelope hash matches exactly; root-read units and the legacy seal are unchanged. |
| + |
| +Targeted `hostfiles` tests could not start: the read-only sandbox denied Go’s temporary build directory. The full `internal/host` suite was not run. |
| + |
| +**NEEDED:** |
| +Nothing from you for this review. |
| + |
| +**RECOMMENDED:** |
| +Fix both findings and add regressions before merging after the freeze. |
| + |
| +**SESSION:** |
| +Safe to close. HEAD and working tree are unchanged; no processes remain running. |
| \ No newline at end of file |
diff --git a/internal/hostfiles/fix5_linux_test.go b/internal/hostfiles/fix5_linux_test.go| index b029c6af0..767040fc5 100644 |
| --- a/internal/hostfiles/fix5_linux_test.go |
| +++ b/internal/hostfiles/fix5_linux_test.go |
| @@ -8,6 +8,7 @@ import ( |
| "encoding/json" |
| "errors" |
| "os" |
| + "strings" |
| "testing" |
| "time" |
| @@ -114,3 +115,28 @@ func TestRunStopsOnAManifestItCannotCommit(t *testing.T) { |
| t.Fatalf("something was committed: %v", err) |
| } |
| } |
| + |
| +// RestorableWithinCap is the readers' rule, exported for the checks made |
| +// before work that needs a manifest restored (#236): an entry of any kind |
| +// past the cap, live, tombstoned or skipped, is ErrTooDeep, and every |
| +// reader refuses the same manifest. |
| +func TestRestorableWithinCapIsTheReadersRule(t *testing.T) { |
| + sha := strings.Repeat("a", 64) |
| + for name, e := range map[string]Entry{ |
| + "live": {Object: pfx + objectKey(1, sha), SHA256: sha, Seq: 1, Size: 1}, |
| + "tombstone": {Seq: 1, Deleted: true}, |
| + "skipped": {Seq: 1, Skipped: "too-large"}, |
| + } { |
| + past := Manifest{V: ManifestVersion, Seq: 1, Entries: map[string]Entry{pathOfBytes(MaxPathBytes + 1): e}, Platform: map[string]Entry{}} |
| + if err := RestorableWithinCap(past); !errors.Is(err, ErrTooDeep) { |
| + t.Fatalf("%s past the cap: %v", name, err) |
| + } |
| + if err := ValidateManifest(past, []string{pfx}); !errors.Is(err, ErrTooDeep) { |
| + t.Fatalf("%s past the cap, ValidateManifest: %v", name, err) |
| + } |
| + at := Manifest{V: ManifestVersion, Seq: 1, Entries: map[string]Entry{pathOfBytes(MaxPathBytes): e}, Platform: map[string]Entry{}} |
| + if err := RestorableWithinCap(at); err != nil { |
| + t.Fatalf("%s at the cap: %v", name, err) |
| + } |
| + } |
| +} |
diff --git a/internal/hostfiles/publisher.go b/internal/hostfiles/publisher.go| index 6fa47d036..19f1f80ac 100644 |
| --- a/internal/hostfiles/publisher.go |
| +++ b/internal/hostfiles/publisher.go |
| @@ -57,6 +57,14 @@ func Open(ctx context.Context, s release.Store, prefix string) (*Publisher, erro |
| return &Publisher{s: s, prefix: prefix, man: m, etag: etag, next: m.Seq}, nil |
| } |
| +// RestorableWithinCap is the check a caller makes before it starts work |
| +// that will need m restored (a move's cut, the claim's files backfill): |
| +// every entry path of m, live, tombstoned or skipped, within the tree's |
| +// length cap. A manifest that fails it is ErrTooDeep: no reader accepts |
| +// it, so an operator replaces it before anything that needs it starts |
| +// (#236, Paul's ruling of 2026-10-15). |
| +func RestorableWithinCap(m Manifest) error { return manifestWithinCap(m) } |
| + |
| // manifestWithinCap holds every entry path of m to withinTreeCap, the cap |
| // the readers apply: Open refuses to continue a manifest that fails it, |
| // and Commit refuses to write one. |
diff --git a/internal/provision/plan14_root_reads_test.go b/internal/provision/plan14_root_reads_test.go| index b52458a3c..2618e1b5a 100644 |
| --- a/internal/provision/plan14_root_reads_test.go |
| +++ b/internal/provision/plan14_root_reads_test.go |
| @@ -735,6 +735,12 @@ func TestPlan14RootReadCensus(t *testing.T) { |
| t.Fatal(err) |
| } |
| seal = "10e024eccd584f40b64e959a105c551302585e3e14e55104fc250dae5960898a" |
| + // Census overlay hostfiles-cap-236 (amadan #236): envelope-only, for |
| + // hostfiles exporting its cap rule as RestorableWithinCap. No root-read |
| + // unit changed or was added, so the legacy seal above stands. |
| + if err = loadRootReadInventory(filepath.Join(root, "internal/provision/testdata/plan14-root-reads-hostfiles-cap-236.tsv"), &inv, true); err != nil { |
| + t.Fatal(err) |
| + } |
| errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...) |
| errs = append(errs, checkRootReadDeclarations(got, inv)...) |
| if len(errs) > 0 { |
diff --git a/internal/provision/testdata/plan14-root-reads-hostfiles-cap-236.tsv b/internal/provision/testdata/plan14-root-reads-hostfiles-cap-236.tsv| new file mode 100644 |
| index 000000000..00cbdb21b |
| --- /dev/null |
| +++ b/internal/provision/testdata/plan14-root-reads-hostfiles-cap-236.tsv |
| @@ -0,0 +1,2 @@ |
| +# Plan 14 census overlay hostfiles-cap-236 (amadan #236, owner region HA, basis moves' review of a8cebad and Paul's rulings of 2026-10-15; loaded after seeded-instances): hostfiles exports RestorableWithinCap, the readers' existing cap rule (manifestWithinCap) under a name the checks made before work that needs a manifest restored can call (moves' cut preflight, the claim's files backfill). No reader or writer changes. Envelope-only: no root-read unit changed or was added, nothing runs as root that did not before, and no registry, /data, release or claim read was added, so the legacy seal stands. The removePastCap unit that census ruling hostfiles-cap-236 (platform-1e) accepted was never merged and is withdrawn. |
| +@coverage internal/hostfiles 38d423afa1d4085d9591adb11b7336318188687ddf25b209a8a0041f5d66957b |