--- 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. **Hardening follow-up (2026-09-28).** The existing build executor now binds execution to a reviewed `build_spec` plus `approved_spec_sha256`, reloads the approval before use, validates literal scope/token/binding inputs, and pins existing policy contents. Credential files are exclusively created as 0600 inside caller-owned 0700 directories before issuance, with symlinks and existing destinations refused. Command errors suppress sensitive output. Historical build approvals were not rewritten and no live lane was changed. The suite passes 108 tests, including refusal-before-mutation, policy drift, permissive-umask, symlink/hardlink/overwrite and error-output regressions. The source contract and recovery limitations are documented in `docs/construction-plan-format.md`. This is maintenance of T04, not a new task. ```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" ```