Skip to content

feat: add control/audit bootstrap envelope, durable stores, and D-158 handoff - #28

Merged
thelostorbital merged 3 commits into
mainfrom
feat/m1-control-bootstrap
Sep 9, 2026
Merged

feat: add control/audit bootstrap envelope, durable stores, and D-158 handoff#28
thelostorbital merged 3 commits into
mainfrom
feat/m1-control-bootstrap

Conversation

@thelostorbital

@thelostorbital thelostorbital commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Summary

M1-05 part 1 (D-158 control/audit bootstrap, pure local): new internal/control package.

  • envelope.go: BootstrapEnvelopeV1 (schema id, canonical JSON, SHA-256) embedding the canonical bootstrap.CompiledPlan bytes, a plan-bound ApprovalProofV1, the complete expected (absent) observation set, and the first JournalEntryV1. Strict parser rejects duplicate/unknown/null/trailing fields, hash mismatches, noncanonical encodings, and every cross-binding failure.
  • statedir.go: explicit owner-private StateDirectory; envelope writer uses exclusive create, mode 0600, fsync, atomic exclusive publish (os.Link, fails closed on an existing name), directory fsync; reader refuses symlinks, non-0600 modes, empty or oversized files; EnsureBootstrapEnvelope accepts only the identical hash on retry. No removal API.
  • store.go: typed ControlObjectName/AuditObjectName constructors for the ARCHITECTURE prefixes (locks/, adoption/, policy/, test/harness-state.json, test/ownership/, test/lifetime/, plans/, operations/<env>/<opId>/steps/ via workflow.JournalObjectName); ControlStore{Create, Read, CompareAndSwap} with generation preconditions and no delete; AuditStore{Create, Read} with no overwrite/delete method at all; in-memory fakes.
  • handoff.go: resumable K1a/K1b engine over a seven-method AuditBucketPort (no delete/list/IAM): write-ahead local claim, describe-before-create, explicit ErrBucketNameConflict precondition failure (never an absence claim), create-only uploads verified by size+CRC32C+generation, archive lifecycle, verification before retention, separate lock call recorded as the PONR only when observed.
  • seeds.go: exact K4 seed identities and content; existing objects preserved byte-for-byte; approved-policy mismatch blocks toward WF-ENV-02.
  • iam.go: closed K3/T6 binding catalog; every binding resource-scoped to one approved bucket and one test/ prefix with the exact CEL condition; compensation limited to bindings absent before the operation.

Closes the four PR #27 review items deferred to M1-05 (resource-scoping, in-operation compensation, exact K4 seeds, create-time bucket-name conflict). Assumptions and proposed ARCHITECTURE prefix additions are in docs/specs/mongo-gcp-cli/m1-records/A-m1-05.md.

Safety properties

  • No process execution, no provider I/O, no internal/gcp change, no new dependency, no frozen file edited.
  • Envelope contains no credential, token, argv, or provider output: every field is typed and derived from the compiled plan, the approval, or the closed journal schema (test asserts no credential-shaped keys).
  • Retry accepts only the same envelope hash; a different hash for the same operation fails closed before any provider call.
  • Unrecorded or conflicting partial audit buckets block (ErrPartialBootstrapBlocked); a bucket is adopted only with a local write-ahead claim or an identical remote envelope object.
  • Hash and generation verification precedes the retention lock; an observed lock without a local claim blocks; the lock is a separate call.
  • The audit store interface has no delete/overwrite method (reflect-enforced test); the control store has no unconstrained delete; CAS races have exactly one winner.
  • Fixtures use fictional values only.

Verification

Run from the worktree with GOWORK=off:

  • go mod tidy -diff — clean
  • go run ./internal/archcheck — pass
  • go test ./... — pass (one transient helper-process failure in the untouched internal/gcp package on a loaded machine; the package passed on rerun)
  • go test -race ./internal/control/... — pass (changed package; aggregate race skipped locally per the board, GitHub Quality authoritative)
  • QLTY_TELEMETRY=off qlty check --all --no-fix --level=low --fail-level=low — No issues
  • bash scripts/test-architecture-rules.sh — all acceptance/rejection cases passed
  • bash scripts/check-go-licenses.sh — skipped: go-licenses not on PATH; no dependency changed
  • git diff --check — clean

No GCP mutation was performed. Read-only probes used: gcloud --version, gcloud storage buckets list --format=json --limit=1 (field-shape check only, no values recorded), and local gcloud storage ... --help.

