diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index 4b8a70b..c7ca795 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,11 +1,11 @@ { "phase": 0, - "stage": "research", + "stage": "ideate", "milestone": "v0.9", "milestone_slug": "rearchitecture", "phase_role": "pre_execution", "attempts": 0, - "updated_at": "2026-08-05T02:05:00Z", + "updated_at": "2026-08-05T02:10:00Z", "milestone_complete": false, "next_milestone": null } \ No newline at end of file diff --git a/.ciagent/IDEATION_v0.9.md b/.ciagent/IDEATION_v0.9.md new file mode 100644 index 0000000..86860d3 --- /dev/null +++ b/.ciagent/IDEATION_v0.9.md @@ -0,0 +1,363 @@ +# Ideation v0.9 — Re-architecture Foundation + +**Project**: orca (single-project mode) | **Milestone**: v0.9/v1.0 re-architecture +**Date**: 2026-08-05 | **Agent**: ideation agent | **Confidence threshold**: 0.60 +**Next REQ ID prior to this run**: REQ-060 (v0.8 complete) + +## Context + +The v0.8 codebase (REQ-001..060, all Complete) is a daemon-based, mTLS, +HCL, single-namespace orchestration engine. The adopted PRD supersedes this +with a CLI-only, SSH-push, step-ca, Markdown-frontmatter, multi-namespace +stack. 9 packages are deprecation targets (~2,400 LOC of v0.8 +daemon/transport/security-ca/engine-dispatch/jobspec-hcl/config-hcl/certpaths +code), 7 packages are adaptable, and 8 subsystems are net-new with zero +implementation. The §23 milestone plan has 11 v0.9 phases + 17 v1.0 phases but +under-specifies the deprecation mechanics, the SSH-push transport design, the +lead-applier execution model, several adapter/bridge layers, and the +migration ordering risk. + +This ideation produced 30 ideas across three tiers, all accepted at ≥0.60 +confidence, mapped to REQ-061..REQ-090. Seven phase-reordering flags against +the PRD §23 plan are listed at the end. + +## Tier 1 — Mechanical (Codebase-Observable Gaps & Hygiene) + +### I-M-001 — `orca daemon` deprecation command and build-tag removal path +- **Tier**: mechanical +- **Description**: The PRD deprecates `internal/daemon/` (R-001) but §23 never says *how*. `internal/cli/daemon.go` (100 LOC) registers the `daemon` cobra command and wires `daemon.NewServer` + `engine.Dispatcher`. Big-bang removal would break the v0.8→v1.0 migration path (v1.0-P14) because `orca upgrade --to-v1.0` must run against a live v0.8 cluster that still has daemons. Proposal: (1) in v0.9, `orca daemon` emits a deprecation warning and still runs (dual-write window); (2) in v1.0, `orca daemon` is repurposed to `orca daemon drain-and-stop` (stops v0.8 daemons on peers via SSH, confirms workloads survive via systemd); (3) post-v1.0, the command and `internal/daemon/` are deleted. Add `// Deprecated` Go doc comments + `slog.Warn` on every run. +- **Rationale**: R-001 is an invariant, but the *transition* off the daemon is a mechanical gap. The v0.8 `daemon.go` is wired in `root.go` init; removing it without a transition plan breaks the §24 migration. +- **Proposed REQ ID**: REQ-061 +- **Proposed phase placement**: v1.0-P14 (migration) — deprecation warning lands in v0.9-P0X +- **Confidence**: 0.82 +- **Accept/Defer**: accept + +### I-M-002 — Coverage follow-ups: 3 zero-test packages + `internal/cli` to 70% +- **Tier**: mechanical +- **Description**: v0.8 P01 (REQ-057) raised 6 packages to ≥70% and added first tests for `internal/audit`, `internal/certpaths`, `cmd/orca` at a 50% toe-hold. The v0.9 re-architecture will *replace* several of these packages, but the *adaptable* ones (`internal/store`, `internal/doctor`, `internal/cli`) must keep their 70% floor through the refactor. Once `daemon.go` is deprecated/removed (I-M-001), the exclusion reason disappears and the floor applies to the whole package. Add a coverage-gate assertion in the v0.9 P0X ship phase that `internal/cli` ≥ 70% *including* all new subcommand files (ns, txn, pve, secrets, volume, cache, backup). +- **Rationale**: The PRD §23 does not mention coverage. The config.json policy says 70% floor for new packages, 50% minimum. The 8 net-new subsystems will need 70% floors from their first phase. Without an explicit gate, the v0.7/v0.8 "toe-hold at 50% then defer" pattern will repeat. +- **Proposed REQ ID**: REQ-062 +- **Proposed phase placement**: v0.9-P0X (ship+audit) + each net-new package's first phase +- **Confidence**: 0.88 +- **Accept/Defer**: accept + +### I-M-003 — `known_hosts` flock concurrency gap (deferred P1 from REVIEW_v0.8 A2) +- **Tier**: mechanical +- **Description**: REVIEW_v0.8 flagged A2 (P1): `TOFUHostKeyCallback` capture path (`bootstrap.go:290-302`) and `ResetHostKey` (`bootstrap.go:479-523`) both do read-modify-write on `known_hosts` with no lock. The review said "Defer to v0.9." This is now load-bearing because the SSH-push transport (R-001) will do *many more* concurrent SSH operations than v0.8 did. Add a `flock`-style advisory lock (stdlib `syscall.Flock` wrapper) around the RMW in both paths. Lock file is `cluster/known_hosts.lock` (multi-namespace layout, R-002). +- **Rationale**: The v0.8 single-operator mitigation is weaker under v0.9's parallel SSH fan-out. The PRD doesn't mention this, but the SSH-push transport makes the race more likely. +- **Proposed REQ ID**: REQ-063 +- **Proposed phase placement**: v0.9-P0a1 (path resolver, since it establishes `cluster/` layout) +- **Confidence**: 0.74 +- **Accept/Defer**: accept + +### I-M-004 — HCL→Markdown jobspec adapter/bridge layer +- **Tier**: mechanical +- **Description**: R-013 says the Markdown parser is canonical; `.yaml` and `.hcl` are "accepted by parser dispatcher." But `internal/jobspec/spec.go` (69 LOC) is an HCL-only parser with a flat `Spec{Job, Tasks}` schema — no `kind:` (R-012), runtime blocks, or body preservation (R-014/R-015). The "dispatcher" implies the new parser detects file extension and dispatches. Proposal: keep `internal/jobspec/spec.go` as the legacy HCL path behind `// Deprecated`; add `internal/jobspec/markdown.go` (canonical) + `internal/jobspec/dispatch.go` (extension-based dispatcher: `.md`→Markdown, `.hcl`→legacy, `.yaml`→Markdown-with-empty-body). The dispatcher returns a unified `*WorkloadSpec` that the legacy parser populates via an adapter. Preserves `orca job run old-spec.hcl` during the migration window. +- **Rationale**: R-013 explicitly accepts `.hcl`, so a dispatcher is required. §23 v0.9-P0b says "parser dispatcher" but doesn't specify the adapter. +- **Proposed REQ ID**: REQ-064 +- **Proposed phase placement**: v0.9-P0b (Markdown jobspec parser) +- **Confidence**: 0.85 +- **Accept/Defer**: accept + +### I-M-005 — `orca doctor --legacy-paths` detection for v0.8 residue +- **Tier**: mechanical +- **Description**: The v0.8 layout is `~/.orca/{orca.db, ca.crt, ca.key, server.crt, server.key, orca_ssh_key, known_hosts, config.hcl}`. The v1.0 layout is `ORCA_HOME/{_defaults/, cluster/{ca,master.key,peers,pve,txns}, /{db,.env,.env.secrets,jobs,alloc,ns.md}, orca_cache.db}`. `orca doctor` (`internal/doctor/doctor.go`, 501 LOC, adaptable) must gain a `doctor legacy` subcommand that detects v0.8 residue: presence of `orca.db` at ORCA_HOME root, `ca.crt`/`ca.key` (internal CA, superseded by step-ca), `config.hcl` (HCL, demoted), flat `server.crt` (single-namespace), and a `namespace` column in any `*.db` (R-002 says no namespace column). Output: list of detected legacy artifacts with migration recommendations. This is the *detection* half of v1.0-P14; the *migration* half is I-C-001. +- **Rationale**: §23 v1.0-P14 says "orca upgrade --to-v1.0, post-invariant checks" but doesn't specify the detection surface. `doctor` is the diagnostics framework and is explicitly adaptable. +- **Proposed REQ ID**: REQ-065 +- **Proposed phase placement**: v1.0-P14c (mixed-version tolerance + no-orca enforcement) +- **Confidence**: 0.80 +- **Accept/Defer**: accept + +### I-M-006 — Legacy CA state migration to step-ca (cert import) +- **Tier**: mechanical +- **Description**: `internal/security/ca.go` (338 LOC) holds an internal Go CA with `ca.crt`/`ca.key` (RSA 3072, 10-year). The PRD replaces this with step-ca (R-006, D-101 reverses AD-010). The v1.0-P14 migration must handle existing deployments with an internal CA: (a) import the existing CA key into step-ca as `step ca init --deployment-type standalone --remote-management` with the existing key; (b) issue new SVIDs from step-ca and let old certs expire; (c) document that v0.8 certs are invalidated and re-bootstrap is required. The codebase audit says `ca.go`+`csr.go` are *replaced* — but the *state* (the CA key + issued server certs in `cert_repo` SQLite) may need to be preserved for audit history even if the live trust root changes. Proposal: `orca upgrade --to-v1.0 --import-ca` reads `~/.orca/ca.key`, initializes step-ca with it, and re-issues workload SVIDs. Without this, existing deployments lose their trust root with no path back. +- **Rationale**: AD-010 is explicitly reversed by D-101, but the reversal doesn't address what happens to the existing CA material. §24 covers data migration but not CA migration. +- **Proposed REQ ID**: REQ-066 +- **Proposed phase placement**: v1.0-P14a (data migration) +- **Confidence**: 0.70 +- **Accept/Defer**: accept (design in v0.9-P00 so step-ca integration knows the import contract) + +### I-M-007 — Fuzz test harness for the Markdown frontmatter parser +- **Tier**: mechanical +- **Description**: R-014/R-015 require byte-exact body preservation — "body of every .md config file preserved verbatim." This is a class of bug that's easy to get wrong (off-by-one on the `---` delimiter, trailing newline handling, BOM, CRLF, nested code fences containing `---`). v0.8 has no fuzz tests at all. Proposal: add a `testing.F` fuzz target in `internal/jobspec/markdown_test.go` that round-trips random frontmatter+body through `ParseMarkdown` and asserts `body == roundtripped.body` byte-exact. Also add a corpus of adversarial fixtures (CRLF, BOM, no-frontmatter, empty-frontmatter, frontmatter-with-only-separator). §23 v1.0-P08 mentions integration tests but not fuzzing. +- **Rationale**: R-015 is a *load-bearing invariant* (body appears in inspect/history). Byte-exactness is exactly what fuzz tests are for. The v0.8 jobspec tests are golden-file only (no fuzz). +- **Proposed REQ ID**: REQ-067 +- **Proposed phase placement**: v0.9-P0b (Markdown parser) — fuzz from day one +- **Confidence**: 0.78 +- **Accept/Defer**: accept + +### I-M-008 — Deprecation warnings on removed/repurposed CLI subcommands +- **Tier**: mechanical +- **Description**: The v0.8 CLI has `orca cert {ca-init,gen,show,renew,fingerprint}` (`internal/cli/cert.go`), `orca node join` with mTLS handshake semantics (`internal/cli/node.go`), `orca job run `. The PRD repurposes `node join` to SSH-bootstrap (no mTLS), deprecates `cert` (step-ca handles it), and changes `job run` to accept `.md` specs. Each removed/changed command should emit a `slog.Warn` deprecation banner with the v1.0 replacement, *except* when run under `orca upgrade`. The existing `root.go` `PersistentPreRunE` is the natural hook for a global `--no-deprecation-warnings` flag. +- **Rationale**: Operators running v0.8 commands against v0.9/v1.0 need to know what changed. The PRD doesn't mention deprecation UX. +- **Proposed REQ ID**: REQ-068 +- **Proposed phase placement**: v0.9-P0X (ship) + v1.0-P13 (ns subcommands, when CLI surface is finalized) +- **Confidence**: 0.72 +- **Accept/Defer**: accept + +### I-M-009 — `internal/config/config.go` HCL config demotion via adapter +- **Tier**: mechanical +- **Description**: `internal/config/config.go` (127 LOC) parses HCL config with keys `db_path, listen_addr, ca_path, server_cert_path, server_key_path, node_capacity`. The PRD replaces this with Markdown-frontmatter config (R-014) + per-namespace `.env`/`.env.secrets` (R-011). The `listen_addr` and `server_*_path` keys are daemon-specific (deprecated by R-001). The `root.go` `PersistentPreRunE` calls `config.Load(configPath)` on every command — must be repointed to the new Markdown config loader. Proposal: keep `internal/config/` as `legacy_config.go` with `// Deprecated`; add `internal/config/markdown.go` for the new loader; `root.go` dispatches on file extension (`.hcl`→legacy, `.md`→new). The `--config` flag semantics change: `.hcl` is read-only legacy, `.md` is canonical. +- **Rationale**: R-014 makes Markdown canonical but `.hcl` must still parse during migration. The existing `config.Load` is called unconditionally in `root.go:42-47`. +- **Proposed REQ ID**: REQ-069 +- **Proposed phase placement**: v0.9-P0a1 (path resolver + config demotion) +- **Confidence**: 0.76 +- **Accept/Defer**: accept + +### I-M-010 — `internal/certpaths/` replacement with multi-namespace path resolver +- **Tier**: mechanical +- **Description**: `internal/certpaths/certpaths.go` (64 LOC) returns flat paths: `Dir() = $ORCA_HOME`, `CACertPath() = Dir/ca.crt`, `DBPath() = Dir/orca.db`. R-002 requires multi-namespace layout: `ORCA_HOME//db/`, `ORCA_HOME/cluster/{ca,master.key,peers,pve,txns}`, `ORCA_HOME/_defaults/`. The package is imported by `doctor`, `proxmox`, `store`, `cli` — changing it is cross-cutting. Proposal: replace `certpaths` with a new `internal/paths` package: `paths.NamespaceDir(ns)`, `paths.ClusterDir()`, `paths.CacheDB()`, `paths.MasterKey()`, `paths.NSDb(ns)`, `paths.NSEnv(ns)`, `paths.NSSecrets(ns)`. Keep `certpaths` as a thin shim that calls `paths` with the default namespace for v0.8 compat, then remove the shim post-v1.0. +- **Rationale**: R-002 is foundational and `certpaths` is the single source of path truth. Every adaptable package (`store.Open`, `doctor`, `proxmox`) imports it. Highest-blast-radius mechanical change. +- **Proposed REQ ID**: REQ-070 +- **Proposed phase placement**: v0.9-P0a1 (must come first) +- **Confidence**: 0.84 +- **Accept/Defer**: accept + +### I-M-011 — `internal/store/` schema: per-namespace DBs, drop ns column +- **Tier**: mechanical +- **Description**: R-002 says "No `namespace` column in SQLite." The v0.8 schema has 7 migrations (`0001`..`0007`) with a single `orca.db`. The v1.0 model has one DB per namespace (`/db/orca.db`) plus a CLI-side cache DB (`orca_cache.db`, R-008). The existing `store.Open(path)` takes a path arg — adaptable. But the migrations are global; they need to apply *per namespace DB*. Proposal: `store.Open` gains a namespace parameter (or caller passes `paths.NSDb(ns)`); `migrate.go` runs `0001`..`0007` (minus `0006_node_kind_os` which is v0.8-specific) plus new `0008_namespace_layout.sql`. The `cert_repo` (`0004_certs.sql`) is removed (step-ca handles certs). The audit_log table moves to the CLI-side cache DB (R-008). Existing v0.8 `orca.db` is migrated by splitting tables into per-namespace DBs during v1.0-P14. +- **Rationale**: R-002 is explicit ("No namespace column in SQLite") but the existing schema has a single DB. §23 doesn't specify the schema split mechanics. +- **Proposed REQ ID**: REQ-071 +- **Proposed phase placement**: v0.9-P0a1 + v1.0-P06 (alloc history, which uses cache DB) +- **Confidence**: 0.80 +- **Accept/Defer**: accept + +### I-M-012 — `internal/transport/` deletion + SSH-push package introduction +- **Tier**: mechanical +- **Description**: `internal/transport/` (7 files, ~1300 LOC incl tests) implements mTLS client/server, dispatch, idempotency, retry, handshake logging. R-001 + R-006 replace this with SSH-push. The *idempotency* and *retry* logic (`idempotency.go` 123 LOC, `retry.go` 151 LOC) is conceptually reusable for SSH-push (retry on SSH failure, idempotency keys for SCP'd configs). Proposal: delete `mtls.go`, `dispatch.go`, `handshake_log.go`; extract retry/idempotency patterns into a new `internal/sshpush/` package. The existing `transport.IdempotencyStore` (in-memory `sync.Map` of keys) is directly reusable. This avoids re-implementing retry semantics from scratch. +- **Rationale**: The codebase audit marks `internal/transport/` as fully replaced, but the retry/idempotency *patterns* are transport-agnostic. §23 doesn't call this out. +- **Proposed REQ ID**: REQ-072 +- **Proposed phase placement**: v0.9-P00 (deprecation sweep) — delete in v1.0-P14 +- **Confidence**: 0.68 +- **Accept/Defer**: accept (defer deletion to v1.0-P14 to keep dual-write window open) + +## Tier 2 — Backend-Enriched (Structural / Architectural) + +### I-B-001 — SSH-push transport layer design +- **Tier**: backend-enriched +- **Description**: The PRD replaces `internal/transport/` (mTLS HTTP) with SSH-push but §23 never specifies the transport's internal design. Key decisions: (1) **Connection pooling**: reuse `*ssh.Client` per peer across multiple SCP/exec operations within a single CLI invocation. (2) **Idempotency**: SCP of a config file is idempotent if content hash matches — use content-addressed filename (`/run/orca/.unit`) and skip if present. (3) **Retry**: reuse v0.8's exponential backoff (100ms start, ×2, cap 5s, max 5 attempts) applied to SSH dial/exec failures. (4) **Timeout**: per-operation `context.WithTimeout` (default 30s SCP, 10s exec). (5) **Fan-out**: `errgroup.Group` with bounded concurrency for N-peer ops (default 8). (6) **known_hosts**: reuse `proxmox.TOFUHostKeyCallback` for all peers, not just Proxmox. +- **Rationale**: Load-bearing replacement for the entire v0.8 transport layer. §23 assumes it but never designs it. Without connection pooling, every CLI operation re-dials SSH. +- **Proposed REQ ID**: REQ-073 +- **Proposed phase placement**: v0.9-P01 (first phase needing SSH-push) — design in v0.9-P0a1 +- **Confidence**: 0.86 +- **Accept/Defer**: accept + +### I-B-002 — Emitter template system (Layer 4) +- **Tier**: backend-enriched +- **Description**: The PRD §5 describes a 4-layer architecture where Layer 4 is "emitters" that render systemd units, Traefik dynamic config, Syncthing config, etc. from the workload spec. §23 never specifies the emitter interface. Proposal: an `internal/emitter/` package with `Emitter` interface: `Render(spec *WorkloadSpec, node *Node) ([]File, error)` where `File{Path, Content, Mode}`. Implementations: `systemdEmitter`, `traefikEmitter`, `syncthingEmitter`, `socketEmitter`. The SSH-push transport SCPs the `[]File` atomically (write-to-tmp + rename). Emitters registered per workload kind + runtime. +- **Rationale**: The emitter layer is the bridge between the declarative spec and the server-side files. Without a defined interface, each phase (P02 service, P04 hooks, P08 sockets, P09 storage) will invent its own rendering. +- **Proposed REQ ID**: REQ-074 +- **Proposed phase placement**: v0.9-P0c (schemas + emitter interface) +- **Confidence**: 0.82 +- **Accept/Defer**: accept + +### I-B-003 — Lead applier execution model: pure bash + systemd timer vs CLI-invoked +- **Tier**: backend-enriched +- **Description**: R-001 says "no orca binary on servers." R-010 says the lead applies desired-state transactionally. Unresolved: does the lead run `orca-pull.sh` (pure bash that SCPs a desired-state bundle and applies it via `systemctl daemon-reload` + `systemctl restart`) or does the operator's CLI SSH into the lead and runs `orca apply` remotely (which would put an orca binary on the lead, violating R-001)? The PRD's intent is the former: the lead is bare Linux with systemd timers + bash. Proposal: (1) the CLI renders a *transaction bundle* (tarball of desired-state files + `apply.sh` + `verify.sh`) on the operator host; (2) SCPs it to the lead's `/run/orca/txns//`; (3) the lead's systemd timer runs `/run/orca/txns//apply.sh` which idempotently applies and runs verify; (4) the CLI polls the lead for txn status via SSH (`cat /run/orca/txns//status.json`). The bash scripts are generated by the CLI's emitter (I-B-002), not hand-written per cluster. +- **Rationale**: The most ambiguous load-bearing design decision in the PRD. R-001 + R-010 together imply the lead runs no orca binary, but the lead must apply transactions. §23 doesn't resolve this. Getting it wrong means either violating R-001 or having no transactional apply. +- **Proposed REQ ID**: REQ-075 +- **Proposed phase placement**: v1.0-P10 (transactional plane) — bundle format designed in v0.9-P00 +- **Confidence**: 0.78 +- **Accept/Defer**: accept + +### I-B-004 — step-ca integration: provisioning, CA bootstrap, cert signing API, SVID minting +- **Tier**: backend-enriched +- **Description**: D-101 reverses AD-010 (which rejected step-ca as "too heavyweight"). §23 mentions step-ca in R-006 but never specifies the integration. Key surfaces: (1) **Provisioning**: `orca init` (adapted from v0.8's `internal/cli/init.go`) runs `step ca init` on the lead, stores root + intermediate in `cluster/ca/`. (2) **CA bootstrap**: CLI SSHs to the lead, installs step-ca via apt, runs `step ca init`, stores `step-ca.json` config. (3) **Cert signing API**: workloads request SVIDs via `step ca token` (JWE provisioner token minted by CLI) → `step ca certificate`. The CLI mints the token because it holds the provisioner password (in `cluster/master.key`-derived form). (4) **SVID minting**: each workload gets a SPIFFE ID (`spiffe://orca///`) encoded as a SAN in the step-ca-issued cert. The v0.8 `internal/security/ca.go` is deleted; a new `internal/stepca/` package wraps the `step` CLI via SSH (no Go step-ca client library — keep zero-new-dep posture if possible, or add `github.com/smallstep/cli` as a dep). +- **Rationale**: step-ca is a new external dependency with its own config format, provisioner model, and CLI. §23 assumes it but never designs the integration. security-engineer persona must be reactivated. +- **Proposed REQ ID**: REQ-076 +- **Proposed phase placement**: v0.9-P07 (runtime block — runtimes need SVIDs) + v1.0-P02 (ACL — SPIFFE identities) +- **Confidence**: 0.74 +- **Accept/Defer**: accept + +### I-B-005 — Traefik dynamic config generation and atomic reload +- **Tier**: backend-enriched +- **Description**: R-006 makes Traefik load-bearing (mTLS termination + health checks). §23 puts service blocks + Traefik health checks in v0.9-P02. Design: the CLI's Traefik emitter (I-B-002) renders a dynamic config file (`/etc/traefik/dynamic/orca--.yaml`) with backends (the socket paths from R-007), health checks, and mTLS config pointing at step-ca's root. Atomic reload: Traefik watches the dynamic dir with `fsnotify` — writing the file atomically (tmp+rename) triggers a reload. Drain (v1.0-P05) works by writing a config with the backend's `weight=0` or removing it, triggering Traefik to stop routing. The v0.8 codebase has no Traefik integration at all. **Gated by grill C-10** (Traefik config atomicity protocol: tmpfile+fsync+rename + malformed-config hold-last-good verified). +- **Rationale**: Traefik is net-new and load-bearing. §23 mentions it in R-006/P02/P05 but never specifies config generation or reload mechanism. +- **Proposed REQ ID**: REQ-077 +- **Proposed phase placement**: v0.9-P02 (Service block + checks) +- **Confidence**: 0.80 +- **Accept/Defer**: accept + +### I-B-006 — Runtime abstraction interface (5 backends: wasm/podman/process/pve-vm/pve-ct) +- **Tier**: backend-enriched +- **Description**: v0.8's `internal/engine/executor.go` (211 LOC) is `os/exec` only. R-004/R-007 require 5 runtime backends. §23 puts this in v0.9-P07. Proposal: a `Runtime` interface in `internal/runtime/`: `Prepare(ctx, spec, node) (*Alloc, error)`, `Start(ctx, alloc) (pid/unit, error)`, `Stop(ctx, alloc) error`, `Status(ctx, alloc) (State, error)`. Implementations: `processRuntime` (wraps existing `executor.go` — directly reusable), `wasmRuntime` (wasmtime via CLI SSH exec), `podmanRuntime` (`podman run` via SSH), `pveVMRuntime` (`qm create`/`qm start` via v0.8 `proxmox` SSH session), `pveCTRuntime` (`pct create`/`pct start`). Each registered in a `runtimeRegistry` keyed by the `runtime:` frontmatter value. R-004 (migration with runtime change) means the `Alloc` carries a `runtime` field that can change on migration — `Prepare` re-runs with the new runtime. +- **Rationale**: The 5 backends are the largest net-new implementation surface. §23 lists them as one phase (P07) but under-specifies the interface contract. The existing `executor.go` is a good starting point for the `processRuntime` adapter. +- **Proposed REQ ID**: REQ-078 +- **Proposed phase placement**: v0.9-P07a/P07b/P07c (split per grill PC-10) +- **Confidence**: 0.82 +- **Accept/Defer**: accept + +### I-B-007 — Transaction bundle format and atomicity across N peers +- **Tier**: backend-enriched +- **Description**: R-010 requires transactional control-plane updates. §23 puts this in v1.0-P10. Design: a *transaction bundle* is a tarball containing: (1) `desired-state.json` (full desired state for affected namespaces), (2) `apply.sh` (idempotent apply script), (3) `verify.sh` (post-apply invariants), (4) `rollback.sh` (revert to previous state), (5) `manifest.sig` (signature with `cluster/master.key`). Atomicity across N peers: the CLI uploads the bundle to the lead; the lead applies to itself first, then fans out to peers via SSH. If any peer fails verify, the lead runs `rollback.sh` on all peers that applied. The bundle is content-addressed (` = sha256(desired-state.json)`) and stored in `cluster/txns//`. Drift detection (R-010) compares the last applied bundle's desired-state against the live state (polled via SSH `systemctl show` + file checksums). **Gated by grill C-09** (orca-pull.sh failure contract: idempotent re-run, bounded retry, deterministic state, structured syslog). +- **Rationale**: Multi-peer atomicity is the hardest part of R-010. §23 says "ArgoCD-style" but ArgoCD is Kubernetes-native; the SSH-push model needs a custom bundle format. +- **Proposed REQ ID**: REQ-079 +- **Proposed phase placement**: v1.0-P10 (transactional plane) — designed in v0.9-P00 +- **Confidence**: 0.76 +- **Accept/Defer**: accept + +### I-B-008 — Master key management and HKDF-SHA256 per-line .env.secrets encryption +- **Tier**: backend-enriched +- **Description**: R-011 specifies `.env.secrets` with AES-256-GCM, per-line nonce, master key at `cluster/master.key`. §23 puts this in v1.0-P03. Design: (1) `cluster/master.key` is a 32-byte random key generated by `orca init` (extend v0.8 `internal/security/ca.go`'s `WriteAtomic` pattern for the file write). (2) Each line of `.env.secrets` is `base64(nonce || ciphertext || tag)` where `nonce = random(12 bytes)` and `ciphertext = AES-256-GCM(plaintext, key=master.key, nonce, aad=line-number)`. (3) The AAD is the 1-indexed line number to prevent line-swap attacks. (4) Decryption reads the master key, iterates lines, decrypts with AAD. (5) `orca secrets set ` appends an encrypted line; `orca secrets get ` decrypts and prints (redacted by default, `--reveal` to show). (6) The v0.8 `internal/security/redact.go` (103 LOC) is directly reusable for redaction. HKDF-SHA256 derives per-namespace sub-keys from the master key (`HKDF-SHA256(master, info=)`) so compromising one namespace's key doesn't compromise others — but the master key is the root of trust. **Gated by grill C-19** (threat model for master.key passphrase-less posture). +- **Rationale**: R-011 is precise about the crypto but §23 doesn't specify key derivation, AAD, or CLI surface. The existing `redact.go` and `WriteAtomic` are reusable. +- **Proposed REQ ID**: REQ-080 +- **Proposed phase placement**: v1.0-P03 (secrets subsystem) +- **Confidence**: 0.84 +- **Accept/Defer**: accept + +### I-B-009 — Syncthing config rendering and folder-ID content-addressing +- **Tier**: backend-enriched +- **Description**: R-005 requires storage replication via per-namespace Syncthing. §23 puts this in v0.9-P09. Design: (1) each namespace gets a Syncthing folder `orca-` with a content-addressed folder ID (`sha256(ns + master-key-fingerprint)`). (2) The CLI renders `config.xml` for each peer's Syncthing instance, including the folder, devices (all peers in the namespace), and the path (`/alloc//`). (3) Syncthing runs as a systemd unit (emitted by the systemd emitter, I-B-002). (4) The CLI discovers peers via `cluster/peers/` and adds their Syncthing device IDs (each peer's Syncthing generates its own device key on first run, reported back via SSH). (5) R-005 says "a Service's count replicas share one runtime block" — the Syncthing folder is shared across the Service's alloc instances so all replicas see the same data. Migration (R-004) works because the new node joins the Syncthing folder and syncs before the workload starts. **Gated by grill C-02** (Syncthing feasibility spike) and **C-14** (deterministic conflict-resolution policy + forced-divergence integration test). +- **Rationale**: Syncthing is net-new. §23 lists it in P09 but doesn't specify config rendering, folder-ID scheme, or device discovery. +- **Proposed REQ ID**: REQ-081 +- **Proposed phase placement**: v0.9-P09 (storage replication) — spike in v0.9-P00 +- **Confidence**: 0.72 +- **Accept/Defer**: accept + +### I-B-010 — Namespace inheritance resolver algorithm +- **Tier**: backend-enriched +- **Description**: v0.9-P0a2 requires a "parent walker, cycle detection" for namespace inheritance. Each `ns.md` has a `parent:` field in frontmatter. The resolver walks up the parent chain, merging inherited values (constraints, env, runtime defaults). Cycle detection: DFS with a visited set; if a namespace is revisited, return a cycle error. The resolver returns a flattened `ResolvedNamespace` struct. The `_defaults/` namespace is the implicit root (always exists, has no parent). Inheritance semantics: child overrides parent for scalar fields; arrays (e.g., constraints) are unioned (child adds to parent, not replaces). The resolver is pure (no I/O) — it takes a map of `nsName → *NSConfig` and returns `nsName → *ResolvedNS`. This makes it trivially testable. +- **Rationale**: §23 mentions "parent walker, cycle detection" but not the merge semantics (override vs union) or the resolver's purity for testing. Getting merge semantics wrong breaks constraint inheritance (P05). +- **Proposed REQ ID**: REQ-082 +- **Proposed phase placement**: v0.9-P0a2 (namespace CRUD + inheritance) +- **Confidence**: 0.86 +- **Accept/Defer**: accept + +### I-B-011 — Bin-packing scheduler redesign (CLI-side, runtime-compatibility scoring) +- **Tier**: backend-enriched +- **Description**: v0.8's `internal/engine/scheduler.go` (117 LOC) does best-fit bin-packing by CPU+memory. The v0.9 scheduler must: (1) run CLI-side (not on a daemon), (2) score nodes by runtime compatibility (a wasm workload can only go to a node with wasmtime installed; a pve-vm workload can only go to Proxmox nodes), (3) respect constraints/affinity (CEL over node attributes, P05), (4) handle the 3 kinds differently (Job = one-shot, Service = count replicas spread across nodes, DaemonSet = one per node). The existing `scheduler.go` is a good skeleton but the scoring function changes entirely. Proposal: `Score(node, workload) (score int, fits bool)` where `fits` checks runtime compatibility + constraints, and `score` is the bin-packing score (most free capacity = highest score). For Services, the scheduler picks `count` distinct nodes (anti-affinity by default). For DaemonSets, it picks all matching nodes. +- **Rationale**: The scheduler moves from daemon-side to CLI-side (R-001) and gains runtime-awareness. §23 scatters this across P05 (constraints), P06 (task groups), P07 (runtime), P10 (migration) but never designs the scheduler itself. +- **Proposed REQ ID**: REQ-083 +- **Proposed phase placement**: v0.9-P05 (constraints & affinity — scheduler needs constraints to be meaningful) — skeleton in P0c +- **Confidence**: 0.80 +- **Accept/Defer**: accept + +### I-B-012 — `orca job lint` category-driven lint engine design +- **Tier**: backend-enriched +- **Description**: v1.0-P11 requires `orca job lint` with `--explain`. Design: a `Linter` that takes a `*WorkloadSpec` and runs a series of `Rule` checks, each returning a `Finding{Category, Severity, Message, Explanation}`. Categories: `schema` (missing required fields), `runtime` (incompatible runtime+constraint), `security` (missing SVID, plaintext secret in env), `migration` (missing storage replication for a migratable service), `best-practice` (no health check on a Service). `--explain` prints the rationale for each finding. Rules are registered in a `ruleRegistry` and individually testable. The linter is pure (no I/O) — it checks the spec against static rules, not live cluster state (that's `orca job verify`, P12). +- **Rationale**: §23 puts this in P11 but only says "category-driven." The rule interface and category taxonomy are unspecified. +- **Proposed REQ ID**: REQ-084 +- **Proposed phase placement**: v1.0-P11 (orca job lint) +- **Confidence**: 0.78 +- **Accept/Defer**: accept + +## Tier 3 — Cross-Cutting (Risk & Multi-Phase) + +### I-C-001 — v0.8→v1.0 migration ordering: daemon deprecation vs. new model rollout +- **Tier**: cross-cutting +- **Description**: The PRD §24 covers *data* migration but not *binary/daemon* deprecation ordering. The risk: v0.9 builds the new Markdown+kinds+runtime+SSH-push model, but v0.8 daemons are still running on peers. If v0.9 ships the new `orca job run` (Markdown) while the old daemon is still the execution engine, there's a split-brain: new specs can't run on the old daemon. Ordering proposal: (1) v0.9 ships the new parser + kinds + runtime + SSH-push *alongside* the old daemon (dual-write window); (2) `orca job run` in v0.9 uses the new SSH-push path if the spec is `.md` and the old daemon path if `.hcl`; (3) v1.0-P05 (drain) stops the old daemons; (4) v1.0-P14 (migration) converts remaining `.hcl` specs to `.md` and removes the daemon. The dual-write window means v0.9 is *not* a clean break — it's a compatibility milestone. This must be explicit in the plan or the v0.9 phases will assume the daemon is gone. +- **Rationale**: Single largest risk in the re-architecture. §23 implicitly assumes v0.9 builds the new model in isolation, but existing deployments have running daemons. Getting the ordering wrong means either (a) v0.9 can't be tested against real deployments, or (b) workloads are orphaned when the daemon is removed. +- **Proposed REQ ID**: REQ-085 +- **Proposed phase placement**: spans v0.9-P00 through v1.0-P14 — the *ordering decision* must be made in v0.9-P00 +- **Confidence**: 0.88 +- **Accept/Defer**: accept (most important idea in this report) + +### I-C-002 — "No orca on server" enforcement (doctor post-migration invariant check) +- **Tier**: cross-cutting +- **Description**: R-001 is an invariant: "no orca Go binary on any server." §23 v1.0-P14 says "post-invariant checks" but doesn't specify them. `orca doctor` must gain a `doctor no-orca-on-server` check that SSHs to each peer and verifies: (1) no `orca` binary in PATH (`ssh peer which orca` returns nothing), (2) no `orca` systemd service (`ssh peer systemctl list-units 'orca*'` returns empty), (3) no `orca` process (`ssh peer pgrep -x orca` returns empty), (4) no `/etc/orca/` directory. This check must run *after* v1.0-P05 (drain) and *before* v1.0-P16 (ship). The v0.8 `internal/proxmox/bootstrap.go` already has the SSH session infrastructure (`sessionRunner` seam) — directly reusable for the doctor check. +- **Rationale**: R-001 is a hard invariant but §23 doesn't enforce it post-migration. Without this check, a failed migration could leave orphaned daemons that cause split-brain. +- **Proposed REQ ID**: REQ-086 +- **Proposed phase placement**: v1.0-P14c (mixed-version tolerance) +- **Confidence**: 0.82 +- **Accept/Defer**: accept + +### I-C-003 — Test infrastructure: hermetic 3-linux + 1-proxmox cluster pipeline +- **Tier**: cross-cutting +- **Description**: §23 v1.0-P08 requires "hermetic CoreCI integration pipeline." The PRD §26.E mentions 3 linux + 1 proxmox. This is net-new test infra with zero current implementation. Design: (1) a `test/integration/` directory with a `docker-compose.yml` or `vagrant` setup that creates 4 containers/VMs (3 linux + 1 proxmox-simulated); (2) a Go test harness that SSHes to each, runs the CLI, and asserts end-to-end workflows (namespace create → workload submit → migrate → drain); (3) the proxmox node is simulated via a mock `pct`/`qm` script (the v0.8 `proxmox` package already has a `sessionRunner` seam for testability — extend it). The integration tests run in CoreCI on every milestone merge. The v0.8 e2e tests (`bootstrapE2ESetup` in `bootstrap_test.go`) use an in-process SSH server — this is the foundation but needs to scale to 4 nodes. +- **Rationale**: §23 assumes the infra exists but doesn't design it. devops-engineer persona should be reactivated. Without hermetic infra, the integration tests can't run in CI. +- **Proposed REQ ID**: REQ-087 +- **Proposed phase placement**: v1.0-P08 (integration tests) — harness bootstrapped in v0.9-P00 +- **Confidence**: 0.80 +- **Accept/Defer**: accept + +### I-C-004 — Security-engineer + network-engineer persona reactivation for new attack surfaces +- **Tier**: cross-cutting +- **Description**: The config.json has `security-engineer` and `network-engineer` dormant. The re-architecture introduces step-ca (PKI), Traefik (edge proxy), Syncthing (P2P file sync), wasmtime (sandbox), podman (container runtime) — all new attack surfaces. AD-010 (step-ca rejection) is reversed. The v0.8 security posture (internal CA, mTLS daemon-to-daemon) is replaced by (step-ca, SSH-push, Traefik mTLS). The security-engineer persona must be reactivated to review: (1) step-ca provisioner model (the CLI holds the provisioner password — is that in `cluster/master.key` or a separate secret?), (2) SSH-push blast radius (compromised CLI key = full cluster), (3) Traefik as the new edge (DoS, config injection), (4) `.env.secrets` crypto (I-B-008). The network-engineer persona must review: (1) socket-based service exposure (R-007), (2) Syncthing P2P ports, (3) Traefik routing. §23 doesn't mention persona reactivation. +- **Rationale**: config.json explicitly notes the re-architecture "should reactivate security-engineer and network-engineer." Cross-cutting review concern, not a single phase. +- **Proposed REQ ID**: REQ-088 +- **Proposed phase placement**: spans v0.9 through v1.0 — reactivation in v0.9-P00, review at v1.0-P15.5 (threat model) and v1.0-P16 (final audit) +- **Confidence**: 0.84 +- **Accept/Defer**: accept + +### I-C-005 — Documentation rewrite: ARCHITECTURE.md, PROJECT.md, README, AD-010 supersession +- **Tier**: cross-cutting +- **Description**: All three docs describe the OLD architecture. `ARCHITECTURE.md` (640 lines) describes the daemon layer, mTLS transport, internal CA, HCL jobspec — all deprecated. `PROJECT.md` (30k chars) has D-001..D-010 decisions, several now superseded. `README.md` has the v0.8 quickstart. AD-010 (step-ca rejection) must be explicitly superseded by D-101 with a dated rationale reversal. The anti-patterns section in `ARCHITECTURE.md:471-484` lists "No external PKI" — now reversed. Proposal: (1) in v0.9-P00, add a "v0.9 Architecture (Supersedes v0.8)" section to ARCHITECTURE.md with the new 4-layer model; (2) mark the old sections as "v0.8 (deprecated)" with banners; (3) add a "Superseded Decisions" table (AD-009, AD-010 reversed by D-101; AD-007 HCL demoted by R-013); (4) in v1.0-P15, rewrite README quickstart for the new `curl | sh` + `orca init` + `orca ns create` flow. +- **Rationale**: The docs are the first thing new contributors read. Leaving v0.8 docs as canonical during v0.9 development causes confusion. §23 mentions README in P15 but not ARCHITECTURE.md/PROJECT.md. +- **Proposed REQ ID**: REQ-089 +- **Proposed phase placement**: v0.9-P00 (banners + supersession table) + v1.0-P15 (README quickstart) + v1.0-P16 (final review) +- **Confidence**: 0.82 +- **Accept/Defer**: accept + +### I-C-006 — Dual-write window: can v0.9 ship new parser while old daemon runs? +- **Tier**: cross-cutting +- **Description**: Focused version of I-C-001. The specific question: in v0.9, when the new Markdown parser + kinds + SSH-push are shipped, can they coexist with v0.8 daemons still running on peers? The answer depends on whether `orca job run ` uses the new SSH-push path (bypassing the daemon entirely) or routes through the old daemon. If it bypasses, the daemon is irrelevant for new specs but still serves old `.hcl` specs. If it routes through, the daemon can't handle `.md` specs. Proposal: v0.9 `orca job run` dispatches on extension (`.md`→SSH-push new path, `.hcl`→old daemon path) via the parser dispatcher (I-M-004). This is a *dual-write window* where both paths coexist. The daemon is not removed until v1.0-P05 (drain). The risk: if a `.md` workload and a `.hcl` workload target the same node, the SSH-push path writes systemd units directly while the daemon also manages units — they can conflict. Mitigation: the SSH-push path writes to a separate systemd unit namespace (`orca-v1-.service`) while the daemon uses `orca-.service`. No unit name overlap = no conflict. +- **Rationale**: Operational feasibility question for v0.9. §23 doesn't address it. If the answer is "no dual-write, daemon must be removed first," then v0.9 can't be tested incrementally and must ship as a big-bang — much higher risk. +- **Proposed REQ ID**: REQ-090 +- **Proposed phase placement**: v0.9-P00 (decision before any v0.9 execution phase) +- **Confidence**: 0.86 +- **Accept/Defer**: accept + +## Summary Table + +| ID | Tier | Title | REQ | Phase | Conf | Accept | +|----|------|-------|-----|-------|------|--------| +| I-M-001 | M | `orca daemon` deprecation path | REQ-061 | v1.0-P14 (warn v0.9-P0X) | 0.82 | accept | +| I-M-002 | M | Coverage follow-ups to 70% | REQ-062 | v0.9-P0X + each new pkg | 0.88 | accept | +| I-M-003 | M | known_hosts flock concurrency | REQ-063 | v0.9-P0a1 | 0.74 | accept | +| I-M-004 | M | HCL→Markdown jobspec adapter | REQ-064 | v0.9-P0b | 0.85 | accept | +| I-M-005 | M | `doctor --legacy-paths` detection | REQ-065 | v1.0-P14c | 0.80 | accept | +| I-M-006 | M | Legacy CA state migration to step-ca | REQ-066 | v1.0-P14a | 0.70 | accept | +| I-M-007 | M | Fuzz harness for Markdown parser | REQ-067 | v0.9-P0b | 0.78 | accept | +| I-M-008 | M | Deprecation warnings on CLI subcommands | REQ-068 | v0.9-P0X + v1.0-P13 | 0.72 | accept | +| I-M-009 | M | HCL config demotion via adapter | REQ-069 | v0.9-P0a1 | 0.76 | accept | +| I-M-010 | M | certpaths → multi-namespace path resolver | REQ-070 | v0.9-P0a1 | 0.84 | accept | +| I-M-011 | M | store schema: per-namespace DBs | REQ-071 | v0.9-P0a1 + v1.0-P06 | 0.80 | accept | +| I-M-012 | M | transport deletion + SSH-push package | REQ-072 | v0.9-P00 (delete v1.0-P14) | 0.68 | accept | +| I-B-001 | B | SSH-push transport layer design | REQ-073 | v0.9-P01 | 0.86 | accept | +| I-B-002 | B | Emitter template system (Layer 4) | REQ-074 | v0.9-P0c | 0.82 | accept | +| I-B-003 | B | Lead applier execution model | REQ-075 | v1.0-P10 (design v0.9-P00) | 0.78 | accept | +| I-B-004 | B | step-ca integration | REQ-076 | v0.9-P07 + v1.0-P02 | 0.74 | accept | +| I-B-005 | B | Traefik dynamic config + atomic reload | REQ-077 | v0.9-P02 | 0.80 | accept | +| I-B-006 | B | Runtime abstraction (5 backends) | REQ-078 | v0.9-P07a/b/c | 0.82 | accept | +| I-B-007 | B | Transaction bundle + N-peer atomicity | REQ-079 | v1.0-P10 (design v0.9-P00) | 0.76 | accept | +| I-B-008 | B | Master key + HKDF per-line encryption | REQ-080 | v1.0-P03 | 0.84 | accept | +| I-B-009 | B | Syncthing config + folder-ID | REQ-081 | v0.9-P09 | 0.72 | accept | +| I-B-010 | B | Namespace inheritance resolver | REQ-082 | v0.9-P0a2 | 0.86 | accept | +| I-B-011 | B | CLI-side scheduler redesign | REQ-083 | v0.9-P05 (skeleton P0c) | 0.80 | accept | +| I-B-012 | B | `orca job lint` category-driven engine | REQ-084 | v1.0-P11 | 0.78 | accept | +| I-C-001 | C | v0.8→v1.0 migration ordering | REQ-085 | spans v0.9-P00→v1.0-P14 | 0.88 | accept | +| I-C-002 | C | "No orca on server" enforcement | REQ-086 | v1.0-P14c | 0.82 | accept | +| I-C-003 | C | Hermetic test infra (3 linux + 1 pve) | REQ-087 | v1.0-P08 (bootstrap v0.9-P00) | 0.80 | accept | +| I-C-004 | C | security/network persona reactivation | REQ-088 | spans v0.9→v1.0-P16 | 0.84 | accept | +| I-C-005 | C | Docs rewrite + AD-010 supersession | REQ-089 | v0.9-P00 + v1.0-P15/P16 | 0.82 | accept | +| I-C-006 | C | Dual-write window decision | REQ-090 | v0.9-P00 | 0.86 | accept | + +## Phase Reordering / Addition Flags (against PRD §23) + +1. **I-C-001 / I-C-006 (dual-write + migration ordering)** — require a decision in v0.9-P00 (before any execution phase). **Recommendation: add v0.9-P00 deprecation/migration-ordering pre-phase.** Most important structural addition. +2. **I-M-010 / I-M-011 / I-M-009 / I-M-003** — all land in v0.9-P0a. P0a may be overloaded. **Recommendation: split P0a into P0a1 (path/layout resolver + config demotion) and P0a2 (namespace CRUD + inheritance).** Path resolver is prerequisite for everything; highest blast radius. +3. **I-B-001 (SSH-push transport)** — §23 v0.9-P01 needs SSH-push. The design is a prerequisite. **Recommendation: SSH-push design in P0a1, not deferred to P01.** +4. **I-B-002 (emitter template system)** — should be designed *with* the schemas (P0c). **Recommendation: expand P0c to "schemas + emitter interface."** +5. **I-B-003 (lead applier model)** — bundle format + lead applier model must be designed *in v0.9* so the emitter can produce bundle-compatible output. **Recommendation: design spike in v0.9-P00.** +6. **I-C-003 (test infra)** — hermetic cluster harness should be bootstrapped in v0.9-P00 so every v0.9 phase can run integration tests. **Recommendation: bootstrap in v0.9-P00, expand in v1.0-P08.** +7. **I-C-004 / I-C-005 (persona reactivation + docs)** — span the whole milestone. **Recommendation: fold persona reviews into v0.9-P00 and v1.0-P16; fold doc banners into v0.9-P00.** + +## Cross-Reference Against Existing Decisions + +- **AD-009 (Internal CA, no external PKI)** — Superseded by D-101 (step-ca). I-B-004, I-M-006 implement the reversal. +- **AD-010 (Roll-our-own CA)** — Superseded by D-101. I-C-005 documents the supersession. No re-litigation — the PRD has decided; the override justification records the evidence basis. +- **AD-007 (HCL for job specs)** — Demoted by R-013 (Markdown canonical, HCL accepted). I-M-004 implements the adapter. Not a full reversal — HCL still parses. +- **AD-001 (Single binary with subcommands)** — Still holds. The CLI is the single binary; no orca on servers (R-001) refines this. +- **AD-015 (Best-fit bin-packing)** — Extended, not reversed. I-B-011 adds runtime-compatibility scoring. +- **D-035 (TOFU host-key)** — Still holds for non-Proxmox peers. I-M-003 hardens the concurrency. I-B-001 reuses `TOFUHostKeyCallback`. +- **D-046 (key-reset is local-only)** — Still holds. I-M-003 adds the lock. +- **D-047 (tiered coverage floor)** — Extended by I-M-002 to cover new packages. + +No accepted idea re-litigates a settled decision. All reversals (AD-009, AD-010, SPIFFE, no-container, no-multi-tenancy, HCL-canonical, daemon-on-every-node) are explicitly mandated by the PRD and justified by the recorded override justification. + +## Final Notes + +- **Total ideas**: 30 (12 mechanical, 12 backend-enriched, 6 cross-cutting). +- **Highest-confidence, highest-impact**: I-C-001 (migration ordering, 0.88) and I-C-006 (dual-write window, 0.86) — these shape the entire v0.9 execution strategy. +- **Highest-blast-radius mechanical**: I-M-010 (path resolver, 0.84) — touches every adaptable package. +- **Most under-specified by PRD**: I-B-003 (lead applier execution model, 0.78) — R-001 + R-010 create a tension the PRD doesn't resolve. \ No newline at end of file