008 T3: design_docs/tools/operationalize_design.py — dry-run / apply / close nf-hx6f ← Beads

closed priority 2 task unassigned activesession:operationalize-design

Twinned from https://github.com/cfsh/ninefold/issues/931 by tools/beads/import_github.py's reconcile pass.

Implement T3 from design_docs/008-live-status-for-design-doc-tasks.md (§8). Depends on T2 (merged — #924, PR #930, tools/github_live/ is on main). No longer governance-gated: #925/#926 narrowed perimeter.py's design_docs/* rule to *.md, so design_docs/tools/** merges on the normal bot review.

⚠️ Known non-blocking finding from T2 (on #913): jcantsp-bot can create GitHub Projects under its own account but gets a 403 creating them under the cfsh org (gh project create --owner cfsh fails — outside T1's OAuth scope grant, needs a separate org-level Projects permission). Functionally this doesn't block T3 — tools/github_live works fine against user-owned Projects — it just means the Project this script provisions will live under github.com/users/jcantsp-bot/projects, not github.com/orgs/cfsh/projects, until someone grants the bot org Projects access. Build against this as-is; don't block on it.

Goal: the one script that mints a doc's task issues and Project (--apply), validates a doc's table format before that's even possible (--dry-run, the default), and retires a finished doc (--close <doc-id>).

Context: this is the mechanized form of the "second PR" from §6, and also the tool the author runs pre-merge purely to self-check format. Uses tools/github_live's writes and its parse_task_table for both the local-disk (--dry-run, pre-merge) and Contents-API (re-running against an already-merged doc, e.g. for T4's backfill) read paths.

Files: design_docs/tools/operationalize_design.py, design_docs/tools/tests/.

Expected behaviour: see §6's three-mode description. --apply is idempotent against partially-operationalized docs — it checks list_task_issues before minting, so re-running (or the 005b/006a/007 backfill, which already has some ad hoc issues like #908) doesn't create duplicates.

Test plan: unit tests against tools/github_live's fake for all three modes, including the idempotency case and the missing-Issue-column refusal case. That refusal is exactly why T4's PRs hand-add the column before their first --apply run — 005b/006a/007 don't have it yet, and this task's own refusal rule would otherwise block the backfill outright.

PR shape: one PR, branching from main.

Done when: running --dry-run against a doc missing an Issue column refuses with a clear error; --apply against a fixture doc produces the expected issues/Project/header edit; --close against a fixture Project deprioritizes every non-Done item and closes it.

Dependencies

None.

Comments

No comments.

Add a comment