… handoff

Add the internal/control package: sealed BootstrapEnvelopeV1 with strict
parsing, an owner-private state directory with exclusive 0600 fsync'd
publish, typed generation-preconditioned control and append-only audit
stores with in-memory fakes, the resumable K1a/K1b audit handoff over a
narrow bucket port, exact K4 seed objects, and the closed resource-scoped
K3/T6 bucket binding catalog. Pure local; no process execution.

Claude-Session: https://claude.ai/code/session_01QitjD2fzVf5tiwV1eRVoqz
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-09T03:01:37.163251Z 0a8c340 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a8c340019

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/control/handoff.go
Comment thread internal/control/handoff.go Outdated
Comment thread internal/control/seeds.go Outdated
Comment thread internal/control/seeds.go Outdated
Comment thread internal/control/handoff.go Outdated
Comment thread internal/control/statedir.go
Comment thread internal/control/handoff.go Outdated
Comment thread internal/control/seeds.go Outdated
Comment thread internal/control/handoff.go
Comment thread internal/control/handoff.go
@thelostorbital

Copy link
Copy Markdown
Owner Author

Coordinator (Thread F) review, one VALID finding; everything else in the M1-05 required-proof list maps to code and a test.

F-1 — LockRetention resume converges on a locked bucket without checking the retention period (internal/control/handoff.go, the if state.RetentionLocked { … } branch at the top of LockRetention).

  • Path: after a crash between the provider LockRetention call and the local retention-locked append, a retry observes RetentionLocked=true, finds the local retention-lock-claimed record, appends retention-locked, and returns success. Neither this branch nor verifyObservation/checkCommonState compares state.RetentionSeconds to AuditRetentionSeconds; the first-attempt path (lock was not observed) and checkPreLockState both do.
  • Trigger: a foreign actor changes the retention period inside the claim→lock window (or increases it on the locked bucket) before the retry. Reproduced with the in-memory fake: status reaches retention-locked with err == nil.
  • Impact: the durable PONR record asserts the D-111 365-day lock while the bucket holds a different period; the retry hides contradictory observed state. Violates AGENTS.md "Retries, resume … must not … hide a partial failure" and "contradictory state must stop the operation", and the thread's own record item 4 (an observed lock without matching claim state blocks).
  • Proportionate fix: in that branch require state.RetentionSeconds == AuditRetentionSeconds, otherwise return ErrPartialBootstrapBlocked: foreign retention period on locked bucket; add a test that crashes at the lock boundary, mutates the fake's retention period, and asserts the block.

No other finding met the adjudication bar. Rejected candidates (for the record, no action needed): 60 s adoption skew window (deliberate clock tolerance; the record should say "not before the claim minus 60 s"), untested conflicting-content branch in uploadCreateOnly (evidence gap, guarded equivalently), stale .tmp-* files never removed (no removal API by design). Full gate on the branch is still running here; Quality and CodeQL are green on GitHub.

Require the exact 365-day period when a locked bucket is resumed, refuse
expired approval or plan windows before any mutation, adopt a pre-existing
bucket only when it holds the identical envelope bytes, bind the
retention lock to the last observed metageneration, compare remote
objects byte for byte, re-verify completed handoffs, refuse regressing
clocks before persisting, reject symlinked state-directory components,
preflight and strictly validate every existing K4 seed before creating
any object, and cover each with a regression test.

Claude-Session: https://claude.ai/code/session_01QitjD2fzVf5tiwV1eRVoqz
@thelostorbital

Copy link
Copy Markdown
Owner Author

Coordinator finding F-1: VALID. Fixed in ac2a3ee: the locked-bucket resume branch of LockRetention now requires RetentionSeconds == AuditRetentionSeconds and otherwise returns ErrPartialBootstrapBlocked: foreign retention period on the locked audit bucket. Regression: TestAuditHandoffBlocksForeignRetentionOnLockedBucketAfterCrash (crash at the lock boundary, mutate the fake's period, assert the block and that no retention-locked record is written). The three rejected candidates are recorded as such in m1-records/A-m1-05.md; the 60 s skew window is now moot because adoption by timestamp was removed (Codex thread). All ten Codex threads were adjudicated VALID and fixed with regression tests in the same commit; local gates (tidy, archcheck, go test ./..., race on internal/control, qlty, git diff --check) are green on ac2a3ee. This completes the single confirmation cycle.

@thelostorbital
thelostorbital merged commit 20f8433 into main Sep 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant