--- id: MASON-WP-0008 type: workplan title: "Generate the construction-plan existing-structure survey from live metadata" domain: infotech repo: ops-mason status: finished flavor: implementation owner: claude topic_slug: netkingdom created: "2026-10-05" updated: "2026-10-05" state_hub_workstream_id: "6a562716-aea0-5b9f-a6b5-5bbe6c459467" --- # MASON-WP-0008 — Generate the existing-structure survey ## Goal Section 2 of a construction plan (`docs/construction-plan-format.md`) needs actual OpenBao output: relevant policy, auth and secrets-engine entries, the matching ops-warden routing catalog entries, and an explicit answer to "does an existing lane already satisfy this demand?". Today the drafting agent gathers this by hand. Make it reproducible with one command that runs under the `ops-mason-build` grant. Founder decision: ops-warden routes, ops-mason surveys. ops-warden gets a pointer-only catalog entry (WARDEN-WP-0042), no survey code and no token. ## Boundaries - Metadata only. No `kv get`, no `*/data/*` read, no role-id or secret-id read; enforced by an allowlist in the script and asserted by tests. - `policies/ops-mason-build.hcl` is not changed. A query the grant does not permit is reported as `not permitted by ops-mason-build`, not widened. - No live OpenBao call in this workplan's implementation; tests use a fake bao. ## Tasks ## Implement scripts/plan-survey.py ```task id: MASON-WP-0008-T01 status: done priority: high state_hub_task_id: "17e0429a-25e6-5469-b1a5-2acdcde2fbf3" ``` Script in the `custody-inventory.py` idiom (stdlib plus PyYAML, grant token from `~/.claude-bao-token`, `BAO_ADDR` default `http://127.0.0.1:18200`, loud failure). Takes KV path, proposed policy, AppRole and/or Kubernetes role, consumer and keywords. Runs `bao token lookup` (display_name and policies only; refuses any token other than `ops-mason-build` plus `default`), `bao secrets list`, `bao auth list`, `bao policy list`, `bao list auth//role` for approle and kubernetes mounts, `bao read` of name-matched role configs, `bao policy read` for name-matched policies, `bao kv metadata get` on the target and `bao kv list` on its parent. Greps the ops-warden catalog (`--catalog`, default `~/ops-warden/registry/routing/catalog.yaml`). Emits a pasteable section 2 with counts, timestamp, `BAO_ADDR`, display_name and a draft yes/no/partial answer. ## Unit tests with a fake bao runner ```task id: MASON-WP-0008-T02 status: done priority: high state_hub_task_id: "f11fd0a1-5e60-5b34-a173-6ca30f909cc1" ``` `tests/test_plan_survey.py`: yes/no/partial verdicts, denied capabilities degrade and are listed as incomplete, no grant and non-grant tokens fail loudly, the token never appears in output, every recorded call is metadata-only, and the guard refuses `kv get`, `*/data/*`, role-id/secret-id and write calls. ## Document the command ```task id: MASON-WP-0008-T03 status: done priority: medium state_hub_task_id: "a273a5c5-5c4e-59df-bfb7-dc49eb63f80b" ``` `docs/construction-plan-format.md` § 2 (grant, then survey) and the OpenBao Access section of `AGENTS.md`. ## Route to it from ops-warden ```task id: MASON-WP-0008-T04 status: done priority: medium state_hub_task_id: "d229f8cf-9e85-5006-8393-0b24bd64d502" ``` Pointer-only catalog entry `openbao-structure-survey` in ops-warden (WARDEN-WP-0042): owner ops-mason, `warden_executes: false`, points to the grant, this script and the format doc. ## First live survey: inbound-library IMAP lane ```task id: MASON-WP-0008-T05 status: done priority: medium state_hub_task_id: "c9a73b84-6b37-5d39-a2ab-9f8b68bd940a" ``` Run against the real store for `platform/workloads/inbound-library/imap`, policy `workload-kv-read-inbound-library-imap`, AppRole `inbound-library`, consumer `inbound-library`, and check that `bao list auth//role`, role reads and `bao policy read` behave as the policy file implies (`auth/approle/role/*`, `auth/kubernetes/role/*`, `sys/policies/acl/*`). Record any `not permitted by ops-mason-build` line as a founder decision, not a policy edit. Done 2026-10-05: the founder minted the grant (`token-claude-MASON-WP-0008-T05`, ops-mason-build + default). The live run exited 0. The grant permitted every query, so there is no capability gap: 10 mounts, 5 auth mounts, 76 policies, 13 AppRole and 25 Kubernetes roles, a policy read of the one name-matched candidate, target metadata (`current_version=2`) and 40 catalog entries. The output is pasted verbatim as section 2 of `plans/inbound-library-imap-approle.md`.