# M6.6 — M6 composition gate | Field | Value | |---|---| | Phase | M6 — agent-manager migration | | Size | M — 1–3 days | | Status | ⬜ Not started | | Flags | gate | | Spec | inlined below | | Blocks | all of M6 | ## Goal Prove the schema, the query port, the network path, and the credentials bind together into one working system — not four pieces that each passed their own task in isolation. This is the property no single M6 task owns: M6.1 proves the cluster is healthy, M6.2 proves the schema applies, M6.3 proves the queries work against a local test database, M6.4 proves a raw TCP connection reaches the cluster, M6.5 proves the credential decrypts — none of them alone proves agent-manager, running for real on the Mac, through the real ingress route, with the real ksops credential, against the real cluster, does its actual job: track a session end-to-end without losing or corrupting data. ## Facts (inlined — no spec read needed) **"Bind together smoothly" means two concrete things here, not a vibe:** 1. **The full network path is exercised, not simulated.** M6.3's tests run against a disposable local Postgres — that validates the SQL, not the route. This gate is the first (and only) task that runs agent-manager unmodified, on the actual Mac, through M6.4's nginx `stream {}` route, authenticating with M6.5's ksops-sourced credential, against M6.1's real cluster. 2. **Schema conventions match the rest of this homelab, not just "compiles."** This project's own `memory-db` (M2.x) and `agent-manager-db` (M6.x) are two unrelated Postgres schemas landing in the same cluster around the same time. They don't share data or a cluster (M6.1 already ruled that out), but a reviewer scanning `k8s/infra/databases/` should find the same shape twice: same `Cluster`/`Database` CRD structure, same `storageClass`, same `enableSuperuserAccess: false`, same GitOps-only provisioning discipline. "Binds together smoothly" includes that consistency check, not just agent-manager working in isolation. **What a session round trip actually touches**, so the test isn't shallow: `CreateSession` (writes `sessions` + touches `groups` via the `ON CONFLICT DO NOTHING` insert) -> `UpdateStatus` -> `SetAgentSessionID` -> `SetReviewRepo` (writes `review_targets`) -> `Delete` (must cascade `review_targets` via the FK, per M6.2's decision, with no leftover row). That single flow crosses all 4 non-`settings` tables and exercises both the FK-cascade decision and the placeholder-conversion correctness from M6.3 in one pass. ## Steps 1. On the Mac, with agent-manager built from the fully-ported `add-headless-spawn` branch (M6.3 complete) and configured to use M6.4/M6.5's route and credential: run the `spawn` CLI subcommand to create a real session. 2. Drive it through the full lifecycle above (status update, agent session id capture, review target set, delete) using agent-manager's own CLI/TUI, not a hand-rolled SQL script — the point is proving the actual client works, not that Postgres accepts hand-written SQL. 3. Kill and restart agent-manager mid-lifecycle (after step 2's status update, before delete); confirm it reconnects and reads back the same state — proves the connection isn't accidentally caching state client-side that masks a write that never actually landed. 4. Diff `k8s/infra/databases/agent-manager-db.yaml` against `k8s/infra/databases/memory-db.yaml` field-by-field for the convention-consistency check. 5. Confirm both ArgoCD Applications (agent-manager's and this project's `memory-db`, once it exists) are tracked from the `repoURL` each actually watches — re-verify per M6.1's caveat, since this is the final point where a "pushed but ArgoCD never saw it" mistake would otherwise go unnoticed until much later. 6. Commit `expected/m6.6.txt`; diff. ## Acceptance - A session created, updated, and deleted through agent-manager's real CLI on the Mac round-trips correctly through the full network path. - A mid-lifecycle restart does not lose or duplicate state. - Deleting the session leaves zero orphan rows in `review_targets`/ `review_bases`/`review_scopes` (FK cascade, not app-level cleanup). - `agent-manager-db.yaml` and `memory-db.yaml` match on every field that isn't inherently app-specific (name, storage size). - No manual `kubectl apply`/`psql` anywhere in the setup this gate exercises. ## Verify **Harness:** the real Mac client, the real cluster, agent-manager's own CLI — this gate deliberately does not use a disposable/local database, since proving the disposable path works is exactly what M6.1-M6.5 already did. **Integration test** — `verify/m6.6.sh` diffed against `expected/m6.6.txt`: 1. `a1_full_roundtrip` — create/update/set-review/delete via the real CLI; assert no error at any step. 2. `a2_no_orphan_review_rows` — after delete, query `review_targets`/ `review_bases`/`review_scopes` directly (from inside the cluster, as a final-state check) for the deleted session's id; assert zero rows, with no explicit `DELETE FROM review_*` having been issued by the CLI (proves the FK cascade did the work, not leftover manual-cleanup code nobody removed). 3. `a3_survives_restart` — kill agent-manager between status-update and delete; restart; assert the status update is still visible before proceeding to delete. 4. `a4_schema_convention_match` — diff the two `Cluster` manifests' non app-specific fields; assert empty diff. 5. `a5_repourl_confirmed` — for each of the two ArgoCD Applications involved, print which `repoURL` it watches and confirm it matches which remote was actually pushed. 6. `a6_no_manual_apply_in_history` — review the shell history / session log from M6.1 through M6.5 for a `kubectl apply` or `psql` write command that wasn't inside an explicitly-flagged debugging exception; assert none exist outside that exception. **Command:** `bash verify/m6.6.sh | diff - expected/m6.6.txt` **False pass:** - Running assertion 1 against M6.3's disposable local Postgres instead of the real cluster because it's faster/already running. That's exactly the "four pieces that each passed in isolation" failure mode this gate exists to catch — it must hit M6.1's actual cluster through M6.4's actual route. - Treating a schema diff (assertion 4) as advisory and skipping it when short on time. A convention mismatch here is invisible today and becomes the thing a future reviewer trips over when comparing the two `k8s/infra/databases/*.yaml` files months later with no memory of why they differ. ## Traps - Discovering during this gate that M6.4's nginx route works from inside the homelab LAN but not from wherever the Mac actually sits (VPN, different subnet, etc.) — a gap none of M6.1-M6.5's narrower tests would have caught, since this is the first task that tests from the Mac's actual network position rather than "outside the cluster" in the abstract. - Fixing a gate failure by loosening the gate (e.g. deleting assertion 2 because the cascade "mostly works") instead of fixing the underlying FK/migration issue. Same discipline this project's other gates (M0.8, M1.8, M2.8...) already hold to. --- Background: [M6.1](M6.1-agent-manager-db-manifest.md) · [M6.2](M6.2-schema-port.md) · [M6.3](M6.3-store-query-port.md) · [M6.4](M6.4-nginx-stream-routing.md) · [M6.5](M6.5-credentials-secret.md)