{"success":true,"name":"agentstack_closed_loop_autonomy","prompt":{"name":"agentstack_closed_loop_autonomy","description":"Work Graph default after session: agents.work_next -> plan_claim -> plan_execute (CAS). Follow packet.next_action. Catalog only if no packet.","mcp_domains":["agents","projects","business","discovery","generation"],"onboarding_tier":"domain_playbook","arguments":[{"name":"project_id","description":"Tenant project id (head or organ)","required":true}],"template":"## Closed-loop autonomy (project {project_id})\n\n**Genes:** `core.mcp.inverse_orchestrator.gen1` · `core.agents.plan_graph.gen1` · `core.agents.work_graph.gen1` · `core.agents.orchestrator.gen1` · `core.business.organism.gen1` · `repo.ops.closed_loop.harness.gen1`\n\nOne supported user goal must end **verified complete**, **blocked with a reason**, or **recoverable** — never silent success, lost plan updates, or false completion.\n\n### Loop (server-held state, bounded client steps)\n\n| Stage | MCP / DNA | Agent rule |\n|-------|-----------|------------|\n| **GOAL** | Natural language in `agents.orchestrate` / workspace copilot | One goal per focus; unrelated goal clears `focus_node_id` |\n| **ROUTE** | Bound project: `agents.work_next` first. No `project_id`: `agentstack_session_setup`, then `discovery.search`. | Atom `WG_COLD_START_VS_ROUTE` below. Status-only: `agents.work_status`. |\n| **PLAN** | `agents.plan_get` · `agents.plan_propose` → `agents.plan_apply_proposal` · `agents.plan_patch` (`if_match_revision`) | Read `execution_summary` on validation — atoms below |\n| **WORK** | `agents.work_next` → `agents.ensure_agent` → `agents.plan_claim` → `agents.plan_execute` | Execution ladder atoms below (`dependency_ready` / `execution_ready` / `harness_state`) |\n| **SKILLS** | Unified resolver on routed goal (`instruction_packet.skills`) | Same skill ids on discovery, guidance, orchestrate — not ad-hoc catalog grep |\n| **EXECUTE** | `agents.run` / `agents.orchestrate` | One heavy LLM step per sync batch; `agents.provider_preflight` when `preflight_required` |\n| **EVIDENCE** | Node `evidence` + orchestration slice | Leaf updates only — never full-blob DNA wipe |\n| **VERIFY** | Completion engine (`not_evaluable` ≠ passed) | Diagnostic findings → plan nodes → reconcile |\n| **RECOVER** | `agents.plan_recovery_scan` · `agents.plan_propose` (`mode=repair_plan`) · `generation.realign_to_prod` | `verification_failed` → `recovery.next_actions` — recovery atoms below |\n\n**Inverse orchestration:** external MCP clients read `instruction_packet.allowed_actions` and call **only** those tools per step (`inverse_orchestration_ladder` on `discovery_meta.onboarding`).\n\n### Project self-service (single workspace)\n\n| Need | Actions |\n|------|---------|\n| Orchestrator + copilot | `projects.orchestrator.get` / `patch`, `agents.orchestrate` channel=workspace |\n| Plan backlog | `agents.work_next` → `agents.ensure_agent` (provision) → `agents.plan_claim` → `agents.plan_execute`; drafts via `agents.plan_propose` / `agents.plan_apply_proposal` / `agents.plan_process_backlog` (`env_uuid` on sandbox) |\n| Specialized fleet | `agents.create_from_template`, `agents.team.create` taxonomy goal → templates + scoped capabilities |\n| Diagnostics → repair | `diagnostics.scan` ingest → plan findings; intent `operations.repair` on diag nodes |\n| Tenant DNA safety | `agentstack_safe_project_cycle` — sandbox → diff → gates → promote |\n\n### Business self-service (multi-project organism)\n\nHead project coordinates **organ** children (CRM, storefront, bots) — not a second microservice.\n\n| Need | Actions |\n|------|---------|\n| Bootstrap business | `business.create_composite` (`organs`: crm, storefront, …) |\n| Operator read | `business.command_snapshot`, `business.list_children` |\n| Per-organ work | Set `context.project_id` to **child** project id; same closed loop |\n| Tariff / settings | `business.list_tariff_templates`, `business.apply_tariff`, `business.patch_org_settings` |\n\nPrompt: `agentstack_business_organism` · UI: `/user/projects/:id/business`\n\n### Specialized agent teams (when routed)\n\nWhen `routed_goal.intent_id` is **`agents.team.create`** (or plan decomposes parallel children):\n\n1. `agents.templates_list` → `agents.create_from_template` per role (scoped caps)\n2. Plan graph: parent + `parallelizable` children with `dependencies` / `required_role`\n3. `agents.orchestrate` or child `agents.run` with `parent_run_id`\n4. Team rank policy — atom **`WG_TEAM_OVER_INTEGRATION`** below (not `integrations.*` for team setup)\n\n### Compass + Work Graph playbook\n\nBefore the execution ladder, bind the closed-loop goal to Compass (`agentstack_guidance_compass`):\n\n1. `guidance.list` — confirm `work-graph-agent-loop` is in the project catalog.\n2. `guidance.match_playbook` — `goal_text` e.g. \"work next plan claim execute recovery\".\n3. `guidance.start_path` — `playbook_id` from `match.catalog_entry.id` (or step-0 list id).\n4. Then the work-graph ladder: `agents.work_next` → `agents.ensure_agent` → `agents.plan_claim` → `agents.plan_execute` → evidence → `guidance.path_status` / `guidance.complete_step`.\n5. Recovery: `agents.plan_recovery_scan` when claim stale or WIP full.\n\n### Work-graph atoms (SoT: `shared/mcp/work_graph_instruction_atoms.py`)\n\n- **`WG_COLD_START_VS_ROUTE`:** Cold start: no project_id → agentstack_session_setup then discovery.search. Bound project: skip catalog shopping → agents.work_next first. Status-only poll → agents.work_status (no claim params).\n- **`WG_GUIDANCE_LIST`:** Before work-graph execution: guidance.list to confirm work-graph-agent-loop in catalog; guidance.match_playbook with closed-loop goal_text; guidance.start_path with catalog_entry.id from match.\n- **`WG_HARNESS_STATE`:** Blocked agents.work_next packets include diagnostics.harness_state — read-only projection (dependency_ready, execution_ready, blocked_reason); not a second orchestrator.\n- **`WG_ROLE_TAXONOMY`:** no_compatible_agent for role=data|data_engineer is a taxonomy alias — prefer agents.ensure_agent create=false (H5) then create=true before repair_plan; create_from_template only when ensure_agent is ambiguous; role_matches_template in role_taxonomy.py is SoT.\n- **`WG_FACET_REPAIR`:** plan_propose runs ensure_proposal_facet_coverage (facet_repair, max 2 passes) after enrich for create_plan and decompose_node (including from_draft_tree meta under create_plan) — do not hand-patch semantic facet children; for facet gaps after other modes use mode=repair_plan.\n- **`WG_HARNESS_GATES`:** Dev gates: doctor --full (repo.ops.closed_loop.harness.gen1); key suites: ensure_agent_provision_e2e_v2, ensure_agent_idempotent_retry_v1, bind_vs_claim_semantics_v1, persist_unexecutable_v1, mcp_structured_domain_error_v1, resolver_parity_v1, instruction_plane_dry_v2, planner_runtime_parity_v3.\n- **`WG_HANDOFF_RECOVERY`:** role_mismatch / specialization_mismatch after claim → work_next handoff=true node_id=… — follow recovery.next_actions, do not retry same agent. capability_mismatch is missing required_capabilities: repair_plan or ensure_agent, not handoff.\n- **`WG_TEAM_OVER_INTEGRATION`:** Team-create goals (agents.team.create): discovery rank demotes integrations.* — prefer agents.team.create / agents.orchestrate / agents.plan_* over integrations.oauth_begin.\n- **`WG_EXECUTION_SUMMARY_ONLY`:** Read execution_summary.missing_requirements — missing_roles is legacy; do not infer fleet gaps from validation.missing_roles when execution_summary is present.\n- **`WG_ENV_UUID_PARITY`:** Thread the same env_uuid across plan_get, plan_propose, plan_apply_proposal, plan_process_backlog, plan_recovery_scan, and work_next on sandbox forks.\n- **`WG_ENSURE_H5`:** agents.ensure_agent preview template_id must match agents.work_next top compatible_templates[0] for the same node (harness H5 / resolver_parity_v1).\n- **`WG_ENSURE_CREATE_SAGA`:** Commit-last provision: work_next blocked → ensure_agent create=false (H5 template_id) → create=true provisions with bind_node=False → eligibility gate → rollback on created_agent_not_eligible → next_action agents.plan_claim (bind+lease, if_match_revision); idempotent_replay when bound eligible agent exists.\n- **`WG_PERSIST_UNEXECUTABLE`:** validation_mode=persist_unexecutable on plan_propose / plan_apply_proposal / plan_process_backlog when plan_valid but fleet cannot execute — use the same mode on propose and apply.\n- **`WG_PLAN_VALID_NOT_EXECUTABLE`:** plan_valid / validation_ok means structural+semantic OK — not fleet-executable; use validation_mode=persist_unexecutable or ensure_agent when plan_valid but not execution_ready; executable_now=false under semantic alias is intentional.\n- **`WG_DISCOVERY_ROUTE`:** Discovery naming: intent_id (taxonomy) ≠ primary_action (router verb) ≠ agents.work_next (fleet pick) — do not treat a route phrase as an MCP action name.\n- **`WG_DEPENDENCY_VS_EXECUTION_READY`:** dependency_ready is structural (deps/status/decomposition); execution_ready is fleet (live agent or provision path) — never collapse the two gates.\n- **`WG_BIND_VS_CLAIM`:** node.agent_id binding ≠ active claim lease — already_claimed requires status=in_progress with non-expired lease meta; bound agent on ready/pending is not already_claimed; retry ensure_agent idempotent_replay or plan_claim with if_match_revision.\n- **`WG_CAS_RETRY`:** plan_revision_conflict → plan_get reload if_match_revision — never apply stale proposal after concurrent plan_patch or backlog apply.\n- **`MCP_DOMAIN_ERROR`:** Domain denial: tools/call isError=false with structuredContent (ok=false, error_code, failure_kind=domain, recovery). Transport/internal uses isError=true (failure_kind=transport). plan_revision_conflict → agents.plan_get then retry with fresh if_match_revision.\n- **`WG_RECOVERY_VERIFICATION_FAILED`:** Recovery reason SoT is verification_failed (aliases verify_failed, plan_verify_failed) — route via recovery.next_actions, not missing_roles.\n- **`WG_CLOSED_LOOP_STALE_CLAIM`:** blocked_reason=expired_claim — next_action agents.plan_reclaim_stale; beats downstream dependency_blocked when no executable leaf.\n- **`WG_CLOSED_LOOP_EXECUTABLE`:** state=executable and execution_ready — next_action agents.plan_claim with if_match_revision from plan_get.\n- **`WG_CLOSED_LOOP_PROVISION`:** resolution=provision_required — next_action agents.ensure_agent create=false (H5) then create=true before plan_claim.\n- **`WG_CLOSED_LOOP_PLANNING`:** planning_required — next_action agents.plan_propose mode=create_plan or decompose_node.\n- **`WG_CLOSED_LOOP_VERIFY_FAIL`:** verification_failed — next_action agents.plan_recovery_scan then repair_plan.\n- **`WG_CLOSED_LOOP_DEPENDENCY`:** dependency_blocked — no claim; reason names upstream node_id until deps complete.\n- **`WG_STORAGE_NOT_KNOWLEDGE`:** File upload / document persistence → storage.* (MCP operator or REST binary upload). Do not ingest to knowledge unless the goal requests RAG/Q&A/semantic search.\n- **`WG_MCP_SDK_REST_PLANE`:** MCP: operator/agent configure + work graph. SDK: app runtime integration. REST: binary transfer or capability not in SDK. Never duplicate platform CRM/Auth/Storage.\n- **`WG_VERIFY_AFTER_WRITE`:** After each platform mutation, verify via canonical read on meta.verify_after_write_action (storage.list_files, crm.list_contacts, logic.dry_run, rag.search) — attach evidence, do not mark complete from agent prose alone.\n- **`WG_GRAPH_FOR_DURABLE_WORK`:** Durable work (more than one read) enters the Work Graph: agents.plan_propose (create_plan or decompose_node), agents.plan_apply_proposal when validation.safe_to_auto_apply, then agents.work_next following next_action until state=idle, blocked, or recoverable. A single domain call (one CRM write, one file upload, discovery.search, auth.get_profile) stays a direct MCP action. Do not mark a node complete from prose — attach meta.verify_after_write_action evidence. Idle with an empty graph is valid.\n- **`WG_PLAN_FIRST`:** Lightweight progress: agents.work_status (summary_only). Full autonomous loop: agents.work_next — follow next_action, not next_actions[0] parsing.\n- **`WG_CLAIM_BEFORE_EXECUTE`:** agents.plan_execute requires an active claim lease from agents.plan_claim unless operator policy allows execute-without-claim; pass if_match_revision from plan_get.\n- **`WG_BLOCKED_REASON`:** blocked_work[].blocked_reason is canonical (block_reason.REASON_PRIORITY); use recovery.next_actions — not validation.missing_roles.\n- **`WG_PROVISION_PARITY`:** When execution.fully_provisionable and one top template, recovery routes agents.ensure_agent create=true (preferred over create_from_template); ambiguous ties → create_from_template choose_template.\n- **`WG_CAPABILITY_HARD_GATE`:** required_capabilities are mandatory — capability_mismatch blocks claim; route agents.ensure_agent create=false (H5) then create=true, or plan_propose mode=repair_plan; not handoff (capability_hard_gate_v1).\n- **`AGENTSTACK_NATIVE_FIRST`:** Before custom servers: discovery.search → domain workflow → MCP/SDK configure → verify via canonical read. Frontend consumes @agentstack/sdk — not operator Bearer.\n- **`WG_WORK_NEXT_LOOP`:** Closed loop: plan_get → work_next → next_action → execute/claim → work_next until state=idle. Blocked: read recovery.next_actions or singular next_action.\n\n### Harness invariants (H6–H11)\n\n- **H6** required_capabilities are mandatory — capability_mismatch blocks claim; route agents.ensure_agent create=false (H5) then create=true, or plan_propose mode=repair_plan; not handoff (capability_hard_gate_v1).\n- **H7** Commit-last provision: work_next blocked → ensure_agent create=false (H5 template_id) → create=true provisions with bind_node=False → eligibility gate → rollback on created_agent_not_eligible → next_action agents.plan_claim (bind+lease, if_match_revision); idempotent_replay when bound eligible agent exists.\n- **H8** Backlog scan uses planner eligibility (decomposition_status=needed) — not execution_ready; validation_ok reflects plan_valid (structural+semantic), not fleet gaps.\n- **H9** plan_propose runs ensure_proposal_facet_coverage (facet_repair, max 2 passes) after enrich for create_plan and decompose_node (including from_draft_tree meta under create_plan) — do not hand-patch semantic facet children; for facet gaps after other modes use mode=repair_plan.\n- **H10** meta.planning_artifact nodes are evidence-only (resolution=non_executable_artifact) — attach evidence on parent; plan_process_backlog and claim skip them for execution.\n- **H11** node.agent_id binding ≠ active claim lease — already_claimed requires status=in_progress with non-expired lease meta; bound agent on ready/pending is not already_claimed; retry ensure_agent idempotent_replay or plan_claim with if_match_revision.\n\nOperator H1–H11 map: `docs/plans/AGENT_HARNESS_ARCHITECTURE_MAP.md` · H6–H11 in execution-loop numbering (not engineering §4 rows).\n\n### Discovery hints\n\n- Workflow mode: `slots.instruction_packet` + `router_primary_action` on selected workflow\n- Quality gates on route: honor `quality_gates` before `plan_complete`\n- Platform gate (dev): `npm run audit:closed-loop-harness` · gene `repo.ops.closed_loop.harness.gen1`\n\n### Anti-patterns\n\n- Full `projects.patch_data` blob for one leaf\n- Re-route every tool call (use `route_source=plan_node` on `plan_execute`)\n- Mark complete with empty `completion_predicates` (status **`not_evaluable`**, not passed)\n- Tenant prod DNA edit without generation sandbox (see `agentstack_tenant_8dna_supply`)\n- Treat `validation.missing_roles` as authority when `execution_summary` is present\n- Pass mismatched `env_uuid` between `plan_propose` and `plan_apply_proposal` / `plan_process_backlog`\n- Retry the same agent after `specialization_mismatch` or `role_mismatch` — only `work_next handoff=true` (not an API-key limit, not ensure_agent)\n- Treat `capability_mismatch` as handoff — use `ensure_agent` or `plan_propose mode=repair_plan`\n- Call `ensure_agent create=true` without previewing `template_id` (H5 parity)\n- Treat `validation.ok` as fleet-ready when `validation_mode=persist_unexecutable` — check `plan_valid` + `execution_summary` separately\n\n\n### Template (not executable)\n\nRecipe `mcp_work_loop_v1` is a template. Fill required inputs ((none)) via `options.recipe_params` before any side effect. Do not copy empty params, OLD/NEW, or an unconditional promote.\n"}}