Skip to content

Latest commit

 

History

History

README.md

DevStrap Audit Archive

This directory holds DevStrap's chronological design & implementation audits. This file is the single source of truth for the audit program: the index of passes, the conventions for keeping them sane, and the consolidated open backlog (what's still actionable). Per-finding detail and file:line evidence live in each pass's own file.

Moved here from the repo root on 2026-06-29 to declutter the root and end the cross-pass finding-ID collisions (fifth-pass finding P5-PROC-01).

Index of passes

Pass Date File Scope Findings Status
1 (initial) AUDIT_RECOMMENDATIONS.md First-pass design & implementation review Largely implemented / superseded by later passes
2 2026-06-27 AUDIT_RECOMMENDATIONS_2026-06-27.md Second pass: CI (CI-*), non-VCS/remote-less (NOVCS-*), forges (FORGE-*), working-state sync, zero-knowledge hub 65 Largely implemented / superseded
3 2026-06-28 AUDIT_RECOMMENDATIONS_2026-06-28.md Cloud-sync architecture: EAGER-*, DRAFT-*, HUB-01..08, XP-*, SCALE-* workstreams EAGER-*/DRAFT-*/HUB-01..08/XP-* shipped (PR #16); SCALE-* documented-not-built
4 2026-06-28 AUDIT_RECOMMENDATIONS_2026-06-28_PASS4.md Fourth pass: audit of the now-shipped cloud-sync system 44 (P1=17, P2=23, P3=4) ~32 shipped/narrowed (incl. P4-SEC-02/04/07, P4-GIT-03/04, P4-QUAL-02/05, P4-PROD-04/05, P4-SYNC-02/06, P4-HUB-11/12); ~12 open/partial — see the Pass 4 table below
5 2026-06-29 AUDIT_RECOMMENDATIONS_2026-06-29_PASS5.md Fifth pass: adversarial review of the PASS4 batch + under-examined dimensions + new features 36 (P1=1, P2=12, P3=23) 35 shipped (P5-ARCH-01 2026-07-04); 1 open (P5-CLI-01) — see below
6 2026-07-01 AUDIT_RECOMMENDATIONS_2026-07-01_PASS6.md Sixth pass: adversarial audit of the PR #24/#25 batch (live R2 hub + envelope-encryption foundation) + under-examined dimensions 43 (P1=5, P2=25, P3=13) Closed — 43/43 shipped
7 2026-07-10 AUDIT_RECOMMENDATIONS_2026-07-10_PASS7.md Seventh pass: adversarial audit of the post-Pass-6 waves (git/folder hub carriers, OS sandbox, device-trust + env-sync propagation, distribution/service install) + commercial readiness 47 (P1=1, P2=25, P3=21) Open — see below

Conventions (going forward)

  1. Pass-scoped, globally-unique finding IDs. Prefix every finding with its pass: P4-SEC-01, P5-HUB-01, etc. The bare-ID scheme of passes 1–4 caused collisions (GIT-01 denotes a repo-lock bug in pass 2 and an empty-checkout bug in pass 4). Pass 5 onward is pass-scoped; this ledger back-labels earlier passes with P<n>- where it lists them.
  2. New audits live in docs/audits/, not the repo root.
  3. Update this ledger every pass: add the new file to the index and reconcile the open backlog (move shipped findings out, add new ones).
  4. Work-log rotation (spec/18_WORK_LOG.md, now 1,200+ lines): rotating older cycles into a dated archive is recommended but deliberately deferred to keep each PR reviewable; tracked as a follow-up (see P6-DOC-02, which recommends promoting this from a convention bullet into an actual backlog row).
  5. The spec-drift gate (internal/specdrift) treats any change under docs/ as requiring a spec/18_WORK_LOG.md entry, and the four specs that track audit files (spec/00, spec/12, spec/14, spec/17) point their tracks_code: frontmatter at docs/audits/.

Open backlog — single source of truth for "what's left"

Currently-actionable findings, pass-scoped. Earlier passes (1–3) are largely implemented or superseded (see spec/18_WORK_LOG.md for the shipped history); Pass 6 is fully closed; the open backlog is concentrated in passes 4, 5, and 7.

2026-07-01 — Pass 6 landed. The sixth pass (AUDIT_RECOMMENDATIONS_2026-07-01_PASS6.md) audited trunk 8c739b8 (PR #25) via a verification-driven nine-dimension workflow: 43 findings (P1=5, P2=25, P3=13), each adversarially verified against the code and checked for novelty against this backlog. Headlines: P6-SEC-01/P6-SYNC-01 (the envelope layer still trusts the hub — unverified grant ingestion + whole-batch abort on one bad event), P6-HUB-01/P6-DATA-01 (the now-live hub GC deletes live draft blobs), and P6-GIT-01 (a universal 2-minute git timeout silently breaks eager materialization of large repos). This pass also reconciled the ledger below per convention #3: P4-SEC-02 moved to Recently shipped, P4-SEC-05 corrected to partial (its own finding is P6-DOC-02). 2026-07-02 update: the hub-trust workstream (P6-SYNC-01, P6-SEC-01, P6-SEC-02; PRs #30–#34), P6-DATA-01 (PR #35), P6-HUB-01 (PR #36), and P6-GIT-01 are shipped and moved to Recently shippedall five Pass 6 P1s are closed (the AD-2 hardening-freeze P1 wave is complete). 2026-07-03 update: P6-DATA-02 is shipped; the open backlog is now the remaining P2/P3 set below. 2026-07-04 — snapshot-exchange wave (PRs #65–#76): the event log is now BOUNDED — P4-SYNC-02/P4-HUB-11 (compaction + full-state snapshot exchange, both P1s open since Pass 4), P4-HUB-12, P4-SYNC-06 (narrowed), and Pass-6's P6-HUB-04 shipped, alongside a parallel quick-win sub-wave closing seven Pass-6 P2/P3s (P6-GIT-02, P6-GIT-03, P6-CLI-01, P6-XP-01, P6-QUAL-04, P6-QUAL-05, P6-DOC-04; PRs #66–#72). Pass-6 drops 19→11 open. Every code PR carried dual review (coordinator line-by-line + Codex gpt-5.5, several with an additional opus pass); review findings fixed pre-merge include a fail-open hollow-manifest parse (P1), a CAS self-race, a byte-for-byte read-back gate before compaction deletes, a gc stale-list race, and an owner-aware sweep-lock release. 2026-07-03 (evening) — sync-convergence wave (PRs #59–#63): P5-SYNC-01 (the last AD-2 hardening-freeze critical — the freeze is COMPLETE), P6-SYNC-02, P6-DATA-03/05/06, P6-XP-04, and P6-QUAL-01/02 shipped and moved to Recently shipped; Pass-6 drops 26→19 open. 2026-07-03 update: P6-GIT-02 shipped; Pass-6 drops 19→18 open. A separate 2026-07-03 pairing wave (init --join --workspace-id adoption, the founder-pinning ceremony, doctor mismatch detection, and the two-device runbook; PRs #48–#50 + the docs PR) closed the cross-device workspace-ID pairing remainder of P4-SEC-07 and the joiner half of P4-SEC-04 — both are Pass-4 findings tracked in the Pass 4 table, so the Pass-6 count is unchanged (they narrow, not close, and never counted toward the 43). 2026-07-04 update: P6-HUB-03 shipped (fix/p6-hub-03); Pass-6 drops 6→5 open. P6-CLI-03 shipped (fix/p6-cli-03); 5→4 open. P6-CLI-04 shipped (fix/p6-cli-04); 4→3 open. P6-DOC-01 test-hardening residual shipped (fix/p6-doc-01-test); 3→2 open. P6-XP-06 shipped (fix/p6-xp-06); 2→1 open. P6-GIT-06 shipped (fix/p6-git-06); 1→0 open — Pass 6 is fully closed (43/43). 2026-07-05 — next wave chosen: multi-device completeness — synced env-bundle exchange (ENV-SYNC-01) + synced device-trust propagation (TRUST-01); direction and backlog rows live in spec/14_MVP_ROADMAP_AND_BACKLOG.md. 2026-07-06 — unattended-operation wave COMPLETE (PRs #136–#139): issues #133/#134 closed, P4-GIT-04 and P4-PROD-04 shipped and moved to Recently shipped; see the spec/14 DIRECTION bullet.

2026-07-10 — Pass 7 landed. The seventh pass (AUDIT_RECOMMENDATIONS_2026-07-10_PASS7.md) audited trunk d667530 via a verification-driven ten-dimension workflow (opus-4.8 / GPT-5.6 / Grok-4.5 reviewers; every candidate adversarially verified, every candidate P1 double-verified): 47 findings (P1=1, P2=25, P3=21), concentrated on the post-Pass-6 waves — the git/folder hub carriers (AD-1), the OS-enforced agent sandbox (P4-GIT-03), device-trust propagation (TRUST-01), cross-device env-sync (ENV-SYNC-01), and the distribution/service install wave — plus a commercial-readiness dimension feeding spec/20. Headline: P7-SYNC-01 (P1) — device revocation is erased by hub compact and absent from snapshot.v1, so a device recovering via snapshot permanently keeps a revoked device approved. Top P2s: P7-QUAL-02 (the curl\|sh installer verifies only sha256, not the cosign/SLSA it ships), P7-SEC-02 (post-revoke containment lost on a crash), P7-HUB-02 (git-carrier branch rewind = silent state loss), and the P7-DATA-03/04/05 backup/restore-atomicity cluster. This pass also reconciled the ledger per convention #3: the Pass-5 count is corrected to 35 shipped / 1 open (P5-CLI-01; contextcheck is a P4-QUAL-07 sub-item, not a Pass-5 row — both prior counts were stale), the Pass-6 index status is corrected to closed, and the Recently shipped invariant note is reworded to the ID-prefix filter rule below (it had hand-listed "seven" non-P6 rows when 16 now exist; the = 0 arithmetic was unaffected).

2026-06-30 — most of Pass 5 shipped. The PASS5 implementation cycle (fix/pass5-backlog, see spec/18_WORK_LOG.md) landed 32 of the 36 Pass-5 findings (now including P5-HUB-01) plus P4-SEC-05 (partial — goreleaser-action SHA-pin only) and P4-QUAL-07 (partial). Still open: P5-SYNC-01 (transport-cursor redesign — deferred with design in spec/07, latent), P5-CLI-01 (the render seam landed and is wired into materialize; full rollout to every leaf command remains), P5-ARCH-01 (convergence property tests shipped; the formal pure Decide extraction remains), and P4-QUAL-07's contextcheck (deferred — needs threading a context through the forge chain). P5-HUB-01 shipped today (branch fix/p5-hub-01): the aws-sdk-go-v2 S3 adapter is wired behind hub: r2://<bucket> (hubFromOptions), with DEVSTRAP_HUB_S3_* env/config credentials, aws.NopRetryer{} single-retry, and an env-gated MinIO conformance test (TestR2MinIOConformance) plus hermetic mapS3Error/conformance unit tests. The PASS4 carried-forward XL items (SEC-07 envelope encryption foundation shipped 2026-06-30, GIT-03 OS sandbox, SEC-02 shipped 2026-06-30, SEC-04, SYNC-02/HUB-11 compaction) — SEC-07 full workspace-ID pairing and SEC-08 remained open as of that date (pairing shipped 2026-07-03; see the Pass-4 table below).

Recently shipped (moved out of "still open" per convention #3)

ID Sev Shipped Note
P7-DATA-05 P2 fix/p7-data-05-restore-journal (2026-07-11) Restore promotion is one journaled all-or-nothing transaction: a durable pre-swap journal, one shared aside suffix, keep-every-aside-until-all-Done, sweep-then-remove-journal-last; recoverRestoreJournal rolls forward only from a durably complete journal and otherwise reverses to the exact pre-restore state, failing closed (journal retained) on invariant damage or an unsafe journal. openState fences all commands on a pending journal; db restore --recover completes-or-reverses; doctor reports it. A state-level maintenance lock (repo-lock primitive) serializes restore, full backup, db down (closing the P7-DATA-07 check-vs-Down residual), and run-loop ticks. Ported from the interrupted prior session's combined branch; 13 unit tests + db_restore_journal_recovery.txtar.
P7-DATA-04 P2 fix/p7-data-04-archive-manifest (2026-07-11) Full-backup archives carry a hash manifest (manifest.json v1: per-entry size + SHA-256, required set, workspace/device/custody metadata) written as the last tar entry; db restore fails closed before any swap — entries hash-verified, unlisted extras refused, truncated archives refused, pre-manifest archives need explicit --allow-legacy — and a recoverability probe cross-checks the STAGED DB read-only (referenced blobs staged + content-address-matching, device identity/signing keys present, held WCK epoch files present), closing the "successful but unrecoverable restore". Backup-side, the snapshot Store is now the authority for every archive decision (refs, custody, device, workspace, held epochs — the P7-DATA-03 completion) and blobs are re-verified against their content address while streaming. Ported from the interrupted prior session's combined branch.
P7-DATA-03 P2 fix/p7-data-03-snapshot-enumeration (2026-07-11) db backup --full now enumerates blob refs from the VACUUM INTO snapshot (state.AllBlobRefsInFile), not the live store, so a rotation between snapshot and enumeration can no longer produce an archive whose DB references a blob the tar omits. Up to 3 snapshot+enumerate+stat passes heal legitimate concurrent-rotation drift; refs still missing after that are a hard exitConflict naming every ref — the warn-and-skip missing_blobs path is gone, so a "successful" full backup can never silently omit a referenced secret. Key material deliberately still stages live (append-mostly custody). Pinned by TestAllBlobRefsInFile, TestFullBackupMissingBlobFatal, TestFullBackupRetriesOnDrift, and e2e db_full_backup_missing_blob.txtar.
P7-QUAL-07 P2 fix/p7-qual-07-fslock-owner (2026-07-11) The shared carrier lock (fsLock, git + folder carriers, internal/hub/folder.go) now writes an immutable JSON owner record (pid, hostname, crypto/rand nonce, platform.ProcessStartTime identity) at O_EXCL create instead of a never-read bare PID. Same-host staleness is owner-aware: a live holder is never broken regardless of mtime (a suspended writer's checkout can no longer be stolen at the 10m TTL — the second acquirer times out with an owner-naming error), a dead holder — or a PID-recycled one, when a usable ProcessStartTime identity is recorded and resolvable — is broken immediately, and legacy/corrupt/cross-host records keep the mtime-TTL fallback as the upgrade path. Stale break double-reads the owner bytes before removal; release removes only its own nonce generation (no more break-then-release cascade theft); the heartbeat stops when the lock file disappears. The verify-then-remove filesystem race is the documented bounded residual (spec/15). Pinned by nine TestFSLock* cases under -race.
P7-QUAL-07 P2 fix/p7-qual-07-fslock-owner (2026-07-11) The shared carrier lock (fsLock, git + folder carriers, internal/hub/folder.go) now writes an immutable JSON owner record (pid, hostname, crypto/rand nonce, platform.ProcessStartTime identity) at O_EXCL create instead of a never-read bare PID. Same-host staleness is owner-aware: a live holder is never broken regardless of mtime (a suspended writer's checkout can no longer be stolen at the 10m TTL — the second acquirer times out with an owner-naming error), a dead or PID-recycled holder is broken immediately, and legacy/corrupt/cross-host records keep the mtime-TTL fallback as the upgrade path. Stale break double-reads the owner bytes before removal; release removes only its own nonce generation (no more break-then-release cascade theft); the heartbeat stops when the lock file disappears. The verify-then-remove filesystem race is the documented bounded residual (spec/15). Pinned by nine TestFSLock* cases under -race.
P7-DATA-07 P3 fix/p7-data-07-rollback-guard (2026-07-11) Store.Down refuses to roll back FROM schema version 23 while any env_profiles row carries a non-NULL LWW source-event coordinate — the down block would drop the columns and erase the cross-device incumbent, letting a delayed older env event overwrite a newer value after down→up. Empty-coordinate rollback and every other down step are unchanged; the SQL migration itself is untouched (guard lives in Store.Down, covering db down and direct callers, keeping the embedded SQL-only goose setup). Pinned by TestMigration00023DownRefusesPopulatedCoordinates / ...EmptyCoordinatesSucceeds.
P7-GIT-03 P2 fix/p7-git-03-pid-reuse (2026-07-11) Repo locks and agent-run crash sweeps now pair a PID with an opaque platform start-time identity. Linux records raw /proc/<pid>/stat field 22 and macOS records kern.proc.pid start time in microseconds; a live PID with a different identity is treated as recycled, while a missing identity or failed lookup preserves conservative legacy behavior. Migration 00024 adds agent_runs.runner_started_at. Pass-7 arithmetic: open 41→40; P2 22→21.
P7-SYNC-03 P3 fix/p7-sync-03-draft-latest-tiebreak (2026-07-11) Draft-snapshot "latest" selection, pruning, and the hub gc --dry-run retained-refs preview now agree on a canonical, fleet-wide tiebreak instead of per-device local state. LatestDraftSnapshot, PruneDraftSnapshots, and RetainedBlobRefs (internal/state/store.go) all order/rank by (source_event_hlc, source_event_device_id, source_event_id) DESC — the same coordinate samePathLess/envCoordLess use elsewhere — with local created_at DESC, id DESC retained as FINAL tiebreakers only (legacy unstamped rows collapse to (0,'','') and keep a stable local order; stamped rows are unique on the canonical triple, migration 00012). RetainedBlobRefs was a Codex review catch; the legacy fallback a CodeRabbit catch. Pinned by TestLatestDraftSnapshotDeterministicTiebreak, TestPruneDraftSnapshotsDeterministicTiebreak, TestRetainedBlobRefsDeterministicTiebreakMatchesPrune. spec/07 materialize/restore prose updated.
P7-SYNC-04 P3 fix/p7-sync-04 (2026-07-10) Self-healing WCK rotation is now owed by every device that LEARNS of a revocation, not only the local revoker. The trust-apply path (internal/sync/events.go device.revoked/lost) and the snapshot importer (importTrustTx) arm the wck_rotation_pending marker transactionally with the flip, guarded on epoch>0, so the receiver's next sync rotation gate mints epoch+1 excluding the revoked device. The marker format moved to internal/state (SetWCKRotationPendingTx/CurrentKeyEpochTx, storm-guarded to preserve the original "owed since") so internal/sync writes it without importing internal/cli. Accepted residual (spec/07/15): a newer epoch is not proof of exclusion (a peer that hasn't pulled the revoke can regrant it), so each learner rotates once — bounded, terminating, forward-secure.
P7-SEC-02 P2 fix/p7-sec-02 (2026-07-10) devices revoke/lost writes a merged, machine-local revoke_containment_pending record in the same transaction as the trust flip and synced event. The direct path clears its device only after rotation is complete or separately owed, secret bindings are flagged, and blob rewrap succeeds; sync resumes crash-interrupted containment after pull, rotates at most once per cycle, and doctor names pending devices/timestamps.
P4-SEC-02 P1 PR #25 (8c739b8) Namespace-map event log is envelope-encrypted at rest on the hub (internal/sync/eventcrypt.go, encryptedhub.go). Fully shipped — no longer open.
P4-SEC-07 P2 PR #25 (8c739b8), foundation WCK epoch keyring (internal/workspacekeys) + age-wrapped grants + Rotate on revoke. Foundation only; open remainder tracked below.
P4-SEC-07 (pairing) P2 pairing wave (2026-07-03, PRs #48–#50 + docs) Cross-device workspace-ID pairing shipped: devstrap init --join --workspace-id <id> adopts the founder's ws_<uuidv7> at init (born-correct — a mismatch on an already-initialized store refuses with a remove-and-reinit remedy), devstrap status / devices recipient --workspace-id surface the id, a keyless joiner pins the founder (devices enroll … --approve) before first sync — closing the joiner half of the P4-SEC-04 TOFU window — and doctor --remote detects the joiner/empty-prefix mismatch signature (regression-tested by TestR2WorkspacePrefixIsolation). Docs: spec/19 §E runbook, spec/07 init lifecycle, spec/15 threat note, README two-device quickstart. Open remainders (Pass 4 table): P4-SEC-07 periodic (non-revoke) rotation; P4-SEC-04 part 2 — one-paste pairing code + founder-side automation (fingerprint confirmation shipped 2026-07-03).
P4-SEC-04 P1 PRs #54/#57 (2026-07-03) Pre-enrollment bootstrap window: local pairing plane fully closed. Part 1 (PR #54): full 256-bit device fingerprint (internal/devicekeys/fingerprint.go, golden-vector-pinned) binding signing key + age recipient, compare-and-confirm gate on devices approve/enroll --approve before any DB write, SECU-05 keyless-placeholder refusal. Part 2 (PR #57): one-paste devstrap-pair1: pairing codes (devices pairing-code, init --join --code [--fingerprint], devices enroll --code --approve --fingerprint), unauthenticated-by-design blob with integrity from the derived-fingerprint ceremony; control-char rejection; carried fields surfaced at confirm time. Proven live on R2 (dogfood run 3). Remaining (tracked separately): authenticated snapshot exchange, remote trust propagation.
P4-SEC-07 (rotation) P2 PR #56 (2026-07-03) Periodic (non-revoke) WCK rotation — the last open remainder: keys rotate (pure Rotate, wrap-first atomic, no revoke side effects) + age-triggered auto-rotation in sync (keys.rotate_max_age default 90d, 0 disables; post-pull/pre-push with same-cycle grant push; storm suppression via pulled-grant age reset), doctor workspace key age. Forward exposure only; documented-not-built: old-epoch containment, keychain-slot growth. Proven live on R2 (dogfood run 3: epoch 1→2 converged in one sync per device). With the foundation (PR #25) and pairing (PRs #48–#50) rows above, P4-SEC-07 is fully shipped.
P4-GIT-03 P1 slices 1–6 (2026-07-05, PRs #107/#121–#124/#125/#127 + read-confine) OS-enforced agent sandbox, fully shipped. macOS Seatbelt + Linux bubblewrap + Linux Landlock fallback confine writes to the worktree/tmp, deny credential reads (full-fidelity backends), and deny policy-scoped network; a pure-Go seccomp syscall denylist (mount/kexec/ptrace/keyring/io_uring/legacy-escape → EPERM) rides both Linux backends; unsigned local sandbox.violation telemetry records backend/mode/limitations per run and persists tagged macOS Seatbelt denials, surfaced by agent show/doctor; and tighter read confinement (--read-confine auto|on|off, default-on for the readonly policy, --read-allow extras) restricts reads to the worktree/tmp, OS toolchain roots, and $HOME build caches on all three backends — which also gives the Landlock fallback a credential-read boundary it otherwise lacks. --sandbox auto|off|require gates the whole thing; require refuses to launch when a requested confinement cannot be enforced. Documented-not-built follow-ups (not P4-GIT-03 remainders): Linux runtime denial detection, macOS mach-lookup tightening, and containerization (spec/14 roadmap).
P4-PROD-04 P2 feat/p4-prod-04-service-install devstrap service install|uninstall|status wraps run-loop in a per-user launchd LaunchAgent (macOS, com.devstrap.run-loop, modern bootstrap/bootout/print verbs) or systemd --user service (Linux, devstrap-run-loop.service) so the workspace converges unattended. The OS branch lives entirely behind internal/platform (ServiceSpec/ServiceManager enriched; service_launchd.go/service_systemd.go untagged + golden-tested, service_darwin.go/service_linux.go PATH-shim-tested). Install refuses an unconfigured hub and an ephemeral $TMPDIR/go-build exec path, writes the plist/unit atomically at 0600, restarts on failure throttled to 30s, and bakes no secret (adapters add only PATH); the systemd path advises loginctl enable-linger when lingering is off and never auto-downgrades keychain custody (P6-XP-04). doctor folds in an optional service check. Docs: spec/13 §service, spec/05/spec/06 shipped installers, spec/16 test rows. (Pass-4 finding — does not count toward the Pass-6 total.)
P6-SYNC-01 P1 PR #30 Per-event event_verification_failure quarantine + approve-time replay; one bad signed event no longer wedges the batch. Residual now CLOSED: synced device.revoked/device.lost trust propagation shipped (TRUST-01, 2026-07-05 — revoke is no longer local-only), survives compaction (P7-SYNC-01), and since P7-SYNC-04 every device that learns of a revoke also owes the WCK rotation.
P6-SEC-01 P1 PRs #31/#33/#34 (a) grant carriers verified before WCK ingestion; (b/c) (epoch, kid)-addressed custody, overwrite refusal, grant-preferred PushKey, replay-time grant ingestion. Fully shipped.
P6-SEC-02 P2 PRs #32/#33 Founder/join split (init --join, pull-before-push, founder gate) + (epoch, kid) keying; a joiner never self-mints or loses pre-approval events. Open residuals: P6-SEC-03 truncate wedge, P4-SEC-04 part 2 (one-paste pairing code; fingerprint confirmation shipped).
P6-DATA-01 P1 PR #35 (2026-07-02) Origin records its own draft_snapshots row in one transaction with the draft.snapshot.created event (InsertLocalEventTx + RecordDraftSnapshotTx), on both the create and revoke-rewrap paths; e2e-pinned by draft_snapshot_gc_retains_origin.txtar.
P6-HUB-01 P1 PR #36 (2026-07-02) hub gc is sync-first (pre-GC pull+apply via pullAndApplyEvents, including blob caching), refuses to sweep on any truncated/skipped pull (PullStats), quarantined/cursor-held apply (ApplyEventsWithStats), or open quarantine conflict, and keeps unreferenced blobs younger than --grace-window (default 24h; Hub.ListBlobs now carries LastModified). Skew quarantines auto-resolve when their event later applies, so a clock hiccup cannot block gc forever. E2e-pinned by hub_gc_stale_marks.txtar. Follow-ups (all since shipped): signed retention marker (P6-HUB-04, PR #75); sweep lock + dedup-PutBlob freshness + pre-delete re-stat (P4-HUB-12, PR #76); skipped-at-log-tail gc refusal (P6-SYNC-02, PR #63).
P6-GIT-01 P1 fix/p6-git-01 (2026-07-02) Git timeout split by command class: Runner.LongTimeout (default 30m, config materialization.clone_timeout, gitRunner(opts) at every CLI call site) applies per attempt to clone/fetch/LFS; a self-imposed deadline kill is the distinct terminal ErrTimeout (network exit code), ending the wipe-and-retry — a >2-minute blobless clone now completes. Pinned by the one-attempt/no-wipe fake-git tests + config round-trips. Completes all five Pass 6 P1s (AD-2 P1 wave).
P6-GIT-02 P2 fix/p6-git-02 (2026-07-03) Agent diff summaries now compare the recorded BaseSHA to HEAD under Committed since base: and keep dirty residue under Uncommitted:, so committed agent work appears in agent show and generated PR bodies. Unborn-HEAD repositories fall back to the old working-tree-only summary. Pinned by real-git tests for committed changes, dirty residue, and unborn HEAD.
P6-GIT-03 P2 PR #69 (2026-07-03) Dependency rebuild now runs before env hydrate and captures rebuild stdout/stderr to ~/.devstrap/logs/rebuilds/<sanitized-project-path>.log with mode 0600; failures name the log path. Pinned by TestMaterializeRebuildsBeforeHydrate and TestMaterializeRebuildLogIsWritten0600.
P6-SYNC-03 P2 PR #38 (2026-07-03) Sticky fail-closed enrollment: hasEnrolledDevices counts trust_state IN ('approved','revoked','lost'), so revoking/losing the last approved device keeps verification fail-closed instead of reopening the pre-enrollment window; post-revoke traffic (revoked or unknown devices) quarantines per P6-SYNC-01. Pending placeholders still don't count; the never-enrolled bootstrap window (P4-SEC-04) is unchanged. Pinned by TestHasEnrolledDevicesStickyAfterRevoke + TestApplyEventsRevokedLastDeviceStaysFailClosed. Residual now CLOSED: synced device.revoked/device.lost trust propagation shipped (TRUST-01, 2026-07-05), with learner-owed WCK rotation since P7-SYNC-04.
P6-DATA-02 P2 fix/p6-data-02 (2026-07-03) ClearRotationForProject now filters via namespace_entries.env_profile_id instead of the non-existent env_profiles.namespace_id; store coverage proves per-project isolation and CLI coverage proves one-arg env rotate <path> succeeds.
P6-CLI-02 P2 fix/p6-cli-02 (2026-07-03) scan <dir> --adopt is gated on the scanned root naming the same directory as the workspace root (sameResolvedDir: byte-exact after EvalSymlinks, no case-folding; refusal is exitUsage), so one command can no longer rewrite the fleet namespace with out-of-tree repos; adoption proceeds under the canonical root spelling and read-only scans of arbitrary directories keep working. Pinned by TestScanAdoptRefusesNonWorkspaceRoot/...ExplicitWorkspaceRootSucceeds/...AcceptsSymlinkedWorkspaceRoot/...ReadOnlyAllowsNonWorkspaceRoot.
P6-GIT-05 P2 fix/p6-git-05 (2026-07-03) createFreshWorktree failures after git worktree add (LFS pull, current-device lookup, DB insert) now remove the just-created checkout and delete its agent/... branch under a detached bounded context (context.WithoutCancel + 2m cap, so a Ctrl-C that caused the failure cannot no-op the cleanup); the agent run file-policy-denial cleanup deletes the branch too, and the LFS error names the worktree path. Pinned by TestCreateFreshWorktreeCleansUpAfterLFSPullFailure/...AfterInsertWorktreeFailure. Doctor orphan-worktree check deliberately out of scope.
P6-SYNC-04 P2 PR #44 (2026-07-03) Hard cut to enc.v2: the AEAD AAD binds the full carrier tuple (ID, DeviceID, sealing-key kid, Seq, HLC, epoch), the signature domain moves to devstrap:event:v2 (+device_id/seq, v1 verify fallback for re-pushed history), and a held-key AEAD failure forwards the carrier to an undecryptable quarantine conflict that the per-pull replay auto-recovers once its grant arrives. v1 is dead (loud skip + re-found guidance).
P6-QUAL-03 P2 fix/p6-qual-03 (2026-07-03) A minio-conformance ubuntu CI job boots a digest-pinned minio/minio via docker run (checkout with persist-credentials: false) and runs TestR2MinIOConformance against it on every push/PR, so the production aws-sdk-go-v2 S3 adapter is now exercised against a real backend in CI instead of only the in-memory memS3 fake; go test ./... stays hermetic by default.
P6-QUAL-04 P3 fix/p6-qual-04 (2026-07-03) SSH host-alias forge tests now install a canned ssh executable in t.TempDir() and prepend it to PATH, deterministically covering the ssh -G hostname override path, the no-override echo path, and non-zero-exit fallback to the file parser. No production code change was needed because exec.LookPath("ssh") is intercepted by the PATH shim.
P6-HUB-02 P2 fix/p6-hub-02 (2026-07-03) Hub S3 credentials resolve env/config (op:// refs via op read, 60s-bounded) → AWS_* → per-workspace keychain slot written by new devstrap hub login/logout (0600 file fallback under DEVSTRAP_NO_KEYCHAIN); resolved secret rides redact.Secret (with struct-level Stringer guards); auth failures map to ErrS3Auth with a hint. spec/13/15/19 reconciled (age-blob variant deliberately not built).
P5-SYNC-01 P2 PR #59 (2026-07-03) Transport cursor decoupled from HLC: per-origin-device Seq cursors (hub_device_cursors, migration 00017) — a late-pushed event can never be skipped (e2e sync_late_push.txtar, verified failing pre-cursor); R2 writes the per-device eventlog/ layout with legacy dual-read (no bucket wipe); per-device fault isolation (a held event pins only its origin device); push watermark by gapless Seq (no legacy backfill — it could strand a regressed-HLC event); founder gate consults both cursor tables. Completes the AD-2 hardening freeze. Residual (documented, spec/15): byzantine withhold+forge slot consumption; recovery = snapshot exchange (P4-SYNC-02).
P4-SYNC-02 P1 PRs #65/#73/feat/hub-compact (2026-07-04) Event-log compaction + snapshot exchange SHIPPED end-to-end: sealed content-addressed snapshot.v1 objects under the current-epoch WCK + a signed CAS retention manifest (devstrap:retention:v1, per-device floors, PrevSHA256 chain); ImportSnapshot pure-LWW merge with per-device chain anchors (migration 00020) and fail-closed producer verification (no pre-enrollment window); hub compact publishes floors from the transport cursors with monotonic-CAS + byte-for-byte read-back confirm BEFORE any deletion. Also the recovery path for the spec/15 byzantine withhold+forge residual.
P4-PROD-05 P2 v0.1.0 (2026-07-05; PRs #105/#108, tags v0.1.0-rc.1/rc.2/v0.1.0) Distribution SHIPPED end-to-end: GoReleaser cask into Reederey87/homebrew-devstrap (brew install Reederey87/devstrap/devstrap, completions linked, quarantine-strip postflight), checksum-verifying scripts/install.sh (curl|sh, latest-release resolution, never sudo), completions/LICENSE/README in archives, and the release pipeline hardened live: the first stable attempt exposed that two tags on one commit made GoReleaser rebuild rc artifacts (git version sort ranks -rc.1 above the bare version); fixed by pinning GORELEASER_CURRENT_TAG (PR #108) and verified by promoting v0.1.0 on v0.1.0-rc.2's exact commit.
P4-QUAL-05 P2 v0.1.1 (2026-07-05; PRs #115/#117, tags v0.1.1-rc.1/v0.1.1) SBOM + build provenance SHIPPED and live-verified: per-archive SPDX-2.3 SBOMs (syft, 47 packages in the darwin/arm64 SBOM), cosign keyless bundle over checksums.txt (Verified OK against the release-workflow Fulcio identity), and a SLSA v1 attestation (multiple.intoto.jsonl, slsa-verifier PASSED on all four tarballs) — verification recipes in README/RELEASING.md. The P4-SEC-05 notarization remainder is tracked separately.
P4-HUB-11 P1 feat/hub-compact (2026-07-04) R2 compaction: CompactEventsBelow bounds the seq-layout listing at each floor key and deletes only parseable below-floor legacy keys (unparseable KEPT); snapshot objects at snapshots/<sha256>.json, manifest at meta/retention.json; superseded snapshots pruned (--keep-snapshots). Pull cost/memory now bounded by the floor.
P6-HUB-04 P3 PR #75 (2026-07-04) The retention floor gains its signed hub-side markers: per-device meta/acks/<device_id>.json (AckMarker, Ed25519 under devstrap:ack:v1) written after every fully-clean sync cycle — best-effort, last-writer-wins, unchanged-cycle skip — recording the consumed transport cursor, push watermark, and HLC clock (the tombstone-safety clock). Hub gains PutAck/ListAcks/DeleteAck; unblocks P4-SYNC-06.
P6-HUB-03 P3 fix/p6-hub-03 (2026-07-04) Bounded errgroup fan-out (r2PushConcurrency for events, blobPushConcurrency for referenced blobs) replaces the serial per-event/per-blob PUT loops. All-or-nothing batch semantics stay unchanged: the caller advances the push watermark only on full-batch success, so no ordering/wave machinery is needed after P5-SYNC-01's per-device Seq cursor made the old HLC concern moot. Tests prove a mid-batch failure surfaces correctly under concurrency.
P6-CLI-03 P3 fix/p6-cli-03 (2026-07-04) Every usage-error class now exits 10: SetFlagErrorFunc wraps cobra flag-parse errors in appError{exitUsage}; usageArgs wraps all 26 positional-arity validators; unknown top-level subcommands (unreachable by any cobra hook — resolved inside Find() before RunE/Args run, and root's Args can't intercept without a silent help-and-exit-0 regression) classify at the single ExitCodeWithWriter site on cobra's stable error format, rationale pinned. spec/13 exit-code table caveat retired. Pinned by TestUsageErrorsExitTen.
P6-CLI-04 P3 fix/p6-cli-04 (2026-07-04) --quiet is now real: options.progressf (no-op under quiet) routes sync/materialize/init/run-loop/hub/scan progress and action-summary chatter; dry-run output, result rows, warnings, prompts, --json, and errors stay ungated; flag help reworded to "suppress progress output (results and errors still print)". Pinned by TestQuietSuppressesInitProgressButCreatesWorkspace / TestQuietSuppressesMaterializeHumanProgressOnly (incl. a --quiet --json case).
P6-DOC-01 P2 doc: audit PR #28; test: fix/p6-doc-01-test (2026-07-04) Command-doc gate is now path-anchored: collectCommandPaths recurses the cobra tree into full parent-prefixed paths (arbitrary depth) and requires each as a contiguous substring of BOTH spec/13 and spec/00's inventories — the bare-leaf Contains gate let an undocumented env rotate pass via unrelated log-rotation prose. The hardened gate flushed 19 real spec/00 gaps (all fixed by expanding the shorthand inventories to literal full paths). Fully shipped with the doc portion from the audit PR.
P6-XP-06 P3 fix/p6-xp-06 (2026-07-04) scan.Walk now compiles the workspace root's .devstrapignore per walk (ignore.CompileFromDir(root, true); warn + defaults fallback on compile error; Options.Ignore test seam), so user negations (!bin/) re-include default-pruned dirs at scan time, matching the draft plane. Pruned-dir count surfaced as Result.PrunedDirs + one informational line in interactive scan (deliberately NOT a warning — run-loop echoes scan warnings every tick). Pinned by TestWalkCompilesDevstrapignoreAndPrunesCustomPatternWithDefaults / ...MalformedDevstrapignoreWarnsAndFallsBack... / ...NegationReincludesDefaultPrunedDirectory.
P6-GIT-06 P3 fix/p6-git-06 (2026-07-04) agent pr refuses a non-complete run with exitConflict unless --allow-incomplete (stderr warning; --allow-stale-base precedent). Migration 00021 adds agent_runs.runner_pid (agent run records os.Getpid()); a sweep before agent list/show/pr plus a doctor check flip running rows with dead PIDs to interrupted (pre-migration NULL-PID rows untouched). Pinned by the extended TestAgentRunRecordsLogsDiffAndPRStaleGate (blocked failed run, no push; --allow-incomplete --dry-run proceeds with warning) and TestAgentRunSweepReconcilesDeadRunnerPID (dead/live/NULL PID matrix).
P4-HUB-12 P1 PR #76 (2026-07-04) Hub reclamation closed out: hub migrate-events re-keys the legacy HLC layout into the per-device seq layout (conditional PUT → equal-bytes read-back verify → delete; unparseable/mismatched objects KEPT fail-open; idempotent, --dry-run); an advisory meta/sweep.lock (create-only CAS, TTL by backend mtime, nonce-bound owner-aware release) serializes gc/compact/migrate-events — the single-designated-sweeper caveat is retired; a 412-dedup PutBlob refreshes LastModified AND hub gc re-stats each candidate immediately before deletion, closing the P6-HUB-01 grace-window race end-to-end.
P6-SYNC-02 P2 PR #63 (2026-07-03) Durable, classified pull-drop records (sync_skipped_events, migration 00018): unknown envelope version defers per origin device within sync.key_grant_grace then quarantines (post-upgrade replay recovers; implausible ≤-current versions quarantine immediately); malformed envelopes forward to the undecryptable quarantine; retired-v1/anti-downgrade drops hold their device's cursor with status/doctor surfacing and a fail-closed hub gc refusal; records self-clear on consume (apply or dedup). No --replay-skipped flag by design (held classes self-retry; quarantined classes ride the replay).
P6-DATA-03 P2 PR #61 (2026-07-03) Event emission + derived state commit in ONE transaction at every local site (add, scan --adopt, both conflict_resolve paths, conflicts resolve, keyring Grant/Rotate) via Tx-scoped constructors; Rotate's wrap-first invariant preserved. Deliberately unchanged: ApplyEvents re-apply-on-duplicate (trust-boundary follow-up).
P6-DATA-05 P3 PR #61 (2026-07-03) idx_events_device_hlc serves the device-scoped HLC event scans.
P6-DATA-06 P3 PR #61 (2026-07-03) Partial unique index enforces a single local device row; EnsureDevice race-tolerant (single tx, INSERT ON CONFLICT DO NOTHING, adopt the winner); migration fails loudly on a pre-violating store (remedy in spec/12).
P6-XP-04 P2 PR #62 (2026-07-03) Typed keychain custody: Secret-Service-unreachable recognition lives in platform.mapKeyringError (narrow missing-bus/missing-service signatures only); devicekeys uses typed sentinels; mint paths refuse to mint over a published identity (ErrKeychainUnreachable); per-store custody decision (local_meta, migration 00019) recorded once at init from safe evidence only — an upgraded keychain-backed store first run headless is never stranded — and honored at every key site; keychain custody fails closed; doctor reports the backend.
P6-QUAL-01 P2 PR #60 (2026-07-03) Two-tier mapped-spec drift satisfaction: ** never satisfies; a file with a specific owner requires one of those specs; broad globs (cmd/**, internal/**) cover only ownerless files. The audit's executed probe is a pinned regression test; first genuine catch the same day (PR #61's spec/13 requirement).
P6-QUAL-02 P2 PR #60 (2026-07-03) Release publishing gated on a read-only verify job: refname-anchored main/release ancestry, go vet, race tests, CI's pinned govulncheck, headless-keychain env + timeout. Manual v* tag-protection ruleset remains an operator step; cosign/SLSA/SBOM stay under P4-SEC-05/P4-QUAL-05.
P6-QUAL-05 P3 fix/p6-qual-05 (2026-07-03) CI now runs on main pushes, pull requests, and the existing daily schedule instead of every branch push plus PR; workflow-level concurrency cancels superseded in-progress PR runs while preserving non-PR runs. Validated with Python workflow YAML parsing and the Pass-6 ledger recount.
P6-CLI-01 P2 PR #72 (2026-07-03) init now compares the existing workspace root to the effective resolved requested root (DEVSTRAP_ROOT, --root, or positional [root]) before EnsureWorkspace; mismatches refuse with exitConflict naming both roots and the --move-root remedy. --move-root performs the relocation and rewrites config.yaml atomically (same-directory temp + rename, 0600) so config and DB agree; same-root re-init stays a no-op success.
P6-SEC-03 P2 fix/never-granted-epoch-grace (2026-07-03) Never-granted epochs no longer truncate sync forever: the missing-key defer is grace-bounded (sync.key_grant_grace, default 72h, 0 = immediate; first sighting persisted in key_grant_waits (migration 00015) with the epoch's earliest first-seen as the clock, so hostile kid relabeling cannot restart it); past the window the carrier quarantines as a replay-recoverable undecryptable conflict and the cursor advances; ReplayUndecryptableConflicts now runs BEFORE the batch applies (one-cycle recovery) and a late-applying successor auto-resolves its event_hash_chain_break conflict; devices approve/enroll --approve gain the epoch-contiguity guard (--allow-epoch-gap override; keyless pinning ceremony exempt); doctor warns awaiting key grants. E2e-pinned by sync_never_granted_epoch_wedge.txtar. Residual (documented): a rotator grants only locally-known approved devices — unknown fleet devices ride grace→quarantine→replay until re-approved; old-epoch containment documented-not-built.
P6-CLI-05 P3 fix/p6-cli-05 (2026-07-03) README project-status/features/quickstart/roadmap now document the shipped hub: r2://<bucket> + DEVSTRAP_HUB_S3_* path (with hub login/op:// custody and a spec/19 link); both init next-steps hints teach configuring the hub in config.yaml; sync --dry-run prints the resolved hub ID instead of an empty target. Non-goal: no init --hub flag.
P6-DOC-02 P2 audit PR #28 Ledger P4-SEC-05 contradiction + convention-#3 violation reconciled in the PR that landed the audit. Fully applied.
P6-DOC-03 P3 audit PR #28 spec/00 re-drift (planned-sync comment, command/test inventories) fixed in the PR that landed the audit. Fully applied.
P6-XP-01 P2 fix/p6-xp-01 (2026-07-03) ShouldPruneDir's bare-name fallback deleted; relSlash is now the sole match target (empty-path guard kept only for a caller with no path). Both live callers (scan.Walk, draftbundle.Pack) already pass the true root-relative path, so no caller changes were needed. Anchored patterns (/dist/) no longer prune same-named nested directories and negations (!keep/build/) are honored. Pinned by TestShouldPruneDirAnchoredPatternDoesNotPruneNested/...NegationReincludes/...RootLevelStillPruned.
P6-DOC-04 P3 PR #71 (2026-07-03) Specific spec ownership is complete for every top-level internal/ package. The earlier audit PR added internal/workspacekeys/** to spec/07/09/15; this follow-up added the real-repo regression gate plus the missing specific owners for internal/config/** (spec/03), internal/id/** and internal/pairing/** (spec/07), and internal/specdrift/** (spec/16).
P6-XP-05 P2 fix/p6-xp-05 (2026-07-04) scan is now offline: new Runner.LocalDefaultBranch resolves the default branch from local refs only (symbolic-ref refs/remotes/origin/HEAD, then a local origin/<fallback> rev-parse) and never runs set-head --auto/ls-remote; scan.Walk calls it and emits a non-authoritative "resolved authoritatively at materialization" warning instead of a per-repo network round-trip inside the WalkDir callback. Authoritative set-head --auto repair stays on the hydrate/worktree path (which resolves at use time). A many-repo tree no longer stalls onboarding for minutes-to-hours offline. Pinned by TestLocalDefaultBranchReadsLocalSymbolicRefWithoutNetwork/...FallsBackToStoredRefOffline/...ErrorsWhenNoLocalRefOffline (blackhole-remote no-network guard) + TestScanResolvesDefaultBranchOfflineAndWarns.
P6-XP-02 P2 fix/p6-xp-02 (2026-07-04) The .devstrapignore/gitignore compiler now matches real gitignore semantics on the draft-sync data plane: parseLine anchors on a leading or middle separator (not only a leading /); patternToRegex translates bracket classes to real regex classes (leading !/^ → negated class that also excludes / per fnmatch FNM_PATHNAME, escaping \/]) and degrades an unclosed [ to a literal \[ instead of failing Compile (no more hard-failed draft snapshot create); and only a whole-segment run of ** (2+ stars, slash-bounded) crosses / recursively like git's wildmatch — a non-standalone a**b collapses to a single *. Behavior-preserving: the built-in default patterns with a middle slash (data/raw/, data/interim/, .devstrap/tmp/, .devstrap/cache/) gained an explicit **/ prefix so they keep pruning at any depth (project-level, not just the scan root) under the corrected anchoring — TestMatchDefaults + internal/scan TestShouldPruneDir. Pinned by a git check-ignore --verbose differential test (ignore_gitdiff_test.go) + TestCompileDoesNotFailOnUnclosedBracket/TestAnchoredMiddleSlashDoesNotMatchNested.
P6-GIT-04 P2 fix/p6-git-04 (2026-07-04) The eager materialize/hydrate path now honors the stored lfs_policy (previously only the worktree path did, so an lfs-policy=always repo materialized as silent pointer files recorded available/clean). New Runner.LFSInstallLocal (git lfs install --local, required because gitEnv sets GIT_CONFIG_GLOBAL=/dev/null, hiding the user's global smudge filter) + applyMaterializeLFSPolicy (mirrors applyWorktreeLFSPolicy: always/agent → install+pull, fail the project on error; auto/never → warn). Applied in materializeGitRepo (the caller), NOT inside hydrateProjectUnlocked — which is shared with worktree creation — so it runs on the fresh clone AND on the SkeletonProjects retry of a repo recorded "failed" (review-caught: the retry hit the already-on-disk early-return and silently flipped "failed"→"available" with pointers). LFSPull carries the P6-GIT-01 long-transfer timeout. Pinned by TestMaterializeLFSAutoRecordsAvailableWithPointers/...AlwaysRecordsFailedWhenPullFails/...AlwaysDoesNotFlipFailedToAvailableOnRetry/...NoLFSUnaffected.
P5-ARCH-01 P3 fix/p5-arch-01 (2026-07-04) Pure Decide(state,event) extraction complete — the reconciler's convergence core is now property-testable. New internal/sync/decide.go: a Projection/ProjectionRow value type + pure Decide(proj, event) → (mutations, conflicts) (no DB/IO/*state.Tx) reusing the already-pure reconcileSamePath/samePathLess. applyEventTx reduces to load-projection → DecideapplyDecisionTx for project.added/updated/deleted; project.renamed (identity-preserving in-place re-key), conflict.*, draft.snapshot.created, device.key.granted stay inline by design. No behavior change — every existing apply_test.go/hlc_test.go passes unchanged (opus-verified). New decide_property_test.go folds Decide over all 8! permutations of a fixed event set asserting an identical final Projection + duplicate-delivery idempotency (stdlib-only Heap's-algorithm generator). Unblocks P4-QUAL-02. Residual (documented, review-surfaced): the extraction exposed a pre-existing delete-vs-re-add order-sensitivity — a delete tombstones unconditionally with its own HLC while a re-add is gated only against the tombstone HLC, so D@5 then A@10 diverges from A@10 then D@5 (a real strong-eventual-consistency gap, the one interaction the property set deliberately excludes). Not introduced here and not a regression; flagged as a follow-up (candidate fix: gate the delete against the live row's source-event coords). Residual FIXED 2026-07-04 (fix/decide-delete-readd-convergence): decideDelete now keeps a live row whose source-event HLC is strictly above the delete (bare-HLC, mirroring importTombstoneTx's pinned equal-HLC-ties-favor-the-delete rule, so import stays equivalent to replay); pinned by TestDecideConvergesDeleteReaddMix (all 5! delivery orders of an add/delete/re-add mix) and TestApplyEventsStaleDeleteDoesNotDestroyNewerAdd (both pull-window orders + the equal-HLC tie through the real apply path). Remaining (review-surfaced, pre-existing): the different-remote reconcile winner is lowest-coordinate, not HLC-monotonic, so a delete mixed with a same-path/different-remote pair can still diverge by order — folded into the P4-QUAL-02 scope. That last residual FIXED 2026-07-04 (fix/reconcile-same-path-hlc-monotonic): reconcileSamePath now installs the highest (HLC, deviceID, eventID) coordinate — nothing about the P5-ARCH-01 seam remains open.
P4-QUAL-02 P1 feat/p4-qual-02-properties (2026-07-04) Property/model-check layer over the pure Decide/Projection seam (P5-ARCH-01→PR #87→here), on the test-only pgregory.net/rapid dep (zero transitive deps, adopted per this finding's explicit ask). Four rapid suites + a fuzz bridge: (1) hlc_property_test.go — Send/Receive interleavings under a backward-stepping INJECTED clock prove strict Send monotonicity, Receive non-regression, the EXACT MaxSkew accept/reject boundary, and logical-overflow carry; (2) decide_rapid_test.go — randomized convergence: two independent delivery permutations of a generated event set fold to one Projection + duplicate idempotency, wrapped as coverage-guided FuzzDecideConvergence (go test -fuzz; a 30s ubuntu CI step); (3) import_replay_property_test.goBuildSnapshotImportSnapshot then subset-replay ≡ full replay (active rows); (4) replica_model_test.go — the audit's 3-replica model check: independent orders SPLIT into sequential ApplyEvents batches (cross-pull-window realism, where the pre-#87 divergence hid) converge byte-identically, with duplicate re-delivery (idempotency) and a tombstone-GC interleaving (active-rows-only invariant, since GC purges only deleted rows). The generator is confined to the provably-convergent class by TWO witness-pinned exclusions. New finding surfaced here (beyond P5-ARCH-01's documented delete residual): same-path/different-remote convergence is order-dependent with NO delete involved whenever one remote carries multiple events — same-remote LWW keeps that remote's HIGHEST HLC while cross-remote reconcileSamePath keeps the LOWEST coordinate, so the winner flips by delivery order. Both divergences share the lowest-coordinate reconcile root cause and are pinned by TestDecideDifferentRemote{Delete,MultiEvent}DivergesWitness tripwires that FAIL the day reconcileSamePath is made LWW-consistent, forcing the matching generator exclusion to be removed. Follow-up SHIPPED 2026-07-04 (fix/reconcile-same-path-hlc-monotonic): the winner is HLC-monotonic (highest (HLC, deviceID, eventID), consistent with same-remote LWW and importEntryTx); both tripwires fired as designed and were retired with their exclusions, and genEventSet now draws the full event space (deletes + multi-event remotes across different-remote paths).
P6-XP-03 P2 fix/p6-xp-03 (2026-07-04) run-loop now runs its advertised scan stage (chosen over the doc-only fix): runLoopTick prepends an idempotent scan+adopt before runSyncCycle, restoring the local→hub auto-path the daemonless loop otherwise lacked. Idempotency (the hazard: adoptFindings unconditionally emits project.added per finding) is handled by findingAlreadyAdopted — a finding is adopted only when store.ProjectByPath has no active row matching its type and (for git_repo) remote_key; adoptNewFindings filters then delegates to the existing adoptFindings, so one-shot scan --adopt is unchanged. Warning-class findings (secret-looking files, symlink escapes) and duplicate-remote findings are routed to stderr and NEVER auto-adopted (duplicates are dropped in the loop). The stderr tick header + Short/doc/README/spec-00 wording are reconciled to "scan + sync + materialize." Pinned by TestRunLoopScanAdoptIdempotentAndPicksUpNewRepos (4 ticks, no duplicate events + mid-run pickup), ...SkipsDuplicateRemotes, ...WarnsSecretWithoutAdopting, and an extended run_loop_once.txtar (pushed 2 → pushed 0).
P6-DATA-04 P2 fix/p6-data-04 (2026-07-04) devstrap db backup --full <out.tar> now captures a recoverable workspace: state.db (VACUUM INTO), the blobs/<sha256>.age files AllBlobRefs reports, the key material (keys/ under file custody; a custody-aware keychain escrow — device age + Ed25519 signing identities + every held WCK epoch from HeldKeys + hub S3 creds via HybridStore.ExportForBackup, a hard error if any required key is unreadable), AND config.yaml (the hub pointer + custom root). New devstrap db restore <in.tar> refuses a non-empty state dir without --force, validates the staged state.db (quick_check+foreign_key_check) before promoting, and swaps ONLY the captured targets in place (temp+rename+rollback) so un-captured Home data (quarantine/, logs/) survives; a zip-slip guard (sanitizeBackupEntry) rejects abs/../symlink/out-of-layout entries. New doctor dangling-blob-refs check + a keychain-custody restore warning; the doctor "restore from a db backup" remedy now points at --full. Round-trip test proves env capture → backup --full → wipe → restore → env hydrate recovers the identical secret AND config hub/root survive; blocking review finding (config.yaml omission / whole-Home wipe) fixed. spec/12 disaster-recovery runbook.
P4-GIT-04 P2 feat/p4-git-04-worktree-gc (2026-07-06) worktree cleanup --merged now also reaps squash/rebase-merged worktrees: new git.Runner.IsSquashMerged — offline content-equivalence via the current-tree merge probe (git merge-tree --write-tree <base> <branch>, git ≥ 2.38: merged iff the simulated merge's tree equals the CURRENT base tree). The first-draft git cherry/patch-id chain was replaced in dual review — historical patch-id equivalence matches merged-then-REVERTED changes and would reap unmerged work (both reviewers reproduced it). Conservative rule: conflicting merge / old git / any error = not merged. Documented accepted limitation: a coincidentally-identical unrelated commit reads as merged (tripwire-pinned); every reap prints the deleted branch's tip SHA as the recovery breadcrumb. Ancestry stays the first check; misses consult content-equivalence, labeled merged (squash); base fetched under the repo lock, deduped per project/ref; reaped branches get git branch -D (warn-only). Forge-API (gh pr list) cross-checks are an explicit non-goal — cleanup works offline. Pinned by the real-git TestIsSquashMerged* matrix (incl. revert-on-base) + CLI e2e TestWorktreeCleanupReapsSquashMergedWorktree.
P7-SYNC-01 P1 fix/p7-sync-01 (2026-07-10) Device revocation now survives event-log compaction: snapshot.v2 carries terminal device-trust rows (trust: [{device_id, state}], revoked/lost only, State-only — the apply is sticky/monotonic so coordinates are unnecessary and the revoke event may already be compacted on the builder), BuildSnapshot reads them from devices, and ImportSnapshot re-derives them in the same tx via ApplyRemoteDeviceTrustTx (never flips local; flags needs_rotation once on an actual change; a malformed row aborts the whole import). Version bump is fail-closed both ways for the snapshot (old binaries refuse v2; v1 snapshots are refused with a hub compact remedy) while retention manifests stay readable at v1 (trust-neutral floors; the first upgraded compactor must reconcile the pre-existing manifest). E2e hub_compact_trust_recovery.txtar pins the full scenario: revoke → compact (revoke event + offline device's epoch-2 grant both deleted) → offline device returns, defers keyless, is re-approved (grants land above the floor) → snapshot recovery lands B REVOKED with rotation flags. Corrected the spec/07 "whole fleet learns the decision"/"fleet-wide" claims (pre-fix they held only for devices online across the revoke-to-compaction window).
P7-HUB-05 P3 fix/p7-hub-05 (2026-07-10) Folder/git fsObjectStore mutable object writes use same-dir temp + fsync + rename (writeFileAtomic); timestamp sidecars too; post-review: stale .tmp- orphan reclamation, tear-proof concurrency test, best-effort parent-dir fsync. Torn retention.json no longer fail-closes every Pull. Residual: cloud-drive mid-replication window still documented in spec/15.
P7-SEC-01 P2 fix/p7-sec-01 (2026-07-10) Sandbox credential deny-list gains ~/.config/gcloud (GCP refresh tokens), ~/.azure (Azure CLI tokens), and ~/.git-credentials (git's plaintext credential.helper store — the .gitconfig that WAS masked merely points at it). Added to the single sensitiveHomeDirs/sensitiveHomeFiles source in internal/platform/sandbox_profile.go, so the Seatbelt profile, bubblewrap masks, credentialAnchors, and readConfineRoots all inherit them; the wrapper-level denyParts in agent.go gains the same three for parity. Regression-pinned by TestBwrapSensitivePathsCoversCloudAndGitCredentials and TestCredentialAnchorsCoverCloudAndGitCredentials. spec/05/10/15 enumerations updated.
P7-QUAL-03 P2 fix/p7-qual-03 (2026-07-10) The Ubuntu release job now rejects partial macOS notarization configuration before GoReleaser with a 0-or-5 gate over all five MACOS_* secrets, reporting only set/missing names. GoReleaser's dormant isEnvSet "MACOS_SIGN_P12" activation is unchanged. Because Ubuntu cannot run spctl, RELEASING.md requires a manual post-release Gatekeeper assessment of the extracted darwin binary on a Mac before rc promotion or cask update.
P7-CLI-01 P2 fix/p7-cli-01-json-stream (2026-07-11) db backup --full --json and db restore --json no longer emit raw warning: text before the JSON payload. Warnings (missing blobs, no key material, no config, keychain-custody restore guidance) are carried in the result struct's warnings array and printed only in the human render branch; the entire stdout under --json is one parseable document.
P7-GIT-01 P2 fix/p7-git-01-cleanup-safety (2026-07-11) worktree cleanup --merged no longer reaps a live agent worktree: RunningAgentRunsByWorktree blocks on any still-running agent_runs row (no PID required); stale-PID sweep runs first; project repo lock held across dirty-check → base-refresh → merge checks → dirty TOCTOU re-check → remove → branch -D → DB mark; lock conflict skips (warn) rather than failing the sweep. Subsumes the P7-GIT-02 lock gap.
P7-GIT-02 P2 fix/p7-git-01-cleanup-safety (2026-07-11) Cleanup path-present mutations now hold acquireRepoLock for the whole sequence (was only the base fetch inside refreshWorktreeBase). Split refreshWorktreeBaseLocked avoids nested-lock deadlock.
P7-CLI-02 P2 fix/p7-git-01-cleanup-safety (2026-07-11) worktree cleanup sets Args: usageArgs(cobra.NoArgs) so a stray positional cannot be silently discarded on a fleet-wide destructive sweep.
P7-GIT-03 P2 fix/p7-git-03-pid-reuse (2026-07-11) Repo locks and agent-run crash sweeps now pair a PID with an opaque platform start-time identity. Linux records raw /proc/<pid>/stat field 22 and macOS records kern.proc.pid start time in microseconds; a live PID with a different identity is treated as recycled, while a missing identity or failed lookup preserves conservative legacy behavior. Migration 00024 adds agent_runs.runner_started_at. Pass-7 arithmetic: open 41→40; P2 22→21.
P7-SEC-04 P3 fix/p7-sec-04-osroot (2026-07-11) Carrier object access is confined by per-operation os.Root handles (per-component O_NOFOLLOW + symlink-target recheck) instead of the check-then-use Lstat-walk safePath: reads, writes (incl. the P7-HUB-05 atomic temp+fsync+rename, now writeRootFileAtomic), stats, deletes, and timestamp sidecars all resolve through the handle, and the folder carrier additionally pins root identity (os.SameFile against the construction-time directory) after each OpenRoot, closing the swap window on a concurrently-writable shared root. Both carriers covered by post-construction symlink-swap refusal tests.
P7-HUB-02 P2 fix/p7-hub-02-head-continuity (2026-07-11) The git carrier persists the last verified head + retention-manifest fingerprint in head.json; a non-descendant head is accepted only as plausible compaction (byte-identical or strictly-advancing manifest AND, when the prior head is locally known, no event object at or above the new floors deleted — the content gate); a rewound or deleted branch is refused with a named rm -rf <cache> recovery path instead of being silently re-founded. CommandError gains ExitCode(). Review: identical-fingerprint acceptance + the content gate replaced the strict-advance-only rule that falsely refused real multi-device compaction.

Pass 7 (2026-07-10) — 28 open of 47

Full detail and file:line evidence in AUDIT_RECOMMENDATIONS_2026-07-10_PASS7.md. The header count equals the rows below (P1=0, P2=12, P3=16 = 28; P7-SYNC-01 (the P1), P7-SEC-01, P7-SEC-02, P7-QUAL-03, P7-HUB-05, and P7-SYNC-04 shipped 2026-07-10, and P7-CLI-01, P7-GIT-01, P7-GIT-02, P7-CLI-02, P7-GIT-03, P7-HUB-02, P7-DATA-03, and P7-DATA-04, and P7-DATA-05 shipped 2026-07-11 — see Recently shipped). Two candidate findings were merged and are not counted twice: the revocation-compaction finding into P7-SYNC-01, and the post-revoke rotation-flag finding into P7-SEC-02.

ID Sev Finding Effort
P7-QUAL-02 P2 curl|sh installer verifies only sha256, ignoring the cosign bundle + SLSA provenance it ships (extends P4-SEC-05) M
P7-QUAL-01 P2 Stable promotion rebuilds and publishes artifacts that were never smoke-tested as release candidates L
P7-QUAL-04 P2 Shipped devstrap service install has no live launchd/systemd end-to-end gate in CI M
P7-XP-01 P2 service install pins the Homebrew Cellar path; brew upgrade silently bricks the unit M
P7-XP-02 P2 service install never checks key custody; a keychain-custody store strands the headless run-loop M
P7-XP-03 P2 systemd service uninstall requires a live --user manager; headless/SSH cannot remove a unit status still reports S
P7-XP-04 P2 Ignore matcher is byte-exact (no NFC); macOS NFD readdir vs NFC patterns break gitignore-parity pruning S
P7-PROD-01 P2 Two-device pairing is the onboarding cliff — 8 cross-machine commands + 2 codes + 2 out-of-band fingerprints; no up/pair wizard M
P7-PROD-02 P2 Zero-knowledge hub has no server-side lifecycle/quota enforcement; a key-less operator can't bound a tenant (extends P4-HUB-15) L
P7-PROD-03 P2 No version-skew policy; fail-closed snapshot/envelope checks wedge a brew-upgraded mixed-version fleet M
P7-PROD-04 P2 No control-plane identity; workspace_id is client-minted with no account/device/quota binding (extends P4-SEC-08) L
P7-DOC-01 P2 Shipped devstrap service + OS sandbox described as unbuilt/future/advisory across six files M
P7-SEC-03 P3 --sandbox require fails closed for network/read-confine but not credential reads on the Landlock fallback S
P7-SEC-05 P3 TRUST-01 gives one compromised approved device a fleet-wide revocation + rotation-lockout DoS (threat-model doc gap) S
P7-SYNC-02 P3 TRUST-01 makes convergence delivery-order-dependent for a revoked device's pre-revocation events M
P7-HUB-01 P3 Git carrier does one fetch+commit+push per hub object, not per sync cycle (history bloat + push churn) M
P7-HUB-03 P3 Carrier local caches grow unbounded — clone never git gc'd, observed.json never prunes compacted keys (folds in host-GC-lag) M
P7-DATA-06 P3 Env-sync/rewrap reference scans lack blob-reference indexes (toward quadratic on large fleets) S
P7-CLI-03 P3 hub init / service install confirmations route through progressf, so --quiet prints nothing for a state-changing action S
P7-XP-05 P3 service status/doctor never validate that the baked ExecPath still exists S
P7-XP-06 P3 Ignore matching is always case-sensitive, diverging from git on default macOS (core.ignorecase); undocumented S
P7-XP-07 P3 macOS Seatbelt Available() is stat-only; Linux backends launch-probe (weaker resolve-time honesty) S
P7-QUAL-05 P3 A single aggregate coverage floor can mask a package-local regression (no per-package floors) M
P7-QUAL-06 P3 Two fuzz corpora (FuzzParseBytes, FuzzCompile) are never exercised in CI S
P7-PROD-05 P3 No client-side telemetry seam for opt-in MAU/sync metrics (extends P4-HUB-14) M
P7-PROD-06 P3 No team/org trust surface — org concept, audit export, SSO (extends P4-SYNC-08) L
P7-DOC-02 P3 Ledger self-consistency: stale Pass-5 count + under-enumerated invariant note (fixed in this pass's PR) S
P7-DOC-03 P3 last_reviewed frontmatter not bumped on substantive edits, so the staleness signal is unreliable S

Pass 6 (2026-07-01) — 0 open of 43 — PASS CLOSED; all five P1s shipped; quick-win, sync-convergence, and snapshot-exchange waves complete

The header count equals the rows in the table below (CodeRabbit, PR #39): 43 findings − 43 shipped P6 rows in Recently shipped = 0. Only rows whose ID begins with P6- count toward Pass 6's 43; every non-P6- row in Recently shipped (the P4-* and P5-* findings — 16 of them as of 2026-07-10) is a Pass-4/Pass-5 finding tracked in its own pass's table and does not count here. The 43 include the 3 fully-applied doc fixes P6-DOC-02/P6-DOC-03/P6-DOC-04. P6-SYNC-01, P6-SEC-01, P6-SEC-02 (PRs #30–#34), P6-DATA-01 (PR #35), P6-HUB-01 (PR #36), P6-GIT-01 (PR #37), P6-GIT-02 (PR #66), P6-SYNC-03 (PR #38), P6-DATA-02 (PR #39), P6-CLI-02 (PR #40), P6-GIT-05, P6-SYNC-04 (PR #44), P6-QUAL-03 (fix/p6-qual-03), P6-SEC-03 (fix/never-granted-epoch-grace), the sync-convergence wave's P6-SYNC-02 (PR #63), P6-DATA-03/P6-DATA-05/P6-DATA-06 (PR #61), P6-XP-04 (PR #62), P6-QUAL-01/P6-QUAL-02 (PR #60), P6-XP-01 (PR #67), P6-QUAL-05 (PR #68), P6-GIT-03 (PR #69), P6-QUAL-04 (PR #70), P6-DOC-04 (PR #71), P6-CLI-01 (PR #72), P6-HUB-04 (PR #75), P6-HUB-03 (fix/p6-hub-03), P6-CLI-03 (fix/p6-cli-03), P6-CLI-04 (fix/p6-cli-04), P6-DOC-01 (fix/p6-doc-01-test), P6-XP-06 (fix/p6-xp-06), P6-GIT-06 (fix/p6-git-06), P6-XP-05 (fix/p6-xp-05), P6-XP-02 (fix/p6-xp-02), P6-GIT-04 (fix/p6-git-04), P6-XP-03 (fix/p6-xp-03), and P6-DATA-04 (fix/p6-data-04) moved to Recently shipped above per convention #3.

None — Pass 6 is fully closed (43/43 shipped; the last finding P6-GIT-06 shipped 2026-07-04).

Pass 5 (2026-06-29) — 35 of 36 shipped, 1 open: P5-CLI-01

Count corrected 2026-07-10 (P7-DOC-02). Prior revisions of this header (and the index row) disagreed with each other ("34 shipped / 2 open" vs "33 shipped / 3 open") and with the code. A row-by-row re-verification against spec/18_WORK_LOG.md (the 2026-06-30 fix/pass5-backlog wave landed 32; later waves landed the rest) and the source tree confirms 35 of 36 findings shipped; only P5-CLI-01 remains open. The contextcheck linter formerly listed as a second open item is a sub-item of P4-QUAL-07 (see the Pass 4 table), not a Pass-5 finding, and does not count here. Verified-shipped spot-checks: P5-SEC-01 (revoke rewrap emits a superseding draft.snapshot.created before deleting the old ciphertext, devices.go), P5-QUAL-01, P5-HUB-02 (PruneDraftSnapshots), P5-CLI-02 (--partial wired), P5-PROD-05 (doctor --remote + status --watch), P5-DATA-02 (migration 00012). The 34 shipped findings not already in Recently shipped landed in the fix/pass5-backlog wave and later cycles; consult spec/18_WORK_LOG.md for per-finding history.

ID Sev Finding Effort
P5-CLI-01 P2 Introduce a Renderer; honor --json everywhere (or reject it) — the render seam landed and is wired into materialize + db backup, but the rollout to every leaf command remains (verified 2026-07-10: seam present in ~2 files only). Extended by P7-CLI-01 (a new JSON-stream break on the db backup/restore commands it is wired into). M

Pass 4 (2026-06-28) — still open (re-prioritized in PASS5 Appendix A); P4-SYNC-02/P4-HUB-11 shipped 2026-07-04 (snapshot-exchange wave), P4-QUAL-02 shipped 2026-07-04 (property/model-check layer) — both moved to Recently shipped

ID Sev Finding
P4-SEC-02 Shipped PR #25 — moved to Recently shipped above (was here in violation of convention #3; fixed by P6-DOC-02).
P4-SEC-04 Shipped 2026-07-03 (parts 1+2, PRs #54/#57) — moved to Recently shipped above.
P4-SEC-05 P1 Sign release binaries — narrowed to notarization-only 2026-07-05: cosign keyless signing + SBOMs (PR #115), SLSA v1 provenance (PR #117), and the dormant notarize config + enrollment runbook (PR #119) are shipped and LIVE-VERIFIED on v0.1.1-rc.1/v0.1.1 (cosign Verified OK at the workflow identity, slsa-verifier PASSED on all four tarballs, checksums 4/4, tap upgrade 0.1.0→0.1.1). Remaining open scope: Apple Developer ID signing + notarization — enrollment in progress; the notarize.macos block self-activates when the five MACOS_* secrets land, then the cask quarantine-strip hook is removed (deadline: Homebrew drops Gatekeeper-failing casks 2026-09-01)
P4-SEC-07 Shipped 2026-07-03 (foundation PR #25 + pairing wave + periodic rotation PR #56) — moved to Recently shipped above.
P4-SEC-08 P2 Hosted-mode prefix-scoped/temporary credentials + object immutability
P4-SYNC-03 P2 Raise epochFloorMS above 0; past-direction quarantine
P4-SYNC-05 P2 Folded running hash chain + signed per-device head
P4-SYNC-06 Shipped 2026-07-04, narrowed (ack wave: ack-gated tombstone GC + revoked-stream cleanup) — moved to Recently shipped above. The per-peer sync_cursors/event_delivery tables stay dead, superseded by the signed ack plane (not built by design).
P4-SYNC-07 P3 MaxOpenConns(1) serializes all WAL reads behind the single writer
P4-SYNC-08 P3 Unblock the multi-workspace future + sign re-stamped workspace-id binding
P4-HUB-14 P2 Emit hub metrics/traces + op/byte counters (narrowed 2026-07-04: the doctor --remote health-probe half shipped via P5-PROD-05; the metrics/op-byte-counter half remains fully open — no counters exist anywhere in internal/hub)
P4-HUB-15 P2 Cost controls, quotas, rate limiting
P4-HUB-16 P2 At-rest versioning/Object-Lock + backup/replication runbook
P4-GIT-03 Shipped 2026-07-05 — OS-enforced agent sandbox complete: macOS Seatbelt + Linux bubblewrap + Linux Landlock fallback (write confinement, credential denies, policy-scoped network deny), the Linux seccomp syscall denylist, unsigned local sandbox.violation telemetry, and tighter read confinement (--read-confine, default-on for readonly) — moved to Recently shipped above. The three named remaining items (seccomp, sandbox.violation telemetry, tighter read confinement) are all shipped; containerization stays a separate spec/14 roadmap direction, not a P4-GIT-03 remainder.
P4-GIT-04 Shipped 2026-07-06 (feat/p4-git-04-worktree-gc) — moved to Recently shipped above.
P4-GIT-07 P3 Persisted per-project materialize failure record, resume, progress detail
P4-QUAL-04 P2 Enforce coverage in CI + add a Windows build (narrowed 2026-07-05: the coverage half shipped — ci.yml generates coverage.out and gates a 50% go tool cover -func floor, closing P5-QUAL-04; remaining open scope is the Windows build only — internal/platform build tags are already windows-clean, known runtime risk: the syscall.Signal(0) liveness probe in internal/cli/repo_lock.go)
P4-QUAL-05 Shipped v0.1.1 (2026-07-05) — moved to Recently shipped above.
P4-QUAL-07 P3 Enable resource/context-leak linters (bodyclose, sqlclosecheck, contextcheck, …)
P4-PROD-04 Shipped 2026-07-06 (feat/p4-prod-04-service-install) — devstrap service install|uninstall|status (LaunchAgent/systemd unit wrapping run-loop) — moved to Recently shipped above.
P4-PROD-05 Shipped v0.1.0 (2026-07-05) — moved to Recently shipped above.

Pass 3 (2026-06-28 cloud-sync) — residual

ID Status
SCALE-* Documented-not-built (multi-user hosting/scaling direction; Fly.io + R2 + managed Postgres). Future/strategic.

Passes 1–2 (AUDIT_RECOMMENDATIONS.md, ..._2026-06-27.md) are largely implemented or superseded by the cloud-sync re-baseline; consult spec/18_WORK_LOG.md for the shipped history before treating any pass-1/2 finding as open.