--- id: MASON-WP-0001 type: workplan title: "Foundation: the four-phase construction process, exercised on a real demand" status: finished state_hub_workstream_id: "fa60e81b-19f6-4ad5-a584-f049cdec1bdb" --- First workplan for ops-mason. Stands up the four phases described in `INTENT.md` — construction plan, review/optimize, executive summary, build — and exercises the whole pipeline on one real, already-waiting piece of demand: the `rein-openweights` OpenBao AppRole (`glas-harness/workplans/GLAS-WP-0002-T02`, currently blocked on exactly the mechanic this repo exists to formalize). That task is not busywork invented for this workplan — it's real backlog this pipeline unblocks the moment phase 4 lands. ## Task: Construction-plan format Design the artifact phase 1 produces: demand description (consumer, credential type, scope requested), an existing-structure survey (what `bao policy list`/`bao auth list`/`bao secrets list`/ops-warden's `registry/routing/catalog.yaml` already cover, and whether any of it already satisfies the demand), and the proposed changes (create/modify/ tear-down, each with a stated reuse-vs-new rationale). Document in `docs/construction-plan-format.md`. Plain, versioned markdown/YAML — not a database — so a plan can be reviewed in a diff and stored next to the workplan that requested it. **Done (2026-07-27).** `docs/construction-plan-format.md`: one file per plan at `plans/.md`, YAML frontmatter (`status: draft -> reviewed -> approved -> built -> catalogued`, the phase-4 hard gate), six body sections (demand, existing-structure survey, proposed changes, phase-2 review notes, phase-3 executive summary, phase-4 build result) — one document threaded through all four phases, not four separate artifacts. Proven against a real demand, not a synthetic one: `plans/rein-openweights-openrouter-approle.md`, phases 1-2 filled in — including a genuine finding in the existing-structure survey (an existing `openrouter-llm-connect` catalog lane that could theoretically be reused, and an explicit, reasoned decision *not* to reuse it, scoped narrowly instead). Phases 3-4 wait on `MASON-WP-0001-T03`/`T04`/`T05`. ```task id: MASON-WP-0001-T01 status: done priority: high state_hub_task_id: "6b25e7ca-52a0-41c2-b5a0-b5e46c824264" ``` ## Task: Review/optimize pass A self-check phase 2 runs over a draft plan before it's shown to anyone: flag likely-redundant new policies/roles against what already exists, naming/TTL/scoping convention drift against established lanes (e.g. the `agent-harness-binky-mail` shape: `token_ttl=15m`, `token_max_ttl=30m`, bounded `token_num_uses`), and note any compaction opportunity (two existing lanes that could merge). This can start as a checklist a human/agent runs manually against a draft plan — doesn't need to be automated tooling on day one. **Done (2026-07-27).** `docs/review-optimize-checklist.md`: six checks (naming, TTL/scoping, redundancy, compaction, ease of use for the consumer, posture) — manual for now, automating a checker deferred until there's a second worked example to generalize from. Applied for real to `plans/rein-openweights-openrouter-approle.md` §4 — including a genuinely useful finding (`credentials.py` already expects exactly this plan's path/delivery shape, zero code changes needed to consume it) and one explicit posture assumption flagged rather than left silent (`secret_id_ttl=0`, carried from `agent-harness-binky-mail`, surfaced again in the executive summary for the approval decision to confirm). ```task id: MASON-WP-0001-T02 status: done priority: high state_hub_task_id: "58a446fb-78d0-42b4-b10a-1119266b1016" ``` ## Task: Executive-summary format Design the phase 3 artifact: a short, decidable rendering of a reviewed plan — who/what gets access to what, for how long, blast radius if the credential leaks, and what it costs to reverse. This is the only mandatory human checkpoint (`INTENT.md` design principle 3/4) — it must be readable without knowing `bao` syntax. Document in `docs/executive-summary-format.md`, with the rein-openweights AppRole plan (task T05) as the worked example. **Done (2026-07-27).** `docs/executive-summary-format.md`: six fixed fields (one-line ask, who/what gets access, to what, for how long, blast radius, cost to reverse) plus an explicit approve/reject/revise decision — no `bao` syntax, no restating sections 1-4, no editorializing beyond the plan's own reuse-vs-new conclusion. Rendered for real into `plans/rein-openweights-openrouter-approle.md` §5 — ready for an actual decision, not a mockup. ```task id: MASON-WP-0001-T03 status: done priority: medium state_hub_task_id: "e37debfd-8a00-4780-b036-9376c6a10557" ``` ## Task: Build executor Phase 4: given an **approved** construction plan (explicit approval marker set after the executive summary was reviewed), execute it against OpenBao — policy write, AppRole creation, KV secret path creation (**empty path/structure only — never a secret value**, see `INTENT.md`), role_id/secret_id delivery to the consuming host, and a proposed pointer-only ops-warden catalog entry (`warden_executes: false`, no authored `steps`, `status: draft` — a normal commit/PR to the `ops-warden` repo, not a live API call). Ends by naming the exact `paste_once_provision` step the founder still needs to do (which path, which field) — it does not attempt to fill the value itself. Must refuse to run against an unapproved or missing-approval-marker plan — this is the one place a bug is a real security incident, not a bad UX. Start narrow: implement only the operations the first real plan (T05) needs, not a general OpenBao automation framework. **Done (2026-07-27).** `src/ops_mason/plan.py` (frontmatter parser + `is_approved()`), `src/ops_mason/executor.py` (`build_approle_kv_lane` — policy write, AppRole create, role_id/secret_id delivery, never the KV path's value), `src/ops_mason/audit.py` (metadata-only JSONL build log). 12 tests, all mocked at the `bao` subprocess boundary (no live OpenBao access from this session — see `GLAS-WP-0002-T02`'s original blocker). Explicitly verified: the refusal gate never even calls `subprocess.run` when the plan isn't fully approved (`test_refusal_never_calls_bao`); role_id/secret_id land as `0600` files and never appear in a log line or exception message; the policy HCL goes over stdin, never argv. ```task id: MASON-WP-0001-T04 status: done priority: high state_hub_task_id: "e949f4b7-5ef4-4e42-8a07-61b6c8040298" ``` ## Task: First real build — rein-openweights OpenBao AppRole Run the whole pipeline for real: construction plan for Option B from `glas-harness/workplans/GLAS-WP-0002-T02` (dedicated `rein-openweights` AppRole + KV secret path at `reins/rein-openweights/openrouter`, mirroring `agent-harness-binky-mail`'s shape) → review/optimize → executive summary → founder approval → build (structure only). Build ends with the founder pasting the real OpenRouter key through ops-warden's `paste_once_provision` desk into the newly created path/field — that one paste is the only place the live value exists outside OpenBao. Once done, notify `glas-harness`/ `rein-openweights` so `GLAS-WP-0002-T02`'s live OpenBao verification can proceed. **Done (2026-07-27), all four phases run for real.** Founder logged in (`bao login -method=oidc -path=netkingdom role=platform-admin` — the initial bare `-method=oidc` 403'd against the wrong default mount path; `netkingdom` + an explicit `role=` was required, found via `ops-warden`'s own catalog/wiki conventions). Executed `build_approle_kv_lane` for real: - **Live-state discrepancy found and handled correctly, not silently:** no `reins/` KV mount existed (`bao secrets list` showed only `platform/`/`secret/`/`tenants/`) — a bigger action (new secrets engine) than the executive summary's blast-radius framing disclosed. Paused and got explicit founder confirmation before mounting, rather than substituting a different path or proceeding unilaterally. - **Built:** `reins/` KV v2 mount, policy `workload-kv-read-rein-openweights-openrouter`, AppRole `rein-openweights`, role_id/secret_id delivered to `~/.local/rein-openweights/approle/` (`0600`). - **Real bug caught and fixed, root cause and all:** built with `token_num_uses=0` (OpenBao's own default = *unlimited*) instead of the plan's own stated `8` — caught by comparing the live AppRole config against `agent-harness-binky-mail`'s immediately after building, fixed live (`bao write ... token_num_uses=8`) and in code (`AppRoleKVSpec.token_num_uses` now has no default — must be passed explicitly, so this can't recur silently). - **Catalog entry landed:** `ops-warden` commit `c0a50bc` — and while writing it, corrected a real misreading in this repo's own `INTENT.md` of the no-double-source rule (pointer *fields* like `auth_method`/ `fetch_command`/`rotation.steps` are normal on non-SSH entries; only a bare top-level `steps:`+`cert_command:` pair is SSH-only) — verified against `ops-warden`'s full 326-test suite, green. - Plan file `status: built`; catalog entry `status: draft` until the founder's paste-once-provision + `GLAS-WP-0002-T02`'s live verification succeed. ```task id: MASON-WP-0001-T05 status: done priority: high state_hub_task_id: "01782608-aa5a-4f9b-a1fb-9a64f6d9c299" ```