
Eliteforge Codex Superpower
- 23 installs
- Updated July 24, 2026
- cloudsen/eliteforge-skills
A lean acceptance-criteria-driven Codex workflow for EliteForge feature delivery with selective multi-agent spawning for TDD, coding, and review.
About
Defines an AC-only Codex workflow for EliteForge feature delivery covering design, DB/API contracts, TDD execution, and selective multi-agent delivery, resolving Codex runtime agent types before each spawn. A developer uses it for feature design, contracts, TDD, or multi-agent delivery.
- Env vars map TDD/coding/reviewer roles to Codex spawn_agent types
- Resolve runtime agent types (worker/explorer/coder) before every spawn
Eliteforge Codex Superpower by the numbers
- 23 all-time installs (skills.sh)
- Ranked #10,032 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cloudsen/eliteforge-skills --skill eliteforge-codex-superpowerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 23 |
|---|---|
| Last updated | July 24, 2026 |
| Repository | cloudsen/eliteforge-skills ↗ |
What it does
A lean acceptance-criteria-driven Codex workflow for EliteForge feature delivery with selective multi-agent spawning for TDD, coding, and review.
Files
EliteForge Codex Superpower
Environment Variables
ELITEFORGE_SKILL_SUPERPOWER_TDD_AGENT[optional] Codex runtimespawn_agent.agent_typefor bounded AC TDD slice workers; defaults toworker.ELITEFORGE_SKILL_SUPERPOWER_CODING_AGENT[optional] Codex runtimespawn_agent.agent_typefor coding subagents such as backend/frontend/database implementation workers; defaults toworker.ELITEFORGE_SKILL_SUPERPOWER_REVIEWER_AGENT[optional] Codex runtimespawn_agent.agent_typefor verify and review workers; defaults toworker.
Codex Runtime Agent Selection
Environment variable values are Codex runtime agent types passed to spawn_agent.agent_type, not workflow role names. Valid values must come from currently available Codex agent types such as worker, explorer, coder, or coder-spark.
Resolve runtime agent types before every spawn_agent call:
- TDD slice workers:
ELITEFORGE_SKILL_SUPERPOWER_TDD_AGENT->worker. - Backend, frontend, database:
ELITEFORGE_SKILL_SUPERPOWER_CODING_AGENT->worker. - Verify, review:
ELITEFORGE_SKILL_SUPERPOWER_REVIEWER_AGENT->worker. - Explorer: always
explorer.
If a configured environment variable is set to a value that is not an available Codex runtime agent type, stop before spawning and record the invalid value. Fall back to worker only when no configured variable in the resolution chain is set.
For every non-explorer worker spawn, pass the resolved value explicitly as spawn_agent.agent_type. If resolution returns coder-spark, call spawn_agent with agent_type: "coder-spark". Do not omit agent_type, do not rely on default spawn behavior, do not replace it with a workflow role, and do not use fork_context: true with an explicit agent_type; pass a focused prompt with required AC rows, command aliases, write scope, and worktree context instead.
Workflow roles such as tdd-slice-worker, backend-worker, frontend-worker, database-worker, and review-worker belong only in task ownership, prompts, worktree plans, branches, and report filenames. They are never Codex runtime agent_type values.
Workflow Nature
Default model:
lead agent + explorer subagents -> approved brainstorming design -> explorer-assisted PRD synthesis -> approved PRD -> repo-aware feature selection -> selected feature model -> AC index -> proof rows + command index -> AC task rows -> AC evidence rowsThis skill is AC-only across documents. The only global business traceability ID is AC-*. Do not create global INT-*, TDD-*, TASK-*, EV-*, or BVE-* IDs. Page interactions, TDD cases, tasks, and browser evidence are fields under the owning AC row.
Execution Modes
solo: explicit fallback only. Lead handles docs, coding, validation, and evidence for a small bounded AC slice on the selected-git-spec main/base branch after recording the reason.hybrid: default. Lead owns docs, integration, and final judgment; behavior-changing AC slices should use onetdd-slice-workerunless the user explicitly approves solo or the slice is documentation-only/trivial.parallel: use multiple workers only when contracts are stable, write scopes are disjoint, and splitting reduces wall-clock time.
Default task unit: one bounded AC slice with phase: tdd, covering RED -> GREEN -> focused VERIFY. Do not split RED worker, GREEN worker, and review worker by ceremony. Split backend/frontend/database workers only for real fan-out.
Base branch rule: selected-git-spec main/base branch creation is mandatory before behavior-changing execution. solo means no delegated worker, not "work directly on the original checkout." Even solo execution must happen from the selected-git-spec main/base branch and be recorded in orchestration/worktree-plan.md.
Dispatch decision rule: every behavior-changing task row (phase: tdd|red|green|refactor) must have a row in orchestration/worktree-plan.md#Dispatch Decisions.
Decision order: 1. If the row is documentation-only, exploration, support, integration, review, or verify-only, do not spawn for implementation. 2. If the user explicitly approved solo or the row is genuinely trivial, choose solo and record the approval/trivial rationale. 3. If subagents are unavailable or git worktree setup is blocked, choose blocked and stop for user decision. 4. If contracts are stable, write scopes are isolated, and splitting reduces wall-clock, choose parallel. 5. Otherwise choose spawn; default hybrid means one bounded AC implementation worker owns RED -> GREEN -> focused VERIFY.
Before each spawn or parallel dispatch, resolve and record the delegated worker runtime_agent_type in orchestration/worktree-plan.md. User-facing dispatch summaries must name both the workflow owner and runtime type, for example execution_owner=tdd-slice-worker and runtime_agent_type=coder-spark.
Grouping: do not mechanically spawn one worker per AC when adjacent AC rows share the same files, command aliases, and interaction surface. One spawned worker may own multiple tightly coupled AC rows if the prompt remains bounded. Do not group AC rows that require disjoint backend/frontend/database fan-out, unrelated files, or independent browser flows.
Single Source Of Truth
- Feature model and boundary:
spec/feature-spec.md; noAC-*, commands, RED/GREEN, or evidence. - Acceptance index:
spec/acceptance.md; the only file that definesAC-*. - Proof plan:
spec/test-plan.md; proof rows byAC-*plus localCMD-*command aliases. - Task rows:
tasks/*.md; execution slices byAC-*, usuallyphase: tdd. - Runtime/process state:
orchestration/*.md;worktree-plan.mdrecords the mandatory selected-git-spec main/base branch, andcontext-index.mdprovides navigation. - Evidence ledgers:
reports/*.md; RED/GREEN/VERIFY/browser evidence byAC-*.
All documents other than the owner file reference facts using AC-*, local CMD-*, or @ref:<relative-file>#<anchor>. Do not duplicate expanded facts across files.
Context Budget
- Generate or refresh
orchestration/context-index.mdbefore dispatch, review, or final validation. - Read
context-index.mdfirst, then open only the needed file, heading, and AC row. - For approved brainstorming and PRD inputs, search by stable headings first. Use module, feature, page, rule, exception, and acceptance heading text from the PRD; open only matching sections. Do not paste a whole brainstorming spec or whole PRD into a feature draft or worker prompt.
- Worker prompts include only relevant AC rows, command aliases, write scope, and referenced sections. Do not paste whole
spec/,tasks/, orreports/. - Reports and worker fragments keep deltas and evidence only; they must not restate feature requirements, AC prose, or page design.
External Browser Skill Compatibility
External browser/testing skills, including webapp-testing, are capability providers only. Do not edit or depend on their EliteForge document contract if it conflicts with this skill.
When delegating browser work, the lead prompt must provide the AC-only contract explicitly:
- Inputs: assigned
AC-*rows, matching proof rows,CMD-*command aliases, route/surface references, and write scope. - Output:
reports/*.mdevidence rows keyed byAC-*. - Browser evidence fields: clicks, inputs, network assertions, visible feedback, and artifacts.
- Forbidden outputs:
test-scope.md, globalTDD-*,TASK-*,INT-*,EV-*, orBVE-*IDs.
Use external browser skills for Playwright technique, server lifecycle, selector discovery, screenshots, traces, and logs. This skill remains the source of truth for document names, IDs, report shape, and validation.
Workspace Contract
Use docs/features/<feature-slug>/ with spec/, tasks/, orchestration/, and reports/. Use assets/ templates. Derive <feature-slug> from product feature name (lowercase kebab-case). Do not derive branch names, worktree paths, or agent names from this skill.
One workspace represents one bounded feature only. Do not use one docs/features/<feature-slug>/ tree to hold an entire broad brainstorming spec or whole-product PRD that still contains several independent capabilities.
Before any behavior-changing execution, select the active git skill:
eliteforge-git-feature-oriented-spec(full spec)eliteforge-git-feature-oriented-lite-spec(lite spec)- If undeclared, stop and ask user
Use the chosen git skill as the only source for main branch, worktree path, multi-agent base, subagent branch, agentId, and subtaskName. Create or verify the selected-git-spec main/base branch before implementation, even when no worker is spawned. Record Codex runtime id separately in worktree-plan.md.
If files exist, preserve decisions and update in place.
Brainstorming And PRD Prerequisites
Before entering New Feature Delivery or Feature Evolution, activate eliteforge-brainstorming, then eliteforge-prd-generator.
- Use
eliteforge-brainstormingto converge scope, alternatives, and design decisions before feature drafting or reopening. - Use
eliteforge-prd-generatorto turn the approved brainstorming output into a searchable product definition. The PRD must carry feature requirements by module, page prototypes and interactions, business rules, exception/boundary cases, shared requirements, feature inventory, dependency map, recommended implementation order, and product-level acceptance. - Keep the handoff non-duplicative: brainstorming owns why, alternatives, and decisions; PRD owns what must be built and how users, pages, rules, exceptions, and boundaries behave.
- Reuse is allowed only when an already approved brainstorming spec or approved PRD still matches the current request and repository state, and the user approves reuse.
- Do not let the lead agent carry brainstorming, PRD synthesis, and feature selection alone. Use read-only
explorersubagents for repo/context scan before or during brainstorming, then again for PRD input synthesis and repo-aware feature selection. - After the PRD is approved, the lead agent must select one bounded feature from PRD headings, confirm shared requirements, page/rule/exception sources, dependency relations, and implementation order against repository reality, then select or reopen one feature workspace.
- Do not start
references/zero-one-delivery.mdorreferences/feature-evolution.mduntil brainstorming and PRD are approved or the user explicitly approves reuse of existing approved upstream docs.
Workflow Selection
New Feature Delivery: after the brainstorming and PRD prerequisites pass, no existing feature workspace, starting new feature 0-1.
Feature Evolution: after the brainstorming and PRD prerequisites pass, changing, refactoring, extending, narrowing, or reopening existing workspace. Re-enter gates from earliest affected design point.
Both start from approved brainstorming output plus an approved PRD, then share the same feature-selection gate, AC-only documents, validators, git-spec constraints, context-index rule, and execution-mode rules.
Non-Negotiable Rules
1. Run brainstorming first. Start with eliteforge-brainstorming before any new feature 0-1 flow or feature evolution, unless the current request already has an approved brainstorming spec that the user approves for reuse. 2. Turn approved brainstorming into PRD. After brainstorming approval, activate eliteforge-prd-generator to write or refine the searchable product definition that superpower will use as its product-layer input. 3. Use subagents for brainstorming, PRD, and feature selection. Before or during brainstorming, dispatch read-only explorer subagents for repo/context scan. After brainstorming approval, dispatch read-only explorer subagents again for PRD input synthesis, then for repo-aware feature selection, heading search, and dependency validation. 4. Select one bounded feature before docs. After PRD approval, select one target feature from the approved product definition before creating or reopening docs/features/<feature-slug>/; capture only the relevant PRD heading references, not the whole PRD. 5. Make shared requirements explicit. Identify common capabilities across candidate features, decide whether they stay feature-local or become foundational work, and prioritize true shared dependencies before dependent features. 6. Make dependencies and order explicit. Record feature-to-feature dependencies, blocking relations, and recommended implementation order before Phase 4 drafting begins. 7. Draft before interrogation. Produce an opinionated draft first. Ask 3-5 blocking questions per round only after a draft exists. 8. Keep AC-only traceability. AC-* is the only global business ID across documents. 9. Keep feature spec clean. spec/feature-spec.md must not contain AC-*, TDD-*, commands, RED/GREEN, browser evidence, or execution logs. 10. Define ACs once. spec/acceptance.md owns all AC-*; each AC is one compact completion judgment line. 11. Use proof rows, not global TDD IDs. spec/test-plan.md proves ACs with case, red, green, optional interaction, and command_ref. 12. Centralize commands. CMD-* aliases live only in spec/test-plan.md#command-index; slow/browser commands require cost and rerun policy. 13. Spec interactions as AC fields. UI interactions are represented by interaction ACs and interaction: proof fields, not separate INT-* files or IDs. 14. Require concrete browser evidence. Browser evidence must list clicks, inputs, network assertions, visible feedback, and artifacts. smoke passed is not evidence. 15. Prefer bounded AC slices. Default phase: tdd task rows cover RED -> GREEN -> VERIFY for one AC slice. 16. Do not default to solo. Default execution is hybrid; solo requires explicit user approval or a recorded docs-only/trivial rationale. 17. Spawn for benefit, not ceremony. Use one tdd-slice-worker for behavior-changing AC slices when tool support is available; add more workers only for real parallelism, isolation, or long-running execution value. 18. Keep lead/worker roles explicit. Lead owns synthesis, gates, integration, review, and completion claims. Subagents own repository scan, PRD input, feature-selection input, and assigned AC slices only. 19. Respect command cost. REVIEW reuses slow/browser VERIFY evidence unless missing, failed, stale, or explicitly requested. 20. Treat external browser skills as tools. Do not edit external skills or inherit legacy EliteForge output contracts from them; the lead prompt must enforce this skill's AC-only browser evidence shape. 21. Separate git identity from Codex runtime. agentId and subtaskName come from the git skill; Codex runtime ids come from spawn_agent. 22. Create base branch before execution. orchestration/worktree-plan.md must record resolved main branch, main worktree path, and multi-agent base branch before implementation. 23. No worker edits lead/root worktree. Delegated workers use selected-git-spec worktrees; approved solo execution uses the selected-git-spec main/base branch and must be recorded in tasks/reports. 24. RED before GREEN. Every behavior-changing AC slice needs RED failure evidence before GREEN evidence. 25. Do not silently steal live worker scope. If a worker owns an AC slice, lead must not duplicate it without recording the decision. 26. Do not infer stalls from time. A running subagent remains in_progress unless platform failure, empty completion, user interruption, or approved timebox breach occurs. 27. Do not hide execution gaps. If subagents are unavailable, shared-file races appear, or evolution invalidates artifacts, record the blocker and stop for user decision.
Reference Index
Load references only when needed:
| need | reference |
|---|---|
| Design prerequisite | eliteforge-brainstorming |
| Product definition handoff | eliteforge-prd-generator |
| PRD-to-feature selection | references/feature-decomposition.md |
| New feature delivery workflow | references/zero-one-delivery.md |
| Existing feature evolution workflow | references/feature-evolution.md |
| TDD quality rules | references/tdd-workflow.md |
| Execution modes and worker prompts | references/subagent-orchestration.md |
| Worktree setup and integration | selected git skill, then references/git-worktree-orchestration.md |
| Database design guidance | references/database-design.md |
Completion Gate
Complete only when: active validation passes, reached gates have approved or approved-with-defaults decisions, every AC-* has proof rows, task rows, and final evidence rows, browser evidence is concrete when UI/browser is claimed, slow/browser reruns follow policy, and unresolved blockers are recorded.
Feature Evolution additionally requires: change classification, downstream impact analysis, affected-gate validation, stale artifact status, and old/new AC evidence migration.
version: 1
agents:
main:
role: Lead AC-Only Orchestrator
description: First agent that activates the skill; orchestrates explorer-assisted brainstorming, PRD handoff, repo-aware feature selection, user gates, AC-only docs, context-index navigation, execution-mode choice, selected git-spec orchestration, integration, and final review.
explorer:
role: Explorer Agent
description: Read-only support agent for repository inspection, brainstorming prep, PRD input synthesis, feature selection, shared-requirement discovery, and dependency/order analysis. No edits.
tdd-slice-worker:
role: TDD Slice Worker
description: Owns one bounded AC RED to GREEN to focused VERIFY slice in a dedicated worktree when delegation is worthwhile.
backend-worker:
role: Backend Worker
description: Implements or verifies backend AC slices after a stable proof row and contract exist.
frontend-worker:
role: Frontend Worker
description: Implements or verifies frontend AC slices with explicit interaction proof and concrete browser evidence expectations.
database-worker:
role: Database Worker
description: Implements migrations, persistence, and DB verification after approved database design and stable AC proof rows.
verify-worker:
role: Verify Worker
description: Runs approved validation commands after integration; browser evidence must list clicks, inputs, network assertions, visible feedback, and artifacts.
review-worker:
role: Review Worker
description: Reviews AC traceability, command policy, concrete browser evidence, and final acceptance; reuses slow browser evidence unless missing, failed, stale, or explicitly requested.
Acceptance: {{FEATURE_NAME}}
Acceptance Index
Only this file defines global business traceability IDs. Use one compact line per AC.
Format: AC-<MODULE>-NN [must|should|could] [business|interaction|api|database|security|regression] Completion judgment; proof: pending.
- AC-MODULE-01 [must] [business] Business outcome is satisfied; proof: pending.
Rules:
- Do not restate the feature background.
- Do not describe implementation steps.
- Do not write test commands.
- Do not create
INT-*,TDD-*,TASK-*,EV-*, orBVE-*IDs. - Interaction ACs describe observable user behavior in one line; detailed proof belongs in
spec/test-plan.md.
Valid types: business, interaction, api, database, security, regression.
Open Decisions
- None.
API Contract: {{FEATURE_NAME}}
API Overview
Summarize external and internal API behavior.
Endpoints
{{METHOD}} {{PATH}} - {{INTERFACE_NAME}}
- purpose: {{purpose}}
- auth: {{roles or permission rule}}
- acceptance_ids: AC-MODULE-01
- parameter_passing: {{path variables | query parameters | request headers | request body | multipart form data | mixed}}
- request: {{DTO name, key fields, validation}}
- response: {{DTO name, key fields, serialization}}
- client_impact: {{generated client method/types or none}}
- compatibility: {{breaking changes, migration path, deprecated fields, or none}}
Auth And Permissions
Allowed roles, denied roles, ownership checks, tenancy rules, audit requirements.
Error Contract
Format: condition -> status/code; response; AC id
- invalid request -> 400 / SERVICE_CODE; unified error response; AC-MODULE-01
Frontend Backend Boundary
Frontend responsibilities, backend responsibilities, shared types, route dependencies, generated client expectations.
Database And Transaction Boundary
Transaction boundaries, persistence responsibilities, locking behavior, consistency expectations, database error mapping.
Mock Or Adapter Expectations
Mocks, external adapters, service virtualization, test doubles, deterministic test seams.
Database Design: {{FEATURE_NAME}}
Database Decision
- status: draft
- impact: unknown
- approved_by: none
- approved_at: none
Use not_applicable when no database/persistence impact. Use approved only after user acceptance.
Existing Schema Impact
List affected tables, views, indexes, constraints, migrations, repositories, ORM entities, caches, reports, and downstream consumers.
Proposed Schema
{{object_name}}
- change_type: create|alter|drop|none
- fields: {{columns or fields}}
- constraints: {{constraints}}
- indexes: {{indexes}}
- notes: {{compatibility/migration notes}}
Relationships And Cardinality
Foreign keys, ownership, cascade, uniqueness, nullable relationships, domain invariants.
Migration Plan
Order, backward compatibility, migration names, backfills, defaults, lock risks, sequencing, rollback.
Query And Performance Plan
Primary query paths, pagination, sorting, filters, index coverage, cardinality, concurrency, locking, risks.
Data Governance
Tenancy, permissions, audit, soft delete, retention, privacy, encryption, regulatory, access logging.
Test Data And Fixtures
Seed data, factories, fixtures, containers/DB, cleanup, migration verification.
Validation Commands
<command>: purpose; expect: evidence.
Feature Evolution Log: {{FEATURE_NAME}}
Summary
- feature_slug: {{FEATURE_SLUG}}
- total_changes: 0
- first_evolution: pending
- last_evolution: pending
Change History
Record every evolution event. spec/feature-spec.md remains current requirement; this log preserves historical trail.
#1
- change_id: 1
- timestamp: pending
- trigger: {{user_request|spec_correction|requirement_change|bug_discovery|scope_adjustment|documentation_only}}
- change_type: {{requirement_expansion|requirement_contraction|feature_model_change|database_change|api_contract_change|acceptance_change|proof_plan_change|task_execution_change|doc_correction_only}}
- gate_reopened: {{Phase 3|Phase 4A|Phase 4B|Phase 4C|Phase 4D|Phase 4E|Phase 5|Phase 6|none}}
- diff_summary: |
What changed vs before:
- {{before}}
+ {{after}}
- downstream_impact: {{feature-spec|database-design|api-contract|acceptance|test-plan|tasks|worktrees|reports|none}}
- old_evidence_status: {{retained|superseded|replaced|not_applicable|none}}
- user_approval: pending
Downstream Artifact Migration
Record how old tasks, report rows, and worktrees were handled. Do not overwrite old evidence.
- None yet.
Feature Spec: {{FEATURE_NAME}}
This file covers one bounded feature selected from the approved PRD-backed feature split. Do not use it to restate several independent features from one broad brainstorm spec or whole-product PRD.
Initial Agent Draft
- drafted_from_requirement: yes
- drafted_from_repository_findings: pending
- best_practice_defaults_applied: pending
- user_confirmation_status: waiting_human_decision
Summarize the first-pass interpretation, recommended defaults, assumptions, and at most 3 blocking confirmation questions. Do not define AC-*, test cases, commands, or evidence here.
Feature Identity And Context
- feature_slug: {{FEATURE_SLUG}}
- feature_name: {{FEATURE_NAME}}
- status: draft
- owner: {{OWNER}}
- source_request: {{SOURCE_REQUEST}}
- main_agent: first agent that activated this skill
- dependency_features: pending scan
- dependency_contracts: pending scan
Requirement
- user_story: {{USER_STORY_OR_NOT_APPLICABLE}}
- intent: {{PROBLEM_TO_SOLVE}}
- outcome: {{EXPECTED_USER_OR_BUSINESS_OUTCOME}}
Product Source Slices
Record only the upstream sections used to draft this bounded feature. Use exact PRD heading text so agents can search with rg and open the relevant section only.
- brainstorming_source: {{path and heading, or approved reuse note}}
- prd_feature_headings: {{Feature: module - feature name}}
- prd_page_headings: {{Page/Interaction headings or not applicable}}
- prd_rule_headings: {{Rule headings or not applicable}}
- prd_exception_headings: {{Exception headings or not applicable}}
- prd_acceptance_headings: {{Acceptance headings or not applicable}}
Scope Boundary
Assumptions
- {{ASSUMPTION}}
In Scope
- {{IN_SCOPE_ITEM}}
Out Of Scope
- {{OUT_OF_SCOPE_ITEM}}
Constraints
- {{CONSTRAINT}}
Feature Model
Describe only stable feature concepts: domain objects, state transitions, core business rules, permissions, tenancy, and invariants for this selected feature. Source the business rules from the PRD rule headings listed above, then rewrite them in repository-aware terms. Do not write acceptance criteria or test proof.
Page And Module Surface
List affected pages, routes, entry points, modules, APIs, database areas, shared libraries, generated clients, adapters, external services, and ownership boundaries.
When UI is in scope, describe the page/module surface enough for spec/acceptance.md to derive interaction ACs: entry, layout regions, controls, button behavior, form rules, visible states, navigation, and feedback. Source this from the PRD page and interaction headings listed above. Do not create interaction IDs.
If UI is not in scope, write exactly UI: Not applicable.
Repository Findings
Record only findings that affect design or execution: docs, modules, routes, migrations, tests, package manifests, CI commands, related feature folders, and strong dependencies.
For dependency scans, write either strong dependencies with feature names/contracts or:
Strong feature dependencies: none found after scanning <n> feature-spec.md files under <features-root>.
Open Decisions
Before writing None, identify whether this feature introduces reusable capability that may be needed by other features, bounded contexts, frontend pages, backend services, generated clients, adapters, or test utilities.
Use compact lines:
{{capability}}; signal: {{reuse signal}}; modules: {{affected modules}}; target: {{shared module/path}}; recommendation: extract now / defer / keep feature-local; decision: waiting_human_decision.
If no candidate exists, write Common capability extraction: no candidate found with a short rationale.
Integration Log: {{FEATURE_NAME}}
Summary
Integration result, highest-risk delta, unresolved blockers (1-2 lines).
Integration Events
#1
- event_id: 1
- worktree_ref: @ref:orchestration/worktree-plan.md#agent-worktrees
- ac_ref: AC-MODULE-01
- task_ref: @ref:tasks/{{TASK_FILE}}#task-rows
- action: merge|cherry-pick|patch-apply|validate|conflict-resolution
- command_ref: @ref:spec/test-plan.md#command-index
- result: pending
- evidence: pending
Conflict Log
- None.
Validation After Integration
- @ref:spec/test-plan.md#command-index: pending evidence.
Blockers
- None.
{{REPORT_KIND}} Report: {{FEATURE_NAME}}
Summary
Result, highest-risk delta, final status (1-2 lines). Do not restate feature requirements.
Evidence Rows
Use one compact row per AC. Do not create global evidence IDs.
Format: AC-<MODULE>-NN | red: <failure summary or not applicable> | green: <pass summary or not applicable> | command_ref: CMD-<ALIAS>|none | browser: clicks=<...>; inputs=<...>; network=<...>; feedback=<...>; artifacts=<...> | status: <pending|done|blocked|not_applicable>.
- AC-MODULE-01 | red: pending | green: pending | command_ref: CMD-BE-TEST | browser: not applicable | status: pending.
Changed Paths
- None yet.
Commands
- @ref:spec/test-plan.md#command-index: pending.
Blockers
- None.
{{TASK_KIND}} Task: {{FEATURE_NAME}}
Execution Contract
- feature_slug: {{FEATURE_SLUG}}
- status: draft
- execution_mode: solo|hybrid|parallel
TDD Contract
Default to one bounded tdd row per AC slice. A tdd row owns RED -> GREEN -> focused VERIFY for the AC inside one write scope.
Only split into multiple workers when contracts are stable, write scopes are disjoint, and splitting reduces wall-clock time.
Task Rows
Format: AC-<MODULE>-NN | phase: <tdd|docs|explore|support|verify|review|integrate|red|green|refactor> | owner: <lead|role> | scope: <paths/modules> | command_ref: CMD-<ALIAS>|none | status: <status>.
- AC-MODULE-01 | phase: tdd | owner: tdd-slice-worker | scope: tests/... src/... | command_ref: CMD-BE-TEST | status: draft.
Worktree Notes
Record whether each AC row runs in the lead worktree or a dedicated worker worktree, plus any split-worker rationale.
Integration Notes
Record cross-team contracts, shared DTOs, route dependencies, migration ordering, fixtures, generated clients, and handoff notes.
Open Decisions
- None.
Test Plan: {{FEATURE_NAME}}
Test Plan Decision
- status: waiting_human_decision
- approved_by: none
- approved_at: none
Valid statuses: waiting_human_decision, approved, approved_with_defaults.
Proof Rows
Each row proves one AC or one local case of an AC. Use multiple rows for multiple cases; do not create global TDD IDs.
Format: AC-<MODULE>-NN | layer: <unit|integration|api|database|frontend|browser|security|regression> | case: <local case description> | red: <expected failure before behavior> | green: <same proof passes> | interaction: <user action -> feedback -> network or not applicable> | command_ref: CMD-<ALIAS>.
- AC-MODULE-01 | layer: api | case: valid request returns expected result | red: proof fails before behavior | green: same proof passes | interaction: not applicable | command_ref: CMD-BE-TEST.
Command Index
CMD-* aliases are local to this file. They are command de-duplication labels, not business traceability IDs.
Format: - CMD-<ALIAS> | command: <command> | cost: <fast|medium|slow/browser> | when: <red|green|verify|final|review-if-stale> | rerun: <policy> | expect: <evidence>.
- CMD-BE-TEST | command: make be-test | cost: medium | when: red/green/verify | rerun: affected backend changes | expect: focused pass/fail output.
- CMD-FE-BROWSER | command: npm --prefix <frontend> exec playwright -- test <spec> | cost: slow/browser | when: verify | rerun: review-if-stale | expect: clicks, inputs, network assertions, visible feedback, artifacts.
Slow/browser commands: VERIFY runs once per relevant change; REVIEW reuses evidence unless missing, failed, stale, or user requests fresh run.
Evidence Rules
- Every
AC-*fromspec/acceptance.mdmust have at least one Proof Row. - Browser or UI interaction proof must include concrete interaction text in the Proof Row.
- Final reports must record RED, GREEN, command output, and browser evidence fields when browser proof is claimed.
- If a layer is not applicable, state
not applicablein the relevant field.
Worktree Plan: {{FEATURE_NAME}}
Main Agent
- feature_slug: {{FEATURE_SLUG}}
- main_agent: first agent that activated this skill
- main_codex_runtime_id: current-thread
- main_codex_runtime_name: lead-agent
- git_skill: eliteforge-git-feature-oriented-spec | eliteforge-git-feature-oriented-lite-spec
- execution_mode: hybrid
- main_branch: unknown
- main_worktree_path: unknown
- status: draft
Repository State
- root_worktree_path: unknown
- current_branch: unknown
- selected_git_skill_verified: false
Multi-Agent Base Branch
- branch: unknown
- base_ref: unknown
- merge_strategy: merge
- conflict_owner: Main Agent
Dispatch Decisions
One row per behavior-changing task row (phase: tdd|red|green|refactor). Default hybrid decision is spawn; use solo only with explicit approval or a genuinely trivial rationale.
| ac_refs | decision | execution_owner | rationale | worker_ref | status |
|---|---|---|---|---|---|
| AC-MODULE-01 | spawn | tdd-slice-worker | bounded AC implementation owns RED -> GREEN -> VERIFY | WT-01 | planned |
Agent Worktrees
One block per delegated worker. Git identity from selected git skill. Codex runtime id/name filled after spawn. Hybrid execution may have zero worker rows only when solo fallback is explicitly approved and recorded.
WT-01
- git_agent_id: {{git-agent-id}}
- subtask_name: {{subtask-name}}
- runtime_agent_type: {{resolved-codex-runtime-agent-type}}
- codex_agent_id: pending-spawn
- codex_agent_name: pending-spawn
- ac_refs: AC-MODULE-01
- tdd_phase: tdd|red|green|verify|review
- branch: {{selected-git-skill-subagent-branch}}
- worktree_path: {{selected-git-skill-subagent-worktree-path}}
- base_ref: {{selected-git-skill-multi-agent-base-branch}}
- status: draft
Branch And Path Rules
- Git skill owns branch/worktree naming; Codex runtime id/name recorded after spawn, never used to derive git names.
- One worktree + one branch per delegated worker; workers write fragments to
reports/agents/; Main Agent updates canonical reports. - Selected-git-spec main/base branch is mandatory even when no worker is spawned.
- Each behavior-changing AC task row must have a dispatch decision before implementation.
- Solo fallback may have no worker row, but requires explicit approval or a recorded docs-only/trivial rationale.
- No worker edits main worktree.
Merge Strategy
- strategy: merge delegated worker branches into selected-git-spec main/base branch after evidence review.
- integration_owner: Main Agent
Conflict Rules
- conflict_owner: Main Agent
- worker_conflict_rule: stop and report; do not rewrite another worker's scope.
Cleanup Plan
- cleanup_after_merge: pending
- preserve_until_final_validation: true
Open Decisions
- None.
Worktree Report: {{FEATURE_NAME}}
Summary
Worktree/integration result, highest-risk delta, unresolved blockers (1-2 lines).
Evidence Rows
- AC-MODULE-01 | red: not applicable | green: not applicable | command_ref: none | browser: not applicable | status: pending.
Changed Paths
- None yet.
Commands
- @ref:spec/test-plan.md#command-index: pending.
Registry
- @ref:orchestration/worktree-plan.md#agent-worktrees: pending.
- Branch: @ref:orchestration/worktree-plan.md#multi-agent-base-branch: pending.
Blockers
- None.
Database Design Reference
Use when a feature touches persistence, migrations, data ownership, tenancy, permissions, reporting, search, audit, caching, or seed data.
Decision
spec/database-design.md must have status: waiting_human_decision | approved | not_applicable.
Implementation tasks cannot begin unless status is approved or not_applicable.
Design Checklist
- Affected bounded context/module
- Table names and ownership
- New/changed columns: type, nullability, default, semantics
- Keys (PK, UK, FK), check constraints, business constraints
- Indexes and expected query patterns
- Migration file naming and ordering
- Forward/backward compatibility
- Rollback strategy or irreversible warning
- Data backfill or seed strategy
- Transaction, locking, consistency
- Tenancy, authorization, data visibility
- Audit fields, soft delete, retention, privacy
- ORM/entity/repository changes
- Fixtures and test data strategy
- Migration validation commands
Compatibility Rule
Prefer expand-and-contract migrations: 1. Add compatible structures 2. Deploy code that reads/writes old and new shape 3. Backfill data 4. Enforce constraints after data is valid 5. Remove old structures after compatibility window closes
Destructive migrations require user confirmation.
Feature Decomposition Gate
Use after an approved brainstorming spec and approved PRD, and before selecting New Feature Delivery or Feature Evolution.
Purpose
eliteforge-brainstorming may approve a broad design that covers multiple business capabilities, admin surfaces, workflow stages, or existing workspaces. eliteforge-prd-generator may then turn that into a whole-product PRD with searchable feature, page, interaction, rule, exception, and acceptance headings. eliteforge-codex-superpower must not pour either upstream artifact into one docs/features/<feature-slug>/ workspace.
The lead agent must use the approved PRD as the primary product-layer input, then map it into bounded features that can each own one feature workspace, one feature model, and one AC inventory. Read only the matching PRD slices needed for the candidate feature.
Mandatory Explorer Support
The lead agent must not perform this analysis alone.
- Before or during brainstorming, dispatch at least one read-only
explorersubagent to scan repository structure, existing feature workspaces, likely dependency surfaces, and hidden constraints. - After brainstorming approval, dispatch at least one read-only
explorersubagent to prepare PRD inputs such as shared requirements, feature inventory, dependency relations, and delivery order. - After PRD approval, dispatch at least one read-only
explorersubagent to validate that product split against repository constraints and propose the bounded feature selection. - For broad specs, multi-domain designs, or multi-feature evolution, use 2 or more
explorersubagents with non-overlapping scopes such as business capability, shared platform capability, and impacted existing features. - If subagents are unavailable, record the blocker and stop before feature doc drafting or feature reopening.
Lead Agent Responsibilities
1. Read the approved brainstorming spec, approved PRD, and current repository context. 2. Search the PRD by module, feature, page, rule, exception, and acceptance headings; do not load the whole PRD when heading slices are enough. 3. Reuse the PRD's feature inventory, shared requirements, dependency map, and implementation order whenever they are already complete and current. 4. Add only repository-specific deltas needed for bounded feature selection, workspace reuse, and sequencing. 5. Split the design into bounded business capabilities, not technical layers. 6. Produce a compact decomposition summary before any feature doc drafting or reopening. 7. Select one target feature to enter the AC-only workflow unless the user explicitly approves batch orchestration.
Persist or restate four outputs. Reuse PRD content instead of duplicating it when possible:
1. Feature Inventory
For each candidate feature, record:
feature_slugfeature_namedelivery_type:new_featureorfeature_evolutiontarget_workspace: existingdocs/features/<feature-slug>/ornew workspacegoal: one-sentence business outcomein_scope: stable capability slice onlydeferred_or_out_of_scope: adjacent capability deferred to another featuresource_prd_headings: matching Feature/Page/Interaction/Rule/Exception/Acceptance headingsdependency_features: upstream/downstream feature dependenciesrecommended_order
2. Shared Requirement Inventory
Record reusable or common needs across features:
shared_requirementwhy_commonconsumer_featuresdelivery_decision: foundational feature now | keep in owning feature | defer extractionpriority_reason
True shared requirements that immediately block several features should be implemented first.
3. Dependency Map
Record at least:
feature -> depends_on -> feature- blocking shared requirements
- current repository constraints that force sequencing
- existing feature workspaces that must evolve before or alongside a new feature
If the PRD already includes these relations, append only repository-specific deltas instead of rewriting the whole graph.
4. Recommended Implementation Order
Produce a numbered sequence with rationale:
- foundational shared requirements first
- then blocking upstream features
- then dependent product features
- then follow-up admin/polish/reporting work
Split Rules
Split when the brainstorming spec combines any of these:
- distinct actor or permission surfaces such as admin, operator, end-user, or system automation
- distinct workflow stages such as ingestion, linking, publishing, notification, or audit
- distinct page/module surfaces that could ship or validate independently
- changes that belong to different existing feature workspaces
- one capability that can be implemented and accepted without waiting for the full rest of the design
Do not split by ceremony alone:
- not backend vs frontend vs database
- not one markdown file per subsystem
- not one worker role per feature by default
One feature workspace should represent one coherent acceptance surface. If the lead cannot explain the feature outcome in 1-2 sentences without listing several independent capabilities, the split is still too broad.
Shared Capability Rule
If a reusable capability appears during decomposition:
- keep it inside the owning feature when reuse is speculative or local
- record it as an open decision when extraction may be needed later
- create a standalone foundational feature only when it has its own acceptance surface or is an immediate dependency for multiple approved features
- prioritize the foundational feature before its dependents when the dependency is real and near-term
Execution Rule
- Do not create or reopen
docs/features/<feature-slug>/workspaces until the feature inventory, shared requirement inventory, dependency map, and implementation order exist. - Do not create one feature workspace that restates the whole brainstorming spec or whole-product PRD.
- Do not paste the whole PRD into
spec/feature-spec.md; carry compactsource_prd_headingsand rewrite only the selected feature model. - Do not treat backend/frontend/database layering as feature decomposition.
- If multiple bounded features remain and defaults are not already authorized, ask the user to confirm the split, shared-requirement decisions, and recommended order.
- After approval, enter
references/zero-one-delivery.mdorreferences/feature-evolution.mdfor one selected feature at a time.
Multi-Feature Evolution Rule
If one brainstorming spec affects several existing features:
- map one row per impacted feature workspace
- classify each row as
feature_evolutionornew_feature - reopen each feature from its own earliest affected gate
- do not mutate several feature workspaces under one blended gate flow without explicit user approval
Feature Evolution Workflow
Use when an existing docs/features/<feature-slug>/ workspace must change after drafting, task planning, active implementation, or delivery.
Brainstorming Gate
Before orientation or gate reopening, activate eliteforge-brainstorming for the current change request.
- Use it to confirm why, scope boundaries, alternatives, trade-offs, and design intent before mutating existing feature docs, tasks, or reports.
- Reuse an existing approved brainstorming spec only when it still matches the requested change and current repository state.
- Do not reopen gates, rewrite docs, or change task/worktree decisions until brainstorming and PRD are approved or the user explicitly approves reuse of existing approved upstream docs.
- Before or during brainstorming, dispatch at least one read-only
explorersubagent to scan impacted workspaces, sibling feature specs, shared capabilities, and likely hidden constraints.
PRD Gate
Activate eliteforge-prd-generator after the brainstorming gate passes.
- Confirm or update the searchable product definition for the current change, including background, goals, scope, feature requirements by module, page prototypes and interactions, business rules, exception/boundary cases, shared requirements, feature inventory, dependency map, recommended implementation order, and product-level acceptance.
- Keep the PRD different from brainstorming: PRD updates must add or change buildable facts under stable feature/page/rule/exception headings, not repeat option analysis.
- Reuse an existing approved PRD only when it still matches the requested change, the approved brainstorming output, and current repository reality.
- Do not reopen feature gates or rewrite downstream implementation artifacts during PRD drafting.
- After brainstorming approval, dispatch at least one read-only
explorersubagent to convert brainstorming plus repository findings into PRD inputs. - Enter Feature Impact Split only after the PRD is approved or the user explicitly approves reuse of an existing approved PRD.
Feature Impact Split
Before reopening any feature workspace, the lead agent must map the approved brainstorming spec plus approved PRD into bounded feature changes. Use references/feature-decomposition.md.
Dispatch at least one read-only explorer subagent for the decomposition pass. For broad or multi-domain changes, use 2 or more explorer subagents with non-overlapping scopes.
If the approved PRD already contains current feature inventory, shared requirement inventory, dependency map, and implementation order, reuse those sections and add only repository-specific deltas.
Search the PRD by module, feature, page, interaction, rule, exception, acceptance, shared-requirement, dependency, and order headings. Open only matching sections for each impacted feature; do not paste the whole PRD into any reopened feature workspace.
Produce a decomposition summary with one row per impacted feature:
feature_slugdelivery_type:feature_evolutionornew_featuretarget_workspace: existingdocs/features/<feature-slug>/ornew workspacegoalchange_surfacedependency_featuressource_prd_headingsrecommended_order
Also produce:
- shared requirement inventory
- dependency map across affected features
- recommended implementation order with rationale
Rules:
- Do not reopen several unrelated feature workspaces as one blended evolution flow.
- If the brainstorm output covers a new capability plus several impacted existing features, split that into separate feature rows.
- Prioritize true shared requirements before dependent feature updates.
- Pause new dispatch and merges until the affected feature inventory is confirmed.
- Resume
Feature Evolutionfor one selected feature at a time unless the user explicitly approves coordinated multi-feature execution.
Orientation And Freeze
Read orchestration/context-index.md first if present, then open only affected files, headings, and AC rows. Scan sibling feature-spec.md files for strong dependencies. If work is in progress, pause new dispatch and merges until impact analysis is confirmed. Preserve files and update in place.
Classify the change:
- requirement expansion, contraction, or clarification
- feature model or page/module surface change
- database or migration change
- API/DTO/permission/error contract change
- acceptance AC change
- proof row or command policy change
- task/worktree execution change
- documentation or evidence correction only
Record Evolution Entry
Before reopening any gate, create or append an entry in orchestration/feature-evolution-log.md:
change_id: sequential number (1, 2, 3...)trigger: user_request | spec_correction | requirement_change | bug_discovery | scope_adjustment | documentation_onlychange_type: from the classification abovegate_reopened: earliest affected New Feature Delivery phasediff_summary: before/after compact blockdownstream_impact: affected docs/tasks/reportsold_evidence_status: retained | superseded | replaced | not_applicableuser_approval: pending until confirmed
spec/feature-spec.md contains only the current feature model. Historical diffs belong in the evolution log.
Reopen Earliest Affected Gate
| change affects | reopen from | downstream to re-check |
|---|---|---|
| feature split, shared requirement inventory, dependency graph, implementation order | Phase 3 | selected feature, downstream spec phases |
| requirement, assumptions, scope, feature model, page/module surface | Phase 4A | database, API, acceptance, test plan, tasks |
| persistence, schema, migration, data governance | Phase 4B | API, acceptance, test plan, tasks |
| endpoint, DTO, auth, error, adapter boundary | Phase 4C | acceptance, test plan, tasks |
| AC wording, AC type, completion judgment | Phase 4D | test plan, tasks, reports |
| proof row, interaction proof, command alias, command cost | Phase 4E | tasks, reports |
| implementation sequencing or ownership only | Phase 6 | worktrees and reports |
Run matching --spec-stage validation after each reopened gate. Run --complete-spec whenever acceptance or test plan changes. If test-plan.md changes, reset Test Plan Decision to waiting_human_decision.
AC And Evidence Migration
If an AC's meaning changes, do not silently reuse old evidence. Either keep the AC id and mark old evidence superseded when wording remains semantically compatible, or create a new AC id and mark the older AC/evidence retained, superseded, replaced, not applicable, or skipped with approval.
Task and report rows are AC-keyed. If work has not started, update rows directly. If in progress or complete, add replacement rows and keep old evidence auditable.
If active worker branches/worktrees exist, decide per worker: continue, rebase, narrow scope, replace AC scope, or mark superseded with explicit user approval. Never silently drop unintegrated changes.
Resume Delivery
If documentation-only and behavior unchanged, record why implementation/test tasks are not applicable, refresh context-index.md, then run affected validation. Otherwise resume from the earliest affected phase: task breakdown, mandatory base branch provisioning, selective worker worktree provisioning, AC TDD slice execution, verify, or final review.
Final review for evolution compares changed AC rows, proof rows, command policy, task rows, worker evidence, reports, and residual risks.
Git Worktree And Multi-Agent Orchestration
Use this reference before dispatching subagents. Read the selected eliteforge-git-feature-oriented-* skill first for branch/worktree naming rules.
Select The Git Skill
Select exactly one git skill based on project declaration:
eliteforge-git-feature-oriented-spec(full spec)eliteforge-git-feature-oriented-lite-spec(lite spec)
If undeclared, stop and ask user. The selected skill provides: main agent branch/path, multi-agent base branch, subagent branch/path, agentId, subtask naming, merge direction, cleanup, commit, and MR/PR rules.
Do not use feature-skill-defined names such as eliteforge/integration/* or eliteforge/<unique-agent-id>/*.
Ownership
Main Agent: planning, gates, git-spec selection, worktree creation, dispatch, integration, conflict resolution, canonical reports, validation, user communication.
Each subagent: exactly one git worktree + one selected-git-spec branch. One branch = one worktree (no sharing).
Codex runtime identity is separate. After spawn_agent, update orchestration/worktree-plan.md with returned runtime id/name. Use runtime id for wait_agent/send_input; use selected-git-skill agentId/subtaskName for branches, paths, and report filenames.
Creation Commands
Scripts belong to the active skill directory. Set skill_dir to the absolute path of eliteforge-codex-superpower:
git switch -c "<selected-git-skill-multi-agent-base-branch>" # only when no base exists
"$skill_dir/scripts/create_agent_worktree.sh" "<selected-git-skill-subagent-branch>" "<selected-git-skill-subagent-worktree-path>" "<base-ref>"Dispatch Order
explore/support -> RED -> integrate RED into base -> GREEN -> integrate GREEN into base -> refactor/verify -> reviewDo not run dependent GREEN before RED is integrated or legacy-coverage exception is documented.
After RED integration, dispatch backend + frontend GREEN as one parallel batch when:
- Both depend on same integrated RED base
- Write scopes are disjoint
- Shared API/DTO contracts are stable in base
- Neither depends on pending database/schema or generated-client output
If a frontend row depends on generated client or backend depends on frontend-visible contract changes, integrate shared artifact first, then dispatch independent rows in parallel.
Dispatch Rules
- Give every subagent: absolute worktree path (from git skill), branch name, task IDs.
- Tell every subagent: Codex runtime name may differ from
agentId/subtaskName; do not edit main worktree; write only in assigned scope; write fragment toreports/agents/<task-id>-<agentId>-<subtaskName>.md. - Do not dispatch GREEN until RED tests are in base branch.
- Do not dispatch VERIFY until GREEN branches are integrated.
- Avoid parallel workers that may edit same files or tightly coupled modules.
Integration
Inspect worker branch before integration:
git -C <worker-worktree> status --short
git -C <worker-worktree> diff --stat <base-ref>...HEAD
git -C <worker-worktree> diff --name-only <base-ref>...HEADPreferred merge:
git switch "<selected-git-skill-multi-agent-base-branch>"
git merge --no-ff "<selected-git-skill-subagent-branch>"Patch apply (when merge unavailable):
git -C <worker-worktree> diff --binary <base-ref> > <patch-file>
git apply --index <patch-file>After integration, rerun the validation command from the task row or approved test scope.
Conflict Rules
- Main Agent resolves conflicts; workers do not resolve other workers' conflicts.
- If two workers changed the same file unexpectedly, stop parallel dispatch for that file family and record in
orchestration/integration-log.md. - Never discard worker changes without recording reason.
Cleanup
Optional until final acceptance:
git worktree remove <worktree-path> && git branch -d "<selected-git-skill-subagent-branch>"Do not delete branches/worktrees with unintegrated changes.
Subagent Orchestration Contract
Execution Modes
solo: explicit fallback only. Lead owns docs, coding, validation, and final evidence for a small AC slice after the selected-git-spec main/base branch exists.hybrid: default. Lead owns docs and integration; onetdd-slice-workerhandles behavior-changing AC slices unless the user approves solo or the slice is documentation-only/trivial.parallel: use multiple workers only after contracts are stable and write scopes are disjoint.
Default rule: selected-git-spec main/base branch is mandatory, then one bounded AC slice per worker. Split backend/frontend/database workers only when the same stable AC proof fans out into independent implementation scopes.
Dispatch Decision Tree
Record one orchestration/worktree-plan.md#Dispatch Decisions row for every behavior-changing task row (phase: tdd|red|green|refactor):
Behavior-changing AC task row → Is it docs-only/explore/support/review/verify-only?
├─ Yes → no implementation spawn
│
└─ No → Did user explicitly approve solo, or is the slice genuinely trivial?
├─ Yes → solo
│ Lead executes on selected-git-spec main/base branch and records rationale
│
└─ No → Are subagents or selected-git worktrees unavailable?
├─ Yes → blocked
│ Record blocker and stop for user decision
│
└─ No → Are contracts stable, write scopes isolated, and fan-out faster?
├─ Yes → parallel
│ Spawn backend/frontend/database workers only for real fan-out
│
└─ No → spawn
Default hybrid path: one bounded AC implementation worker
owns RED -> GREEN -> focused VERIFY -> evidenceGrouping rule: one spawned worker may own multiple adjacent AC rows only when they share the same write scope, command aliases, and interaction surface. Otherwise keep one bounded AC slice per worker.
Coding Agent Selection
Default: worker agent type. Environment variables are Codex runtime spawn_agent.agent_type values, not workflow role names:
ELITEFORGE_SKILL_SUPERPOWER_CODING_AGENT: overrides coding subagents such as backend/frontend/database workers.ELITEFORGE_SKILL_SUPERPOWER_TDD_AGENT: overrides specifically for bounded AC TDD slice workers.ELITEFORGE_SKILL_SUPERPOWER_REVIEWER_AGENT: overrides verify and review workers.
Resolution: 1. TDD slice workers: ELITEFORGE_SKILL_SUPERPOWER_TDD_AGENT -> worker 2. Backend/frontend/database: ELITEFORGE_SKILL_SUPERPOWER_CODING_AGENT -> worker 3. Verify/review: ELITEFORGE_SKILL_SUPERPOWER_REVIEWER_AGENT -> worker 4. Explorer: always explorer
If a configured environment variable is set to an unavailable runtime type, stop before spawning and record the invalid value. Fall back to worker only when no configured variable in the resolution chain is set.
Spawn call contract: before every non-explorer spawn, record runtime_agent_type in orchestration/worktree-plan.md, then pass that exact value as spawn_agent.agent_type. When a resolved value is coder-spark, spawn with agent_type: "coder-spark". Do not omit agent_type, do not rely on default spawn behavior, and do not use fork_context: true with explicit agent_type. Workflow roles such as tdd-slice-worker, backend-worker, frontend-worker, database-worker, and review-worker are prompt/report/worktree roles only; never pass them as spawn_agent.agent_type.
Main Agent
Owns: user communication, AC-only docs, execution-mode choice, git-spec resolution, mandatory base branch setup, selective worker worktree setup, dispatch, integration, final validation, completion claims.
The lead agent is a synthesizer, not the only reader. Use read-only explorer subagents to reduce context pressure before behavior-changing drafting starts.
- During brainstorming preparation, dispatch at least one
explorerto scan repository structure, sibling feature workspaces, shared capabilities, and hidden constraints. - After brainstorming approval, dispatch at least one
explorerto prepare PRD inputs such as background, goals, scope, module-level features, page interactions, business rules, exception boundaries, shared requirements, feature inventory, dependency map, and recommended implementation order. - After PRD approval, dispatch at least one
explorerto validate the PRD against repository constraints, search relevant PRD headings, propose the bounded feature selection, and confirm dependency/order deltas. - For broad specs or multi-feature impact, use 2 or more
explorersubagents with disjoint scopes. - If mandatory explorer support is unavailable, mark the workflow blocked before PRD drafting, feature doc drafting, or feature reopening.
- Generate or refresh
orchestration/context-index.mdbefore dispatch/review - Read context index first, then only relevant AC rows and command aliases
- Create or verify the selected-git-spec main/base branch before behavior-changing execution
- Fill
Dispatch Decisionsbefore implementation; do not silently omit spawn for behavior-changing AC rows - Dispatch one
tdd-slice-workerfor behavior-changing AC slices by default when tool support is available - Use solo only with explicit user approval or a recorded docs-only/trivial rationale
- Create one worktree + one branch per delegated worker from the selected git skill
- Record each delegated worker
runtime_agent_typebefore spawn - After spawn, update
orchestration/worktree-plan.mdwith runtime id and name - Do not use
fork_context: truewhen passing an explicitagent_type; provide a focused prompt with only required context - Avoid parallel workers editing the same files or tightly coupled modules
- Preserve fresh VERIFY evidence during review
Worker Prompt Template
All delegated workers receive this identity header:
You are a <role> worker for feature <feature-slug>.
Worktree: <path> (resolved from selected git skill).
Branch: <branch>.
Agent id: <agentId>. Subtask name: <subtaskName> (from selected git skill).
Codex runtime name may differ from Agent id. Use Agent id and Subtask name for branches, paths, report filenames, and integration records.Worker prompts must include:
- assigned AC rows from
spec/acceptance.md - matching proof rows and
CMD-*aliases fromspec/test-plan.md - write scope and command expectations
- report row requirements
- TDD rules from
references/tdd-workflow.md
Do not paste whole spec/, tasks/, or reports/.
External Browser Skill Use
When a browser/testing skill such as webapp-testing is used, treat it as a Playwright capability provider only. Do not edit the external skill and do not inherit any legacy EliteForge document contract from it.
Browser worker prompts must explicitly override the output contract:
- Use only assigned
AC-*rows as traceability IDs. - Use only
CMD-*aliases fromspec/test-plan.md. - Write evidence into
reports/*.mdrows keyed byAC-*. - Include clicks, inputs, network assertions, visible feedback, and artifacts.
- Do not create
test-scope.md,TDD-*,TASK-*,INT-*,EV-*, orBVE-*outputs.
Command Cost Rules
fast: unit, type, lint, focused parser. VERIFY and REVIEW may run.medium: build, integration, local service, database, container-light. VERIFY runs; REVIEW reruns only when evidence is missing or affected files changed.slow/browser: Playwright, Cypress, browser runtime, full E2E, container-heavy. VERIFY runs at most once per relevant change. REVIEW reuses evidence unless missing, failed, stale, or user requests fresh run.
Browser evidence must include clicks, inputs, network assertions, visible feedback, and artifacts. smoke passed is not evidence.
Running, Stalled, Or Empty Workers
Do not estimate stall from wall-clock alone. While running -> in_progress.
Observable stall conditions:
- Platform reports failed, cancelled, disconnected, or timed out
- Completes with no changed files, no report fragment, and no RED/GREEN/VERIFY evidence
- User asks to stop or replace
- Explicit recorded timebox reached and user approved interruption
After empty completion or approved interruption -> re-dispatch a narrower prompt once. If still no evidence -> mark blocked, record in reports/worktree-plan/integration-log, do not integrate the empty branch, and do not dispatch dependent rows.
Review
Review after reports exist. Cover database alignment, AC proof rows, command policy, worktree integration, changed paths, browser evidence detail, evidence freshness, and final status recommendation.
Review must not rerun slow/browser commands when passing VERIFY evidence is still fresh. Rerun only when:
- no passing VERIFY evidence exists
- prior evidence failed or was marked stale
- files changed in the covered route/page/component/auth flow/API fixture/browser setup
- user requested fresh run
When skipping rerun, record the reused evidence link and freshness rationale in the review report.
TDD Workflow Reference
Core Rule
For every automated acceptance criterion: RED (proof exists and fails for expected missing behavior) -> GREEN (smallest production change makes the same proof pass) -> VERIFY (integration/browser/regression evidence remains passing).
Manual-only criteria are allowed only when automation is impractical and approved in spec/test-plan.md.
AC Proof Granularity
AC-* is the only global business traceability ID. TDD cases are local proof rows under an AC, not global IDs.
Split proof rows when:
- one RED failure would prove multiple independent missing behaviors
- happy, error, boundary, permission, or integration branches are mixed
- browser interaction and API/database proof need separate commands
- GREEN would require unrelated write scopes
Use multiple proof rows for one AC when needed; keep each row compact with case, layer, red, green, optional interaction, and command_ref.
Layer Defaults
- domain rules: unit tests
- validation: unit + API/controller tests when externally visible
- persistence: integration tests with project-standard DB setup
- migrations: migration/schema validation or repository tests
- permissions: service/API tests for allowed + denied
- API contract: controller/API tests + DTO serialization checks
- frontend: component/page tests
- full journey: browser/runtime test when cross-layer behavior matters
Browser/runtime commands are expensive. Record as cost: slow/browser in test-plan.md#command-index. Run once per relevant change; REVIEW inspects evidence instead of rerunning.
RED Evidence Quality
Valid only when:
- command executes the proof row's test or browser proof
- failure is expected missing behavior, not syntax/dependency/environment noise
- maps to an AC id and command alias
- is recorded before dependent production implementation
GREEN Evidence Quality
Valid only when:
- same proof that produced RED now passes
- production changes stay inside assigned write scope
- tests are not weakened
- changed paths, branch/worktree or lead-local note, command alias, and AC id are recorded
Browser Evidence Quality
Browser evidence is valid only when the report row records:
- clicks
- inputs
- network assertions
- visible feedback
- artifacts such as trace, screenshot, or report path
smoke passed is never sufficient.
Database TDD Notes
Test before implementation when practical:
- migration/schema assertion fails before table/column/index exists
- repository/integration test fails before persistence behavior exists
- compatibility test fails before old/new behavior is supported
- fixture/seed-data test when deterministic data is required
If migration cannot be tested directly, record tooling output, schema inspection, or dry-run evidence.
0-1 Feature Delivery Workflow
Use this reference for new features with no existing docs/features/<feature-slug>/ workspace.
Phase 1: Brainstorming Gate
Activate eliteforge-brainstorming before any feature doc drafting.
- Use it to converge why, scope boundaries, alternatives, trade-offs, and design decisions before writing
spec/*.md. - Reuse an existing approved brainstorming spec only when it still matches the current request and repository state.
- Do not create tasks, worktree plans, or reports during brainstorming.
- Before or during brainstorming, dispatch at least one read-only
explorersubagent to scan repository structure, sibling feature workspaces, likely shared capabilities, and hidden constraints. - Enter Phase 2 only after brainstorming is approved or the user explicitly approves reuse of an existing approved brainstorming spec.
Phase 2: PRD Gate
Activate eliteforge-prd-generator after the brainstorming gate passes.
- Turn the approved brainstorming output into a searchable PRD with background, goals, scope, feature requirements by module, page prototypes and interactions, business rules, exception/boundary cases, shared requirements, feature inventory, dependency map, recommended implementation order, and product-level acceptance.
- Keep the PRD different from brainstorming: do not repeat alternative analysis except where it changes product scope; write buildable facts under stable headings such as
Feature:,Page:,Interaction:,Rule:,Exception:, andAcceptance:. - Reuse an existing approved PRD only when it still matches the current request, the approved brainstorming output, and repository reality.
- Do not create feature docs, tasks, worktree plans, or reports during PRD drafting.
- After brainstorming approval, dispatch at least one read-only
explorersubagent to convert brainstorming plus repository findings into PRD inputs. - For broad or multi-domain designs, use 2 or more
explorersubagents with non-overlapping scopes such as user workflow, shared platform capability, and existing workspace impact. - Enter Phase 3 only after the PRD is approved or the user explicitly approves reuse of an existing approved PRD.
Phase 3: Feature Decomposition And Selection
Before creating docs/features/<feature-slug>/, the lead agent must read the approved brainstorming spec and approved PRD, then select one bounded feature. Use references/feature-decomposition.md.
Dispatch at least one read-only explorer subagent for the selection pass. For broad or multi-domain designs, use 2 or more explorer subagents with non-overlapping scopes.
Search the PRD by heading keywords before reading details. Open only matching module, feature, page, interaction, rule, exception, acceptance, shared-requirement, dependency, and order sections for the selected feature. Do not load or paste the whole PRD into the feature workspace.
If the approved PRD already contains complete feature inventory, shared requirement inventory, dependency map, and implementation order, reuse those sections as the primary product input and add only repository-specific deltas needed for workspace selection.
Produce a compact decomposition summary that includes:
Feature Inventory
feature_slugfeature_namedelivery_type:new_featureorfeature_evolutiongoalin_scopedeferred_or_out_of_scopedependency_featuressource_prd_headingsrecommended_order
Shared Requirement Inventory
shared_requirementwhy_commonconsumer_featuresdelivery_decisionpriority_reason
Dependency Map
feature -> depends_on -> feature- blocking shared requirements
- existing feature workspace dependencies
Recommended Implementation Order
- numbered implementation sequence
- rationale for why each item comes before the next
Rules:
- Do not create one feature workspace that restates the whole brainstorming spec.
- Do not create one feature workspace that restates the whole product PRD.
- Do not split only by backend/frontend/database implementation layers.
- Prioritize true shared requirements before dependent features.
- Create or reopen docs for one selected feature at a time unless the user explicitly approves batch orchestration.
- If multiple features remain and defaults are not already authorized, stop for user confirmation before entering Phase 4.
Phase 4: Repository Discovery And Feature Model
Draft from user requirement, repository findings, sibling feature specs, and best practices. Each gate creates or updates only its listed file(s), runs validation, and stops for user confirmation unless defaults are authorized.
Draft progressively:
1. spec/feature-spec.md 2. spec/database-design.md 3. spec/api-contract.md 4. spec/acceptance.md 5. spec/test-plan.md
Do not create tasks, worktree plans, or reports before the complete spec gate passes. orchestration/context-index.md is allowed as a generated navigation artifact after the selected feature workspace exists.
After each gated draft, ask at most 3-5 focused questions:
I drafted <current-file> with these assumptions:
1. ...
Confirm or correct these blocking decisions:
1. ...| gate | create/update | draft focus | validation |
|---|---|---|---|
| 4A Feature Spec | spec/feature-spec.md | feature identity/context, requirement, assumptions-first scope, feature model, page/module surface, repository findings, open decisions; no AC-* | "$skill_dir/scripts/validate_feature_docs.py" <feature-dir> --phase spec --spec-stage feature |
| 4B Database Design | spec/database-design.md | impact decision, schema, migration, query/performance, governance, fixtures, validation commands | "$skill_dir/scripts/validate_feature_docs.py" <feature-dir> --phase spec --spec-stage database |
| 4C API Contract | spec/api-contract.md | endpoints, request/response DTOs, auth, errors, frontend/backend boundary, database/transaction boundary, mocks/adapters | "$skill_dir/scripts/validate_feature_docs.py" <feature-dir> --phase spec --spec-stage api |
| 4D Acceptance Index | spec/acceptance.md | one-line AC-* completion judgments; interaction ACs included as AC rows; no commands or test steps | "$skill_dir/scripts/validate_feature_docs.py" <feature-dir> --phase spec --spec-stage acceptance |
| 4E Proof Plan | spec/test-plan.md | proof rows by AC-*, local CMD-* command aliases, command cost/rerun policy, evidence rules | "$skill_dir/scripts/validate_feature_docs.py" <feature-dir> --phase spec --spec-stage test-plan |
Phase 5: Complete Spec Gate
spec/test-plan.md must have Test Plan Decision set to waiting_human_decision, approved, or approved_with_defaults. Task execution cannot begin until it is approved or approved_with_defaults.
Run:
"$skill_dir/scripts/validate_feature_docs.py" <feature-dir> --phase spec --complete-spec --no-downstream-artifactsStop for confirmation. Only begin task breakdown when the complete spec gate passes.
Phase 6: AC Task Breakdown
Create task files in tasks/. Each row is keyed by AC-*:
AC-<MODULE>-NN | phase: <tdd|docs|explore|support|verify|review|integrate|red|green|refactor> | owner: <role> | scope: <paths/modules> | command_ref: CMD-<ALIAS>|none | status: <status>.Default planning:
- one bounded
tddrow per AC slice - split into backend/frontend/database workers only when contracts are stable, write scopes are disjoint, and splitting reduces wall-clock time
- verify/review rows after integration evidence exists
- task files do not define
TASK-*ids and do not restate proof rows
Run:
"$skill_dir/scripts/validate_feature_docs.py" <feature-dir> --phase tasksSet task status to waiting_human_decision and stop before implementation unless execution is approved.
Phase 7: Context Index And Mandatory Base Branch
Generate or refresh:
"$skill_dir/scripts/render_context_index.py" <feature-dir>Read orchestration/context-index.md before dispatch. Open only relevant files, headings, and AC rows.
Read the selected eliteforge-git-feature-oriented-* skill, then references/git-worktree-orchestration.md. Create or verify the selected-git-spec main/base branch before any behavior-changing execution. This is mandatory for solo, hybrid, and parallel.
Worker worktrees are selective. If explicit solo fallback is approved, skip only the worker worktree rows, not the selected-git-spec main/base branch. Record the solo rationale in task rows, worktree-plan.md, integration log, and reports.
Agent selection:
- TDD slice workers:
ELITEFORGE_SKILL_SUPERPOWER_TDD_AGENTif set, elseworker - Backend, frontend, database:
ELITEFORGE_SKILL_SUPERPOWER_CODING_AGENTif set, elseworker - Verify, review:
ELITEFORGE_SKILL_SUPERPOWER_REVIEWER_AGENTif set, elseworker - If a configured value is not a valid Codex runtime type such as
worker,coder,coder-spark, orexplorer, stop before spawning and record the invalid value; fallback toworkerapplies only when no configured value is set
Run after base branch/worktree-plan provisioning:
"$skill_dir/scripts/validate_feature_docs.py" <feature-dir> --phase worktreesPhase 8: AC TDD Slice Execution
Default execution unit: one bounded AC slice.
Before implementation, fill orchestration/worktree-plan.md#Dispatch Decisions for every behavior-changing task row (phase: tdd|red|green|refactor):
Spawn call contract: before every non-explorer worker spawn, record runtime_agent_type in the worker block, then call spawn_agent with that exact value as agent_type. Do not omit agent_type, do not rely on default spawn behavior, and do not use fork_context: true with explicit agent_type.
AC task row → Is it docs-only/explore/support/review/verify-only?
├─ Yes → no implementation spawn
│
└─ No → Is solo explicitly approved or genuinely trivial?
├─ Yes → solo with recorded rationale
│
└─ No → Are subagents/worktrees unavailable?
├─ Yes → blocked and stop for user decision
│
└─ No → Can stable contracts + isolated write scopes reduce wall-clock?
├─ Yes → parallel fan-out
└─ No → spawn one bounded AC implementation workerDo not silently run behavior-changing hybrid work without a spawn decision row. One spawned worker may cover multiple adjacent AC rows only when they share the same write scope, command aliases, and interaction surface.
Valid AC slice evidence:
- AC id
- changed paths
- command alias and actual command output
- RED failure summary and why it proves missing behavior
- GREEN pass summary with the same proof
- focused VERIFY evidence when relevant
- browser evidence fields when browser proof is claimed
- worktree path or explicit lead-local execution note
If a browser/testing skill such as webapp-testing is used, use it only for Playwright technique, server lifecycle, selector discovery, screenshots, traces, and logs. The lead must still provide the AC-only prompt contract and require reports/*.md rows keyed by AC-*; do not follow any external legacy contract that asks for test-scope.md, global TDD-*, TASK-*, INT-*, EV-*, or BVE-* IDs.
Default hybrid behavior is to dispatch one tdd-slice-worker for behavior-changing AC slices when tool support is available. Lead may execute a bounded slice locally only when the user approves solo fallback or the task is documentation-only/trivial, and only from the selected-git-spec main/base branch.
Phase 9: Optional Parallel Fan-Out
Split into backend/frontend/database workers only when all are true:
- the AC and proof row are stable
- API/database contracts are stable enough to avoid repeated rework
- write scopes are disjoint
- splitting materially reduces wall-clock time
Lead integrates branches, resolves conflicts inside lead-owned scope, reruns affected validation, and updates reports by AC.
Phase 10: Verify, Review, And Final Evidence
Lead creates or updates reports:
"$skill_dir/scripts/render_reports.py" <feature-dir>
"$skill_dir/scripts/render_context_index.py" <feature-dir>Reports keep only AC-keyed evidence rows, changed paths, commands, blockers, and conclusions. Final review compares implementation evidence against acceptance, proof rows, command policy, database design, and worktree evidence.
Final review is evidence review, not full regression. Spot checks with fast commands are fine. Do not rerun slow/browser commands when fresh VERIFY evidence already exists; reuse it unless stale, missing, failed, or explicitly requested.
Browser evidence must list actual clicks, inputs, network assertions, visible feedback, and artifacts. smoke passed is not acceptable.
Run:
"$skill_dir/scripts/validate_feature_docs.py" <feature-dir> --phase finalNo completion while final validation fails, unresolved blockers remain, an AC lacks proof/task/evidence rows, or claimed UI behavior lacks concrete browser evidence.
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 3 || $# -gt 3 ]]; then
echo "Usage: $0 <worktree-path> <base-ref> <output-patch>" >&2
exit 2
fi
worktree_path="$1"
base_ref="$2"
output_patch="$3"
if [[ ! -d "$worktree_path/.git" && ! -f "$worktree_path/.git" ]]; then
echo "ERROR: not a git worktree: $worktree_path" >&2
exit 2
fi
mkdir -p "$(dirname "$output_patch")"
git -C "$worktree_path" diff --binary "$base_ref" > "$output_patch"
echo "patch=$output_patch"
echo "changed_paths:"
git -C "$worktree_path" diff --name-only "$base_ref" | sed 's/^/- /'
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 2 || $# -gt 3 ]]; then
echo "Usage: $0 <git-skill-subagent-branch> <git-skill-worktree-path> [base-ref]" >&2
exit 2
fi
branch="$1"
worktree_path="$2"
base_ref="${3:-HEAD}"
if [[ ! "$branch" =~ ^[A-Za-z0-9._/-]+$ ]]; then
echo "ERROR: branch must be a git-skill-compliant branch containing only letters, numbers, dots, underscores, hyphens, or slashes: $branch" >&2
exit 2
fi
if [[ "$branch" == /* || "$branch" == -* || "$branch" == */ || "$branch" == */..* || "$branch" == *"//"* ]]; then
echo "ERROR: branch must be a normalized relative branch path: $branch" >&2
exit 2
fi
if [[ "$branch" == eliteforge/integration/* || "$branch" == eliteforge/* ]]; then
echo "ERROR: branch uses the obsolete feature-skill naming scheme. Resolve the branch from eliteforge-git-feature-oriented-spec or lite-spec." >&2
exit 2
fi
if [[ "$branch" != */*/* ]]; then
echo "ERROR: branch is too short to be a selected-git-skill subagent branch: $branch" >&2
exit 2
fi
if [[ "$worktree_path" != /* ]]; then
echo "ERROR: worktree path must be absolute and resolved from the selected git skill: $worktree_path" >&2
exit 2
fi
IFS='/' read -r -a branch_parts <<< "$branch"
IFS='/' read -r -a path_parts <<< "${worktree_path#/}"
if (( ${#path_parts[@]} < ${#branch_parts[@]} )); then
echo "ERROR: worktree path must mirror the selected-git-skill branch tree: $worktree_path" >&2
exit 2
fi
for ((i = 1; i <= ${#branch_parts[@]}; i++)); do
branch_index=$((${#branch_parts[@]} - i))
path_index=$((${#path_parts[@]} - i))
if [[ "${path_parts[$path_index]}" != "${branch_parts[$branch_index]}" ]]; then
echo "ERROR: worktree path must mirror the selected-git-skill branch tree: $worktree_path" >&2
exit 2
fi
done
if ! git rev-parse --show-toplevel >/dev/null 2>&1; then
echo "ERROR: not inside a git repository" >&2
exit 2
fi
repo_root="$(git rev-parse --show-toplevel)"
case "$worktree_path" in
"$repo_root"|"$repo_root"/*)
echo "ERROR: worktree path must not be inside the main repository: $worktree_path" >&2
exit 2
;;
esac
mkdir -p "$(dirname "$worktree_path")"
if git show-ref --verify --quiet "refs/heads/$branch"; then
echo "ERROR: branch already exists: $branch" >&2
echo "Use a different selected-git-skill subagent branch or remove the old branch after confirming it has no unmerged changes." >&2
exit 1
fi
if [[ -e "$worktree_path" ]]; then
echo "ERROR: worktree path already exists: $worktree_path" >&2
exit 1
fi
git worktree add -b "$branch" "$worktree_path" "$base_ref"
cat <<OUT
branch=$branch
worktree_path=$worktree_path
base_ref=$base_ref
OUT
#!/usr/bin/env python3
"""Render a compact navigation index for an EliteForge feature workspace."""
from __future__ import annotations
import argparse
import re
import sys
from pathlib import Path
AC_ID_RE = re.compile(r"\bAC-[A-Z0-9]+-\d{2,}\b")
CMD_ID_RE = re.compile(r"\bCMD-[A-Z0-9]+(?:-[A-Z0-9]+)*\b")
HEADING_RE = re.compile(r"^#{2,3}\s+(.+?)\s*$", re.MULTILINE)
SOURCE_HEADING_RE = re.compile(
r"^\s*-\s*(?:brainstorming_source|prd_[a-z_]+_headings)\s*:\s*(.+?)\s*$",
re.IGNORECASE | re.MULTILINE,
)
def read_text(path: Path) -> str:
try:
return path.read_text(encoding="utf-8")
except UnicodeDecodeError:
return path.read_text()
def markdown_files(feature_dir: Path) -> list[Path]:
files: list[Path] = []
for rel_dir in ("spec", "tasks", "orchestration", "reports"):
root = feature_dir / rel_dir
if not root.is_dir():
continue
for path in sorted(root.rglob("*.md")):
if path.relative_to(feature_dir).as_posix() == "orchestration/context-index.md":
continue
files.append(path)
return files
def kind_for(rel: str) -> str:
if rel == "spec/feature-spec.md":
return "feature-model"
if rel == "spec/acceptance.md":
return "acceptance-index"
if rel == "spec/test-plan.md":
return "proof-plan"
if rel.startswith("spec/"):
return "spec-support"
if rel.startswith("tasks/"):
return "task-plan"
if rel.startswith("reports/"):
return "evidence-ledger"
if rel.startswith("orchestration/"):
return "orchestration"
return "markdown"
def owns_for(rel: str, text: str) -> str:
if rel == "spec/feature-spec.md":
return "feature context"
if rel == "spec/acceptance.md":
return "AC-*"
if rel == "spec/test-plan.md":
cmds = sorted(set(CMD_ID_RE.findall(text)))
return ", ".join(cmds) if cmds else "command aliases"
if rel.startswith("tasks/"):
return "execution rows"
if rel.startswith("reports/"):
return "evidence rows"
return "supporting facts"
def compact(values: list[str], empty: str = "none", limit: int = 8) -> str:
if not values:
return empty
unique = list(dict.fromkeys(values))
shown = unique[:limit]
suffix = f", +{len(unique) - limit} more" if len(unique) > limit else ""
return ", ".join(shown) + suffix
def source_refs(text: str) -> list[str]:
refs: list[str] = []
for match in SOURCE_HEADING_RE.finditer(text):
value = match.group(1).strip()
if value and value.lower() not in {"none", "not applicable", "n/a", "pending"}:
refs.append(value)
return refs
def render(feature_dir: Path) -> Path:
rows = [
"| file | kind | owns | headings | source_refs | ac_refs |",
"| --- | --- | --- | --- | --- | --- |",
]
for path in markdown_files(feature_dir):
rel = path.relative_to(feature_dir).as_posix()
text = read_text(path)
headings = [match.group(1).strip() for match in HEADING_RE.finditer(text)]
ac_refs = sorted(set(AC_ID_RE.findall(text)))
rows.append(
f"| {rel} | {kind_for(rel)} | {owns_for(rel, text)} | "
f"{compact(headings)} | {compact(source_refs(text), limit=6)} | {compact(ac_refs)} |"
)
output = feature_dir / "orchestration" / "context-index.md"
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(
"# Context Index\n\n"
"Generated navigation index. Agents read this first, then open only the needed file, heading, and AC rows.\n\n"
+ "\n".join(rows)
+ "\n",
encoding="utf-8",
)
return output
def main() -> int:
parser = argparse.ArgumentParser(description="Render docs/features/<feature-slug>/orchestration/context-index.md.")
parser.add_argument("feature_dir", type=Path, help="Path to docs/features/<feature-slug>")
args = parser.parse_args()
if not args.feature_dir.is_dir():
print(f"ERROR: feature directory not found: {args.feature_dir}", file=sys.stderr)
return 2
print(render(args.feature_dir))
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Render compact AC-keyed report skeletons from EliteForge workspace docs."""
from __future__ import annotations
import argparse
import re
import sys
from pathlib import Path
REPORTS = {
"database-result.md": "Database",
"backend-result.md": "Backend",
"frontend-result.md": "Frontend",
"test-report.md": "Test",
"review-report.md": "Review",
"worktree-report.md": "Worktree",
}
AC_ID_RE = re.compile(r"\bAC-[A-Z0-9]+-\d{2,}\b")
HEADING_ALIASES = {
"Acceptance Index": ("Acceptance Index", "Acceptance Criteria", "Acceptance Criteria Matrix"),
"Proof Rows": ("Proof Rows",),
"Task Rows": ("Task Rows", "Task Index", "Task Table"),
}
def read_text(path: Path) -> str:
if not path.exists():
return ""
try:
return path.read_text(encoding="utf-8")
except UnicodeDecodeError:
return path.read_text()
def section(text: str, title: str) -> str:
lines = text.splitlines()
capture = False
body: list[str] = []
current_level = None
for line in lines:
match = re.match(r"^(#{1,6})\s+(.+?)\s*$", line)
if match and match.group(2).strip() == title:
capture = True
current_level = len(match.group(1))
continue
if capture and match and len(match.group(1)) <= (current_level or 6):
break
if capture:
body.append(line)
return "\n".join(body).strip()
def section_any(text: str, title: str) -> str:
for option in HEADING_ALIASES.get(title, (title,)):
body = section(text, option)
if body:
return body
return ""
def bullet_lines(section_text: str) -> list[str]:
return [line.strip() for line in section_text.splitlines() if line.strip().startswith(("-", "*"))]
def command_ref_from_line(line: str) -> str:
match = re.search(r"\bcommand_ref\s*:\s*(CMD-[A-Z0-9]+(?:-[A-Z0-9]+)*|none)\b", line, re.IGNORECASE)
return match.group(1) if match else "none"
def parse_acceptance_ids(feature_dir: Path) -> list[str]:
text = read_text(feature_dir / "spec" / "acceptance.md")
return sorted(set(AC_ID_RE.findall(section_any(text, "Acceptance Index") or text)))
def proof_rows(feature_dir: Path) -> dict[str, list[str]]:
text = read_text(feature_dir / "spec" / "test-plan.md")
rows: dict[str, list[str]] = {}
for line in bullet_lines(section_any(text, "Proof Rows")):
ids = AC_ID_RE.findall(line)
for ac_id in ids:
rows.setdefault(ac_id, []).append(line)
return rows
def task_rows(feature_dir: Path) -> dict[str, list[str]]:
rows: dict[str, list[str]] = {}
tasks_dir = feature_dir / "tasks"
if not tasks_dir.is_dir():
return rows
for path in sorted(tasks_dir.glob("*.md")):
text = read_text(path)
for line in bullet_lines(section_any(text, "Task Rows")):
ids = AC_ID_RE.findall(line)
for ac_id in ids:
rows.setdefault(ac_id, []).append(line)
return rows
def command_for_ac(ac_id: str, proofs: dict[str, list[str]], tasks: dict[str, list[str]]) -> str:
for source in (tasks, proofs):
for line in source.get(ac_id, []):
command_ref = command_ref_from_line(line)
if command_ref != "none":
return command_ref
return "none"
def browser_for_ac(ac_id: str, proofs: dict[str, list[str]]) -> str:
joined = " ".join(proofs.get(ac_id, [])).lower()
if "browser" in joined or re.search(r"\binteraction:\s*(?!not applicable)", joined):
return "clicks=pending; inputs=pending; network=pending; feedback=pending; artifacts=pending"
return "not applicable"
def evidence_rows(feature_dir: Path) -> list[str]:
ac_ids = parse_acceptance_ids(feature_dir)
proofs = proof_rows(feature_dir)
tasks = task_rows(feature_dir)
rows: list[str] = []
for ac_id in ac_ids:
rows.append(
f"- {ac_id} | red: pending | green: pending | command_ref: {command_for_ac(ac_id, proofs, tasks)} | "
f"browser: {browser_for_ac(ac_id, proofs)} | status: pending."
)
return rows or ["- None yet."]
def report_content(feature_dir: Path, title: str) -> str:
rows = "\n".join(evidence_rows(feature_dir))
return f"""# {title} Report: {feature_dir.name}
## Summary
Generated framework. Fill only AC-keyed evidence deltas, blockers, and conclusion.
## Evidence Rows
{rows}
## Changed Paths
- None yet.
## Commands
- @ref:spec/test-plan.md#command-index: pending evidence.
## Blockers
- None.
"""
def write_report(path: Path, content: str, force: bool) -> bool:
if path.exists() and not force:
return False
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content, encoding="utf-8")
return True
def render(feature_dir: Path, force: bool) -> list[Path]:
reports_dir = feature_dir / "reports"
(reports_dir / "agents").mkdir(parents=True, exist_ok=True)
written: list[Path] = []
for filename, title in REPORTS.items():
path = reports_dir / filename
if write_report(path, report_content(feature_dir, title), force):
written.append(path)
return written
def main() -> int:
parser = argparse.ArgumentParser(description="Render compact AC-keyed EliteForge report skeletons.")
parser.add_argument("feature_dir", type=Path, help="Path to docs/features/<feature-slug>")
parser.add_argument("--force", action="store_true", help="Overwrite existing reports.")
args = parser.parse_args()
if not args.feature_dir.is_dir():
print(f"ERROR: feature directory not found: {args.feature_dir}", file=sys.stderr)
return 2
written = render(args.feature_dir, args.force)
for path in written:
print(path)
if not written:
print("No reports written; existing files were preserved. Use --force to overwrite.")
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""CLI entry point for EliteForge multi-agent feature-oriented TDD workspace validation."""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from validators.specs import check_specs, SPEC_STAGE_FILES # noqa: E402
from validators.tasks import check_tasks # noqa: E402
from validators.reports import check_reports # noqa: E402
from validators.context import check_context_index # noqa: E402
from validators.worktrees import check_orchestration # noqa: E402
from validators.common import DOWNSTREAM_ARTIFACT_DIRS # noqa: E402
def check_no_downstream_artifacts(root: Path, errors: list[str]) -> None:
for rel in DOWNSTREAM_ARTIFACT_DIRS:
path = root / rel
if not path.exists():
continue
children = [child for child in path.iterdir() if child.name != "context-index.md"]
if children:
errors.append(f"{rel}: downstream artifacts must not be created during the spec/test-plan gate")
def validate(root: Path, phase: str, complete_spec: bool, spec_stage: str, no_downstream_artifacts: bool) -> list[str]:
errors: list[str] = []
require_complete = complete_spec or phase in {"tasks", "worktrees", "reports", "final"}
require_approved = phase in {"tasks", "worktrees", "reports", "final"}
ac_ids, tdd_ids = check_specs(
root, errors, complete=require_complete, require_approved=require_approved, spec_stage=spec_stage,
)
if no_downstream_artifacts:
if phase != "spec":
errors.append("--no-downstream-artifacts is only valid with --phase spec")
check_no_downstream_artifacts(root, errors)
task_rows = {}
if phase in {"tasks", "worktrees", "reports", "final"}:
task_rows = check_tasks(root, errors, ac_ids=ac_ids, final=phase == "final")
if phase in {"worktrees", "reports", "final"}:
check_context_index(root, errors, required=phase in {"reports", "final"})
check_orchestration(root, errors, final=phase == "final", task_rows=task_rows)
if phase in {"reports", "final"}:
check_reports(root, task_rows, errors, ac_ids=ac_ids, tdd_ids=tdd_ids, final=phase == "final")
return errors
def main() -> int:
parser = argparse.ArgumentParser(description="Validate an EliteForge multi-agent feature-oriented TDD workspace.")
parser.add_argument("feature_dir", type=Path, help="Path to docs/features/<feature-slug>")
parser.add_argument("--phase", choices=["spec", "tasks", "worktrees", "reports", "final"], default="final")
parser.add_argument(
"--spec-stage", choices=["feature", "database", "api", "acceptance", "test-plan"], default="feature",
help="When --phase spec is used, validate only the current progressive design gate.",
)
parser.add_argument(
"--complete-spec", action="store_true",
help="When --phase spec is used, require all progressive spec files plus test-plan.",
)
parser.add_argument(
"--no-downstream-artifacts", action="store_true",
help="When validating a 0-1 spec/test-plan gate, fail if tasks, orchestration, or reports were created early.",
)
args = parser.parse_args()
root = args.feature_dir
if not root.is_dir():
print(f"ERROR: feature directory not found: {root}", file=sys.stderr)
return 2
errors = validate(root, args.phase, args.complete_spec, args.spec_stage, args.no_downstream_artifacts)
if errors:
print("Feature docs validation failed:")
for error in errors:
print(f"- {error}")
return 1
print(f"Feature docs validation OK: {root} ({args.phase})")
return 0
if __name__ == "__main__":
raise SystemExit(main())
"""EliteForge feature workspace validators package."""
from .common import * # noqa: F401,F403
from .specs import SPEC_REQUIRED_FILES, SPEC_COMPLETE_FILES, SPEC_STAGE_FILES, check_specs # noqa: F401
from .tasks import TASK_FILES, TASK_HEADINGS, TASK_PHASES, FINAL_TASK_STATUSES, READY_TASK_STATUSES, check_tasks # noqa: F401
from .reports import REPORT_FILES, check_reports # noqa: F401
from .context import check_context_index # noqa: F401
from .worktrees import ORCHESTRATION_FILES, check_orchestration, check_feature_evolution_log # noqa: F401
"""Common utilities and shared constants for EliteForge feature workspace validators."""
from __future__ import annotations
import re
from pathlib import Path
from typing import Iterable
HEADING_ALIASES = {
"Acceptance Index": ("Acceptance Index", "Acceptance Criteria", "Acceptance Criteria Matrix"),
"Test Boundary": ("Test Boundary", "Test Boundary Matrix"),
"Command Index": ("Command Index", "Command Matrix"),
"Proof Rows": ("Proof Rows",),
"Task Rows": ("Task Rows", "Task Index", "Task Table"),
"Evidence Rows": ("Evidence Rows",),
}
AC_ID_RE = re.compile(r"\bAC-[A-Z0-9]+-\d{2,}\b")
TDD_ID_RE = re.compile(r"\bTDD-[A-Z0-9]+-\d{2,}\b")
CMD_ID_RE = re.compile(r"\bCMD-[A-Z0-9]+(?:-[A-Z0-9]+)*\b")
FORBIDDEN_GLOBAL_ID_RE = re.compile(r"\b(?:INT|TASK|BVE)-[A-Z0-9]+-\d{2,}\b|\bEV-(?:[A-Z0-9]+-)?\d{2,}\b")
EV_ID_RE = re.compile(r"\b(?:EV-\d{2,}|\d+)\b")
REF_RE = re.compile(r"@ref:([A-Za-z0-9_./-]+\.md)#([A-Za-z0-9_.-]+)")
REF_TOKEN_RE = re.compile(r"@ref:\S+")
INLINE_ANCHOR_RE = re.compile(r'<a\s+(?:id|name)\s*=\s*["\']([^"\']+)["\']', re.IGNORECASE)
INLINE_HASH_ANCHOR_RE = re.compile(r"\{#([^{}]+)\}")
PLACEHOLDER_VALUES = {"", "-", "none", "n/a", "tbd", "todo", "unknown", "not discovered"}
GIT_SKILLS = {"eliteforge-git-feature-oriented-spec", "eliteforge-git-feature-oriented-lite-spec"}
RUNTIME_PENDING_VALUES = {"pending", "pending-spawn", "not spawned", "not_spawned"}
FINAL_MARKERS = re.compile(
r"\b(TODO|TBD|FIXME)\b|waiting_human_decision|待确认|未定|Not run|Not reviewed",
re.IGNORECASE,
)
SLOW_BROWSER_COMMAND_RE = re.compile(r"\b(playwright|cypress)\b|browser[- ]runtime|full\s+e2e", re.IGNORECASE)
DOWNSTREAM_ARTIFACT_DIRS = ("tasks", "orchestration", "reports")
INTERACTION_KEYWORDS = re.compile(
r"\b(click|input|select|submit|navigate|navigation|dialog|search|filter|pagination|paginate|reset|clear|export|download|create|add|new)\b|"
r"点击|输入|选择|提交|导航|弹窗|搜索|查询|筛选|分页|翻页|重置|清空|导出|下载|创建|新建|新增",
re.IGNORECASE,
)
TRACEABILITY_FEATURE_SECTIONS = (
"Requirement",
"Page And Module Surface",
)
TRACEABILITY_KEYWORDS = {
"export": (
re.compile(r"\b(export|download)\b|导出|下载", re.IGNORECASE),
re.compile(r"\b(export|download)\b|导出|下载", re.IGNORECASE),
),
"create": (
re.compile(
r"\b(create|new|add)\s+(api|endpoint|button|action|dialog|form|record|item|row|entry|user|role|task)\b|"
r"\b(api|endpoint|button|action|dialog|form)\s+(for\s+)?(create|new|add)\b|创建|新建|新增",
re.IGNORECASE,
),
re.compile(r"\b(create|new|add)\b|创建|新建|新增", re.IGNORECASE),
),
"search": (
re.compile(r"\b(search|query|filter)\b|搜索|查询|筛选", re.IGNORECASE),
re.compile(r"\b(search|query|filter)\b|搜索|查询|筛选", re.IGNORECASE),
),
"pagination": (
re.compile(r"\b(pagination|paginate|page\s+size|next\s+page|previous\s+page)\b|分页|翻页|页码", re.IGNORECASE),
re.compile(r"\b(pagination|paginate|page\s+size|next\s+page|previous\s+page)\b|分页|翻页|页码", re.IGNORECASE),
),
"reset": (
re.compile(r"\b(reset|clear)\b|重置|清空", re.IGNORECASE),
re.compile(r"\b(reset|clear)\b|重置|清空", re.IGNORECASE),
),
}
def read_text(path: Path) -> str:
try:
return path.read_text(encoding="utf-8")
except UnicodeDecodeError:
return path.read_text()
def headings(text: str) -> set[str]:
found: set[str] = set()
for line in text.splitlines():
match = re.match(r"^#{1,6}\s+(.+?)\s*$", line)
if match:
found.add(match.group(1).strip())
return found
def heading_options(title: str) -> tuple[str, ...]:
return HEADING_ALIASES.get(title, (title,))
def has_heading(found: set[str], title: str) -> bool:
return any(option in found for option in heading_options(title))
def section(text: str, title: str) -> str:
lines = text.splitlines()
capture = False
body: list[str] = []
current_level = None
for line in lines:
match = re.match(r"^(#{1,6})\s+(.+?)\s*$", line)
if match and match.group(2).strip() == title:
capture = True
current_level = len(match.group(1))
continue
if capture and match and len(match.group(1)) <= (current_level or 6):
break
if capture:
body.append(line)
return "\n".join(body).strip()
def section_any(text: str, title: str) -> str:
for option in heading_options(title):
body = section(text, option)
if body:
return body
return ""
def key_value(section_text: str, key: str) -> str:
pattern = re.compile(rf"^-?\s*{re.escape(key)}\s*:\s*(.+?)\s*$", re.IGNORECASE | re.MULTILINE)
match = pattern.search(section_text)
return match.group(1).strip() if match else ""
def normalized(value: str) -> str:
return value.strip().strip("`").lower()
def is_placeholder(value: str) -> bool:
return normalized(value) in PLACEHOLDER_VALUES
def is_template_placeholder(value: str) -> bool:
return "{{" in value or "}}" in value or is_placeholder(value)
def markdown_anchor(title: str) -> str:
anchor = title.strip().lower()
anchor = re.sub(r"`([^`]+)`", r"\1", anchor)
anchor = re.sub(r"[^a-z0-9_. -]+", "", anchor)
anchor = re.sub(r"\s+", "-", anchor)
return anchor.strip("-")
def inline_anchors(text: str) -> set[str]:
found: set[str] = set()
for match in INLINE_ANCHOR_RE.finditer(text):
found.add(match.group(1).strip().lower())
for match in INLINE_HASH_ANCHOR_RE.finditer(text):
found.add(match.group(1).strip().lower())
return found
def target_has_anchor(text: str, anchor: str) -> bool:
if anchor in text:
return True
normalized_anchor = anchor.lower()
for heading in headings(text):
if markdown_anchor(heading) == normalized_anchor:
return True
for canonical, aliases in HEADING_ALIASES.items():
if heading in aliases and markdown_anchor(canonical) == normalized_anchor:
return True
for ia in inline_anchors(text):
if ia == normalized_anchor or ia.replace("-", " ") == normalized_anchor or ia.replace("_", " ") == normalized_anchor:
return True
return False
def has_ref(text: str) -> bool:
return bool(REF_RE.search(text))
def check_references(root: Path, source_rel: str, text: str, errors: list[str]) -> None:
refs: list[tuple[str, str]] = []
for token in REF_TOKEN_RE.finditer(text):
normalized_token = token.group(0).rstrip(".,;:)]}")
match = REF_RE.fullmatch(normalized_token)
if not match:
errors.append(f"{source_rel}: invalid @ref syntax '{normalized_token}'")
continue
refs.append(match.groups())
for ref_path, anchor in refs:
target_rel = Path(ref_path)
if target_rel.is_absolute() or ".." in target_rel.parts:
errors.append(f"{source_rel}: @ref target must be a safe relative markdown path: {ref_path}")
continue
target = root / target_rel
if not target.is_file():
errors.append(f"{source_rel}: @ref target not found: {ref_path}")
continue
if not target_has_anchor(read_text(target), anchor):
errors.append(f"{source_rel}: @ref Anchor not found: {ref_path}#{anchor}")
def check_file(root: Path, rel: str, required_headings: Iterable[str], errors: list[str]) -> str:
path = root / rel
if not path.is_file():
errors.append(f"missing file: {rel}")
return ""
text = read_text(path)
found = headings(text)
for heading in required_headings:
if not has_heading(found, heading):
errors.append(f"{rel}: missing heading '{heading}'")
check_references(root, rel, text, errors)
return text
def table_body_rows(text: str, title: str) -> list[list[str]]:
rows: list[list[str]] = []
in_table = False
for line in section_any(text, title).splitlines():
stripped = line.strip()
if not stripped.startswith("|"):
if in_table:
break
continue
in_table = True
cells = [cell.strip() for cell in stripped.strip("|").split("|")]
if not cells or set(cells[0]) <= {"-"}:
continue
header_tokens = {
"acceptance id",
"cycle id",
"command",
"task id",
"agent id",
"git agent id",
"event id",
"conflict id",
"object",
"id",
"layer",
"scope",
"dependency feature name",
"ac_refs",
}
if cells[0].lower() in header_tokens:
continue
rows.append(cells)
return rows
def subsection_blocks(text: str, title: str) -> list[str]:
blocks: list[str] = []
current: list[str] = []
for line in section_any(text, title).splitlines():
if re.match(r"^#{3,6}\s+.+", line):
if current:
blocks.append("\n".join(current).strip())
current = [line]
continue
if current:
current.append(line)
if current:
blocks.append("\n".join(current).strip())
return blocks
def section_has_unresolved_items(text: str, title: str) -> bool:
body = section(text, title)
meaningful = [
line.strip()
for line in body.splitlines()
if line.strip()
and not re.match(r"^[-*]?\s*(None|No unresolved .+|No blockers|No remaining risks)\.?$", line.strip(), re.IGNORECASE)
and not line.strip().startswith("|")
]
return bool(meaningful)
def is_not_applicable_body(text: str) -> bool:
stripped = "\n".join(line.strip() for line in text.splitlines() if line.strip())
return bool(re.fullmatch(r"(?i)not applicable\.?", stripped))
def dependency_ids(cell: str) -> set[str]:
if normalized(cell) in {"none", "n/a", "-"}:
return set()
return {dep.strip() for dep in re.split(r"[,;\s]+", cell) if dep.strip()}
def id_module(identifier: str) -> str:
parts = identifier.split("-")
return parts[1] if len(parts) >= 3 else ""
def tdd_ids_from_text(text: str) -> set[str]:
return set(TDD_ID_RE.findall(text))
def acceptance_ids_from_acceptance(text: str) -> set[str]:
return set(AC_ID_RE.findall(section_any(text, "Acceptance Index") or text))
def acceptance_ids_from_cell(cell: str) -> set[str]:
return set(AC_ID_RE.findall(cell))
"""Validate generated context-index.md navigation rows."""
from __future__ import annotations
from pathlib import Path
from . import common as C
def _split_table_row(line: str) -> list[str]:
return [cell.strip() for cell in line.strip().strip("|").split("|")]
def context_rows(text: str) -> list[list[str]]:
rows: list[list[str]] = []
for line in text.splitlines():
stripped = line.strip()
if not stripped.startswith("|"):
continue
cells = _split_table_row(stripped)
if not cells or cells[0].lower() == "file" or set(cells[0]) <= {"-"}:
continue
if len(cells) >= 6:
rows.append(cells[:6])
return rows
def check_context_index(root: Path, errors: list[str], required: bool) -> None:
rel = "orchestration/context-index.md"
path = root / rel
if not path.exists():
if required:
errors.append(f"missing file: {rel}")
return
text = C.check_file(root, rel, ["Context Index"], errors)
rows = context_rows(text)
if not rows:
errors.append(f"{rel}: must contain a file/kind/owns/headings/source_refs/ac_refs table")
return
for row in rows:
file_rel, _kind, _owns, headings, _source_refs, ac_refs = row
target = root / file_rel
if not target.is_file():
errors.append(f"{rel}: indexed file not found: {file_rel}")
continue
target_text = C.read_text(target)
if headings.lower() != "none":
for heading in [item.strip() for item in headings.split(",") if item.strip() and not item.strip().startswith("+")]:
if not C.target_has_anchor(target_text, C.markdown_anchor(heading)):
errors.append(f"{rel}: indexed heading '{heading}' not found in {file_rel}")
if ac_refs.lower() != "none":
target_acs = set(C.AC_ID_RE.findall(target_text))
for ac_id in C.AC_ID_RE.findall(ac_refs):
if ac_id not in target_acs:
errors.append(f"{rel}: indexed AC {ac_id} not found in {file_rel}")
"""Validate AC-keyed report files and evidence completeness."""
from __future__ import annotations
import re
from pathlib import Path
from . import common as C
REPORT_FILES = [
"reports/database-result.md",
"reports/backend-result.md",
"reports/frontend-result.md",
"reports/test-report.md",
"reports/review-report.md",
"reports/worktree-report.md",
]
REPORT_HEADINGS = [
"Summary",
"Evidence Rows",
"Changed Paths",
"Commands",
"Blockers",
]
EVIDENCE_ROW_RE = re.compile(r"^\s*[-*]\s*(?P<ac>AC-[A-Z0-9]+-\d{2,})\s*\|\s*(?P<rest>.+?)\.?\s*$")
def field_value(line: str, field: str) -> str:
match = re.search(rf"\b{re.escape(field)}\s*:\s*([^|.]+)", line, re.IGNORECASE)
return match.group(1).strip() if match else ""
def command_aliases(root: Path) -> set[str]:
test_plan = C.read_text(root / "spec/test-plan.md") if (root / "spec/test-plan.md").exists() else ""
return set(C.CMD_ID_RE.findall(C.section_any(test_plan, "Command Index")))
def browser_required_acs(root: Path) -> set[str]:
test_plan = C.read_text(root / "spec/test-plan.md") if (root / "spec/test-plan.md").exists() else ""
required: set[str] = set()
for line in C.section_any(test_plan, "Proof Rows").splitlines():
if not line.strip().startswith(("-", "*")):
continue
ids = C.AC_ID_RE.findall(line)
layer = field_value(line, "layer").lower()
interaction = field_value(line, "interaction").lower()
if "browser" in layer or (interaction and interaction != "not applicable"):
required.update(ids)
return required
def evidence_rows(text: str) -> list[dict[str, str]]:
rows: list[dict[str, str]] = []
for line in C.section_any(text, "Evidence Rows").splitlines():
stripped = line.strip()
if not stripped.startswith(("-", "*")):
continue
match = EVIDENCE_ROW_RE.match(stripped)
if not match:
rows.append({"acceptance_id": "", "_invalid": "expected AC-keyed evidence row", "_line": stripped})
continue
rows.append({"acceptance_id": match.group("ac"), "rest": match.group("rest"), "_line": stripped})
return rows
def concrete_browser_evidence(rest: str) -> bool:
browser = field_value(rest, "browser")
if not browser or browser.lower() == "not applicable":
return False
if re.search(r"smoke\s+passed", browser, re.IGNORECASE):
return False
lowered = browser.lower()
return all(token in lowered for token in ("clicks=", "inputs=", "network=", "feedback=", "artifacts=")) and "pending" not in lowered
def check_reports(
root: Path,
task_rows: dict[str, list[dict[str, str]]],
errors: list[str],
ac_ids: set[str],
tdd_ids: set[str],
final: bool,
) -> None:
del task_rows, tdd_ids
found_reports = [rel for rel in REPORT_FILES if (root / rel).exists()]
if final and not found_reports:
errors.append("reports: no report files found for final validation")
return
commands = command_aliases(root)
browser_acs = browser_required_acs(root)
acs_with_evidence: set[str] = set()
acs_with_browser: set[str] = set()
for rel in found_reports:
text = C.check_file(root, rel, REPORT_HEADINGS, errors)
if not text:
continue
rows = evidence_rows(text)
if final and not rows:
errors.append(f"{rel}: Evidence Rows must contain at least one AC-keyed row")
for row in rows:
if row.get("_invalid"):
errors.append(f"{rel}: evidence row '{row.get('_line', '')}' {row['_invalid']}")
continue
ac_id = row["acceptance_id"]
rest = row["rest"]
acs_with_evidence.add(ac_id)
if ac_id not in ac_ids:
errors.append(f"{rel}: evidence row references unknown acceptance id {ac_id}")
for field in ("red", "green", "command_ref", "browser", "status"):
if not field_value(rest, field):
errors.append(f"{rel}: evidence row for {ac_id} missing `{field}:`")
command_ref = field_value(rest, "command_ref")
if command_ref != "none" and command_ref not in commands:
errors.append(f"{rel}: evidence row for {ac_id} references unknown command_ref {command_ref}")
if C.TDD_ID_RE.search(row.get("_line", "")) or C.FORBIDDEN_GLOBAL_ID_RE.search(row.get("_line", "")):
errors.append(f"{rel}: evidence row for {ac_id} must not create non-AC global ids")
if concrete_browser_evidence(rest):
acs_with_browser.add(ac_id)
elif re.search(r"\bbrowser\s*:\s*(?!not applicable)", rest, re.IGNORECASE):
if final:
errors.append(
f"{rel}: browser evidence for {ac_id} must include concrete clicks, inputs, network, feedback, artifacts and no pending/smoke"
)
if C.section_has_unresolved_items(text, "Blockers"):
errors.append(f"{rel}: contains unresolved blockers")
if final and (C.FINAL_MARKERS.search(text) or re.search(r"\bpending\b", text, re.IGNORECASE)):
errors.append(f"{rel}: contains unresolved markers (TODO/TBD/waiting_human_decision/pending/not run etc.) in final validation")
if final:
missing_evidence = ac_ids - acs_with_evidence
if missing_evidence:
errors.append(f"reports: missing final evidence rows for AC ids: {', '.join(sorted(missing_evidence))}")
missing_browser = browser_acs - acs_with_browser
if missing_browser:
errors.append(f"reports: missing concrete browser evidence for AC ids: {', '.join(sorted(missing_browser))}")
"""Validate AC-only spec files: feature-spec, database-design, api-contract, acceptance, and test-plan."""
from __future__ import annotations
import re
from pathlib import Path
from . import common as C
SPEC_REQUIRED_FILES = {
"spec/feature-spec.md": [
"Initial Agent Draft",
"Feature Identity And Context",
"Requirement",
"Product Source Slices",
"Scope Boundary",
"Assumptions",
"In Scope",
"Out Of Scope",
"Constraints",
"Feature Model",
"Page And Module Surface",
"Repository Findings",
"Open Decisions",
],
"spec/database-design.md": [
"Database Decision",
"Existing Schema Impact",
"Proposed Schema",
"Relationships And Cardinality",
"Migration Plan",
"Query And Performance Plan",
"Data Governance",
"Test Data And Fixtures",
"Validation Commands",
],
"spec/api-contract.md": [
"API Overview",
"Endpoints",
"Auth And Permissions",
"Error Contract",
"Frontend Backend Boundary",
"Database And Transaction Boundary",
"Mock Or Adapter Expectations",
],
"spec/acceptance.md": [
"Acceptance Index",
"Open Decisions",
],
"spec/test-plan.md": [
"Test Plan Decision",
"Proof Rows",
"Command Index",
"Evidence Rules",
],
}
SPEC_STAGE_FILES = {
"feature": ["spec/feature-spec.md"],
"database": ["spec/feature-spec.md", "spec/database-design.md"],
"api": ["spec/feature-spec.md", "spec/database-design.md", "spec/api-contract.md"],
"acceptance": [
"spec/feature-spec.md",
"spec/database-design.md",
"spec/api-contract.md",
"spec/acceptance.md",
],
"test-plan": [
"spec/feature-spec.md",
"spec/database-design.md",
"spec/api-contract.md",
"spec/acceptance.md",
"spec/test-plan.md",
],
}
SPEC_COMPLETE_FILES = dict(SPEC_REQUIRED_FILES)
AC_LINE_RE = re.compile(
r"^\s*[-*]\s*(?P<ac>AC-[A-Z0-9]+-\d{2,})\s+"
r"\[(?P<priority>must|should|could)\]\s+"
r"\[(?P<type>business|interaction|api|database|security|regression)\]\s+"
r"(?P<body>.+?);\s*proof:\s*(?P<proof>.+?)\.?\s*$",
re.IGNORECASE,
)
PROOF_ROW_RE = re.compile(r"^\s*[-*]\s*(?P<ac>AC-[A-Z0-9]+-\d{2,})\s*\|\s*(?P<rest>.+?)\.?\s*$")
COMMAND_ROW_RE = re.compile(r"^\s*[-*]\s*(?P<cmd>CMD-[A-Z0-9]+(?:-[A-Z0-9]+)*)\s*\|\s*(?P<rest>.+?)\.?\s*$")
def bullet_lines(text: str, title: str) -> list[str]:
return [line.strip() for line in C.section_any(text, title).splitlines() if line.strip().startswith(("-", "*"))]
def field_value(line: str, field: str) -> str:
match = re.search(rf"\b{re.escape(field)}\s*:\s*([^|.]+)", line, re.IGNORECASE)
return match.group(1).strip() if match else ""
def check_feature_spec(text: str, errors: list[str]) -> None:
if C.AC_ID_RE.search(text):
errors.append("spec/feature-spec.md: must not define or reference AC-* ids; ACs belong only in spec/acceptance.md")
if C.TDD_ID_RE.search(text) or C.FORBIDDEN_GLOBAL_ID_RE.search(text):
errors.append("spec/feature-spec.md: must not contain TDD/INT/TASK/BVE/EV ids")
if re.search(r"\b(red|green)\s*:", text, re.IGNORECASE) or re.search(r"\bcommand_ref\s*:", text, re.IGNORECASE):
errors.append("spec/feature-spec.md: must not contain RED/GREEN proof fields or command references")
if re.search(r"\bbrowser\s*:\s*clicks=", text, re.IGNORECASE):
errors.append("spec/feature-spec.md: must not contain browser evidence")
dependency_context = C.section_any(text, "Feature Identity And Context")
if not C.key_value(dependency_context, "dependency_features"):
errors.append("spec/feature-spec.md: Feature Identity And Context must include dependency_features")
if not C.key_value(dependency_context, "dependency_contracts"):
errors.append("spec/feature-spec.md: Feature Identity And Context must include dependency_contracts")
def acceptance_lines(text: str) -> list[str]:
return bullet_lines(text, "Acceptance Index")
def check_acceptance(text: str, errors: list[str]) -> set[str]:
lines = acceptance_lines(text)
if not lines:
errors.append("spec/acceptance.md: Acceptance Index must contain at least one AC row")
return set()
ac_ids: set[str] = set()
seen: set[str] = set()
section = C.section_any(text, "Acceptance Index")
non_bullets = [
line.strip()
for line in section.splitlines()
if line.strip()
and not line.strip().startswith(("-", "*"))
and not line.strip().startswith("Format:")
and not line.strip().startswith("Only ")
and not line.strip().startswith("Rules:")
and not line.strip().startswith("Valid types:")
]
if non_bullets:
errors.append("spec/acceptance.md: Acceptance Index must be compact AC bullet rows, not prose paragraphs")
for line in lines:
match = AC_LINE_RE.match(line)
if not match:
errors.append(
"spec/acceptance.md: each AC row must match `AC-<MODULE>-NN [must|should|could] [type] ...; proof: ...`"
)
continue
ac_id = match.group("ac")
if ac_id in seen:
errors.append(f"spec/acceptance.md: duplicate AC id {ac_id}")
seen.add(ac_id)
ac_ids.add(ac_id)
if len(match.group("body").split()) > 28:
errors.append(f"spec/acceptance.md: {ac_id} is too verbose; keep AC rows to one compact completion judgment")
if C.CMD_ID_RE.search(line) or re.search(r"\b(command|red|green|clicks|inputs|network|feedback)\s*:", line, re.IGNORECASE):
errors.append(f"spec/acceptance.md: {ac_id} must not contain commands, test steps, or evidence fields")
if C.TDD_ID_RE.search(line) or C.FORBIDDEN_GLOBAL_ID_RE.search(line):
errors.append(f"spec/acceptance.md: {ac_id} must not reference non-AC global ids")
return ac_ids
def command_rows(test_plan: str) -> dict[str, str]:
rows: dict[str, str] = {}
for line in bullet_lines(test_plan, "Command Index"):
match = COMMAND_ROW_RE.match(line)
if match:
rows[match.group("cmd")] = match.group("rest")
return rows
def check_command_index(test_plan: str, errors: list[str]) -> set[str]:
rows = command_rows(test_plan)
if not rows:
errors.append("spec/test-plan.md: Command Index must contain at least one CMD-* row")
return set()
for cmd_id, rest in rows.items():
for field in ("command", "cost", "when", "rerun", "expect"):
if not field_value(rest, field):
errors.append(f"spec/test-plan.md: {cmd_id} missing `{field}:` in Command Index")
cost = field_value(rest, "cost").lower()
when = field_value(rest, "when").lower()
rerun = field_value(rest, "rerun").lower()
line = f"{cmd_id} | {rest}"
if C.SLOW_BROWSER_COMMAND_RE.search(line) and cost != "slow/browser":
errors.append(f"spec/test-plan.md: {cmd_id} Playwright/Cypress/browser command must be marked cost: slow/browser")
if cost == "slow/browser":
if when == "review":
errors.append(f"spec/test-plan.md: {cmd_id} slow/browser command must not be unconditional review; use verify or review-if-stale")
if rerun in {"always", "review", "every review", "unconditional review"}:
errors.append(f"spec/test-plan.md: {cmd_id} slow/browser rerun policy must not be unconditional review")
return set(rows)
def proof_lines(test_plan: str) -> list[str]:
return bullet_lines(test_plan, "Proof Rows")
def check_test_plan(test_plan: str, ac_ids: set[str], errors: list[str], complete: bool) -> set[str]:
commands = check_command_index(test_plan, errors)
lines = proof_lines(test_plan)
if not lines:
errors.append("spec/test-plan.md: Proof Rows must contain at least one AC-keyed proof row")
return set()
proof_acs: set[str] = set()
for line in lines:
match = PROOF_ROW_RE.match(line)
if not match:
errors.append("spec/test-plan.md: each Proof Row must start with `AC-<MODULE>-NN | ...`")
continue
ac_id = match.group("ac")
proof_acs.add(ac_id)
if ac_ids and ac_id not in ac_ids:
errors.append(f"spec/test-plan.md: Proof Row references unknown AC id {ac_id}")
rest = match.group("rest")
for field in ("layer", "case", "red", "green", "command_ref"):
if not field_value(rest, field):
errors.append(f"spec/test-plan.md: Proof Row for {ac_id} missing `{field}:`")
command_ref = field_value(rest, "command_ref").strip()
if command_ref not in commands:
errors.append(f"spec/test-plan.md: Proof Row for {ac_id} references unknown command_ref {command_ref or '<missing>'}")
layer = field_value(rest, "layer").lower()
interaction = field_value(rest, "interaction")
if ("browser" in layer or "frontend" in layer) and not interaction:
errors.append(f"spec/test-plan.md: browser/frontend Proof Row for {ac_id} must include `interaction:`")
if C.TDD_ID_RE.search(line) or C.FORBIDDEN_GLOBAL_ID_RE.search(line):
errors.append(f"spec/test-plan.md: Proof Row for {ac_id} must not create non-AC global ids")
if complete and re.search(r"\bstatus\s*:\s*draft\b", line, re.IGNORECASE):
errors.append(f"spec/test-plan.md: Proof Row for {ac_id} must not remain draft after complete spec validation")
if complete:
missing = ac_ids - proof_acs
if missing:
errors.append(f"spec/test-plan.md: missing Proof Rows for AC ids: {', '.join(sorted(missing))}")
return proof_acs
def check_decision_statuses(root: Path, errors: list[str], require_approved: bool) -> None:
db_text = C.read_text(root / "spec/database-design.md") if (root / "spec/database-design.md").exists() else ""
test_plan_text = C.read_text(root / "spec/test-plan.md") if (root / "spec/test-plan.md").exists() else ""
if db_text:
db_status = C.normalized(C.key_value(C.section(db_text, "Database Decision"), "status"))
if require_approved and db_status not in {"approved", "not_applicable"}:
errors.append("spec/database-design.md: Database Decision status must be approved or not_applicable before task execution")
if test_plan_text:
test_plan_status = C.normalized(C.key_value(C.section(test_plan_text, "Test Plan Decision"), "status"))
if require_approved and test_plan_status not in {"approved", "approved_with_defaults"}:
errors.append("spec/test-plan.md: Test Plan Decision status must be approved or approved_with_defaults before task breakdown")
def check_specs(root: Path, errors: list[str], complete: bool, require_approved: bool, spec_stage: str) -> tuple[set[str], set[str]]:
selected = SPEC_COMPLETE_FILES if complete else {rel: SPEC_REQUIRED_FILES[rel] for rel in SPEC_STAGE_FILES[spec_stage]}
texts: dict[str, str] = {}
for rel, required in selected.items():
texts[rel] = C.check_file(root, rel, required, errors)
feature_spec = texts.get("spec/feature-spec.md", "")
if feature_spec:
check_feature_spec(feature_spec, errors)
acceptance = texts.get("spec/acceptance.md", "")
ac_ids = check_acceptance(acceptance, errors) if acceptance else set()
test_plan = texts.get("spec/test-plan.md", "")
if test_plan:
check_test_plan(test_plan, ac_ids, errors, complete=complete)
if complete:
check_decision_statuses(root, errors, require_approved=require_approved)
return ac_ids, set()