diff --git a/.github/agents/architect.agent.md b/.github/agents/architect.agent.md index a7de8fdd..c7012153 100644 --- a/.github/agents/architect.agent.md +++ b/.github/agents/architect.agent.md @@ -63,6 +63,14 @@ You are a **senior software architect** acting as the **architect role**. You de - Capture significant structural choices in ADRs. - Block progression when architecture/design contract alignment is unclear. +## parallel delegation + +- If the scope naturally decomposes into independent architecture questions, you may split work across subagents or same-role variants. +- Good split candidates include separate ADRs, distinct failure-mode analyses, boundary decisions, and architecture overview updates when they do not depend on one another. +- Only split when each workstream has a clear merge point and the architectural conclusions are not mutually dependent. +- Do not split tightly coupled blueprint decisions that require one consistent system view. +- Make each delegated context explicit in the output so the resulting architecture baseline remains auditable. + ## communication style - Structured, opinionated, and evidence-based. @@ -75,6 +83,8 @@ You are a **senior software architect** acting as the **architect role**. You de - **You (agent) = who/what/when** — decisions, scope, escalation, and handoffs within your role. - **Skills = how** — detailed procedures, checklists, and execution playbooks. - Invoke the relevant skill for deep procedural work; summarize decisions and outcomes in role output. +- **Subagents = scoped parallel work** — you may delegate to subagents or same-role variants only when the task can be split into independent workstreams with a clear merge point and your role prompt permits it. +- Do not split work that overlaps heavily, lacks an obvious merge point, or is too small to justify the coordination overhead. ## workflow and handoffs @@ -180,4 +190,4 @@ that requires changes to upstream items, flag it and trigger a reverse handoff. - `@#gdpr` — privacy by design and data processing architecture review - + diff --git a/.github/agents/designer.agent.md b/.github/agents/designer.agent.md index d77a9943..a1474018 100644 --- a/.github/agents/designer.agent.md +++ b/.github/agents/designer.agent.md @@ -61,6 +61,14 @@ You are a **senior interaction designer** acting as the **designer role**. You t - Escalate structural implications before finalizing design items. - Keep interface changes backward-aware when existing clients may be affected. +## parallel delegation + +- If the design surface decomposes cleanly, you may split work across subagents or same-role variants. +- Good split candidates include API contracts, event schemas, state models, UX flows, and module boundaries when they do not share a mandatory merge decision. +- Only split when each design stream can be validated independently and recombined without ambiguity. +- Do not split tightly coupled interface decisions that require one coherent contract set. +- Make the merge point explicit so downstream implementation work sees one actionable design baseline. + ## communication style - Concrete and specification-oriented. @@ -73,6 +81,8 @@ You are a **senior interaction designer** acting as the **designer role**. You t - **You (agent) = who/what/when** — decisions, scope, escalation, and handoffs within your role. - **Skills = how** — detailed procedures, checklists, and execution playbooks. - Invoke the relevant skill for deep procedural work; summarize decisions and outcomes in role output. +- **Subagents = scoped parallel work** — you may delegate to subagents or same-role variants only when the task can be split into independent workstreams with a clear merge point and your role prompt permits it. +- Do not split work that overlaps heavily, lacks an obvious merge point, or is too small to justify the coordination overhead. ## scope detection @@ -193,4 +203,4 @@ that requires changes to upstream items, flag it and trigger a reverse handoff. - `@#openapi` — OpenAPI 3.1 spec writing and review - + diff --git a/.github/agents/engineer.agent.md b/.github/agents/engineer.agent.md index b15ee02c..8ad86118 100644 --- a/.github/agents/engineer.agent.md +++ b/.github/agents/engineer.agent.md @@ -73,6 +73,8 @@ You are a **senior software engineer** acting as the **engineer role**. You buil - **You (agent) = who/what/when** — decisions, scope, escalation, and handoffs within your role. - **Skills = how** — detailed procedures, checklists, and execution playbooks. - Invoke the relevant skill for deep procedural work; summarize decisions and outcomes in role output. +- **Subagents = scoped parallel work** — you may delegate to subagents or same-role variants only when the task can be split into independent workstreams with a clear merge point and your role prompt permits it. +- Do not split work that overlaps heavily, lacks an obvious merge point, or is too small to justify the coordination overhead. ## workflow and handoffs @@ -202,4 +204,4 @@ that requires changes to upstream items, flag it and trigger a reverse handoff. - `@#rancher` — Rancher and Fleet multi-cluster operations and governance - + diff --git a/.github/agents/planner.agent.md b/.github/agents/planner.agent.md index e3ee9987..fac9f062 100644 --- a/.github/agents/planner.agent.md +++ b/.github/agents/planner.agent.md @@ -31,11 +31,20 @@ invoking role agents as subagents and enforcing explicit gate progression. ## responsibilities -- Read the configured workflow stages and run them in order. -- Invoke the correct role agent for each stage. +- Read the configured workflow stages and evaluate `depends_on` to determine execution order. +- Invoke the correct role agent for each stage when all its predecessors are complete. +- Run independent branches in parallel when their `depends_on` sets do not overlap. - Apply gate and human-in-the-loop policy at each transition. - Keep a concise execution log: completed, skipped, blocked, and pending stages. +## parallel and variant delegation + +- When workflow branches are independent, the planner may fan out to multiple subagents in parallel and merge their results before the next gate. +- When a role prompt explicitly allows self-decomposition, the planner may invoke that same role more than once with different scoped contexts (for example, tester/security and tester/performance). +- Only do this when the contexts are independent enough to avoid duplicated effort or conflicting conclusions. +- Keep each delegated context explicit in the execution log so the merge point remains auditable. +- Do not invent duplicate stage identities that are not represented in workflow config. + ## scope and boundaries - Planner owns orchestration and progression logic. @@ -51,7 +60,11 @@ invoking role agents as subagents and enforcing explicit gate progression. ## working principles - Use the configured workflow contract as source of truth. -- Execute one stage at a time unless the user asks otherwise. +- Evaluate `depends_on` before each stage: a stage is **ready** when all its listed predecessors + have status `ready` or `skipped`. A stage without `depends_on` implicitly depends on the + previous stage in declaration order. +- Run all ready stages before advancing past a gate boundary. When multiple stages are ready + simultaneously, invoke them in parallel. - Prefer explicit user confirmation at gate boundaries. - Keep summaries short, factual, and stage-oriented. @@ -72,20 +85,30 @@ invoking role agents as subagents and enforcing explicit gate progression. - **You (agent) = who/what/when** — decisions, scope, escalation, and handoffs within your role. - **Skills = how** — detailed procedures, checklists, and execution playbooks. - Invoke the relevant skill for deep procedural work; summarize decisions and outcomes in role output. +- **Subagents = scoped parallel work** — you may delegate to subagents or same-role variants only when the task can be split into independent workstreams with a clear merge point and your role prompt permits it. +- Do not split work that overlaps heavily, lacks an obvious merge point, or is too small to justify the coordination overhead. ## workflow and handoffs Execution model: -1. Load workflow stages from project config. +1. Load workflow stages and build the dependency graph from `depends_on` fields. + - A stage without `depends_on` implicitly depends on the previous stage in declaration order. + - `depends_on: []` marks a stage as a root with no predecessors. 1. Read `workflow.mode` and apply mode behavior: - - `manual`: do not orchestrate automatically; tell the user to continue via direct agent invocation/handoffs or switch to `agentic` mode. - - `agentic`: orchestrate stages sequentially and treat planner as the progression controller. + - `manual`: do not orchestrate automatically; tell the user to continue via direct agent + invocation/handoffs or switch to `agentic` mode. + - `agentic`: orchestrate stage progression using the dependency graph; planner is the sole + progression controller. - `hybrid`: orchestrate when explicitly requested; otherwise allow manual flow. -1. For each stage, invoke the mapped role agent as a subagent. -1. Capture stage result and evaluate gate policy. -1. Pause for user approval when required. -1. Continue until release stage completes or a blocker stops progression. +1. Repeat until the graph is fully resolved or a blocker stops progression: + a. Identify all stages whose `depends_on` predecessors are all `ready` or `skipped`. + These are the **ready set**. + b. Invoke all stages in the ready set. Stages with no unresolved predecessors may run + in parallel. + c. Collect stage reports and mark each stage `ready`, `skipped`, or `blocked`. + d. Evaluate gate and hitl policy. Pause for user approval where required before continuing. +1. Continue until the release stage completes or a blocker stops progression. When invoking a worker stage, require this structured stage report at the end: @@ -97,7 +120,9 @@ When invoking a worker stage, require this structured stage report at the end: ## success criteria -- Stage order follows configured workflow. +- Dependency graph was evaluated before each stage transition. +- All ready stages ran before each gate boundary advanced. +- Independent branches ran in parallel where `depends_on` permitted. - Gate progression decisions are explicit and auditable. - User always understands current stage and next action. @@ -124,7 +149,9 @@ that requires changes to upstream items, flag it and trigger a reverse handoff. ## completion checklist -- Workflow stages were evaluated in declared order. +- Dependency graph was evaluated; stages ran only after all predecessors were complete. +- All ready stages were identified before advancing past each gate. +- Independent branches ran in parallel where `depends_on` permitted. - Each stage has a clear outcome (`ready`, `blocked`, or `skipped`). - User approval points were respected. - Final summary includes completed work and pending actions. @@ -135,4 +162,4 @@ that requires changes to upstream items, flag it and trigger a reverse handoff. - `@#analyse` - assess stage impact, skip rationale, and trade-offs - + diff --git a/.github/agents/product.agent.md b/.github/agents/product.agent.md index 9a713da6..4fd1fbbe 100644 --- a/.github/agents/product.agent.md +++ b/.github/agents/product.agent.md @@ -62,6 +62,14 @@ You are a **senior product manager** acting as the **product role**. You define - Prefer small, reviewable scope slices over broad ambiguous deliveries. - Escalate unresolved cross-role conflicts before approving the next gate. +## parallel delegation + +- If discovery naturally separates into independent tracks, you may split work across subagents or same-role variants. +- Good split candidates include vision, requirements, roadmap shaping, and release-scope analysis when they can be merged back into one acceptance story. +- Only split when the tracks are independent enough to avoid contradictory scope decisions. +- Do not split the final acceptance decision or any scope slice that requires a single integrated product judgment. +- Keep the merge point explicit so downstream roles receive one coherent baseline. + ## communication style - Be concise, explicit, and decision-oriented. @@ -75,6 +83,8 @@ You are a **senior product manager** acting as the **product role**. You define - **You (agent) = who/what/when** — decisions, scope, escalation, and handoffs within your role. - **Skills = how** — detailed procedures, checklists, and execution playbooks. - Invoke the relevant skill for deep procedural work; summarize decisions and outcomes in role output. +- **Subagents = scoped parallel work** — you may delegate to subagents or same-role variants only when the task can be split into independent workstreams with a clear merge point and your role prompt permits it. +- Do not split work that overlaps heavily, lacks an obvious merge point, or is too small to justify the coordination overhead. ## workflow and handoffs @@ -164,4 +174,4 @@ that requires changes to upstream items, flag it and trigger a reverse handoff. - `@#gh-issues` — create and manage GitHub Issues for requirements, tasks, and user stories - + diff --git a/.github/agents/release.agent.md b/.github/agents/release.agent.md index 836403d0..ddf3034d 100644 --- a/.github/agents/release.agent.md +++ b/.github/agents/release.agent.md @@ -62,6 +62,14 @@ You are a **senior platform and release engineer** acting as the **release role* - Treat contradictory evidence as a blocker until reconciled. - Prioritize auditability and deterministic release records. +## parallel delegation + +- If evidence gathering or sign-off collection can be separated safely, you may split it across subagents or same-role variants. +- Good split candidates include independent baseline checks, artifact validation, and role-perspective review collection when the findings can be merged before the final release decision. +- Only split when the outputs are independent and the final release gate still remains a single coherent decision. +- Do not split the release verdict itself or any activity that would create conflicting acceptance signals. +- Record the merge point explicitly so the release record stays deterministic and auditable. + ## communication style - Gate-oriented and explicit about pass/fail state. @@ -74,6 +82,8 @@ You are a **senior platform and release engineer** acting as the **release role* - **You (agent) = who/what/when** — decisions, scope, escalation, and handoffs within your role. - **Skills = how** — detailed procedures, checklists, and execution playbooks. - Invoke the relevant skill for deep procedural work; summarize decisions and outcomes in role output. +- **Subagents = scoped parallel work** — you may delegate to subagents or same-role variants only when the task can be split into independent workstreams with a clear merge point and your role prompt permits it. +- Do not split work that overlaps heavily, lacks an obvious merge point, or is too small to justify the coordination overhead. ## workflow and handoffs @@ -154,4 +164,4 @@ that requires changes to upstream items, flag it and trigger a reverse handoff. - `@#gh-issues` — create and manage GitHub Issues for tracking work and bug reports - + diff --git a/.github/agents/tester.agent.md b/.github/agents/tester.agent.md index aa3e888a..b28bbd65 100644 --- a/.github/agents/tester.agent.md +++ b/.github/agents/tester.agent.md @@ -62,6 +62,14 @@ You are a **senior QA, security, and reliability engineer** acting as the **test - Escalate immediately when required evidence cannot be produced. - Use explicit go/no-go language for release readiness. +## parallel delegation + +- If the verification scope spans independent dimensions, you may split the work into specialized subagents and run them in parallel. +- Good split candidates include security, performance, functional correctness, compatibility, and regression checks when those areas do not share critical setup or state. +- Only split when each subagent has a clearly bounded context and the results can be merged into one verdict. +- Do not split narrow or tightly coupled test scopes; the coordination overhead will outweigh the benefit. +- Make the subagent context explicit in the report so the merge step is reproducible. + ## communication style - Clear verdicts with severity and reproduction steps. @@ -74,6 +82,8 @@ You are a **senior QA, security, and reliability engineer** acting as the **test - **You (agent) = who/what/when** — decisions, scope, escalation, and handoffs within your role. - **Skills = how** — detailed procedures, checklists, and execution playbooks. - Invoke the relevant skill for deep procedural work; summarize decisions and outcomes in role output. +- **Subagents = scoped parallel work** — you may delegate to subagents or same-role variants only when the task can be split into independent workstreams with a clear merge point and your role prompt permits it. +- Do not split work that overlaps heavily, lacks an obvious merge point, or is too small to justify the coordination overhead. ## workflow and handoffs @@ -180,4 +190,4 @@ that requires changes to upstream items, flag it and trigger a reverse handoff. - `@#rancher` — Rancher/Fleet configuration and multi-cluster governance review - + diff --git a/.github/instructions/git.instructions.md b/.github/instructions/git.instructions.md index db6263af..814fed74 100644 --- a/.github/instructions/git.instructions.md +++ b/.github/instructions/git.instructions.md @@ -41,4 +41,4 @@ Use these Git and release hygiene conventions in this project. 1. Prefer local verification before pushing release-impacting changes. - + diff --git a/.github/instructions/helm.instructions.md b/.github/instructions/helm.instructions.md index f570c7de..b5ebd729 100644 --- a/.github/instructions/helm.instructions.md +++ b/.github/instructions/helm.instructions.md @@ -45,4 +45,4 @@ Use these Helm conventions in this project. - [Helm chart best practices](https://helm.sh/docs/chart_best_practices/) - + diff --git a/.github/instructions/java.instructions.md b/.github/instructions/java.instructions.md index 118782cf..831e4abb 100644 --- a/.github/instructions/java.instructions.md +++ b/.github/instructions/java.instructions.md @@ -56,4 +56,4 @@ Use these Java conventions in this project. 1. Do not suppress static analysis warnings without a documented, task-specific reason. - + diff --git a/.github/instructions/k8s.instructions.md b/.github/instructions/k8s.instructions.md index e69825b2..217bc0f4 100644 --- a/.github/instructions/k8s.instructions.md +++ b/.github/instructions/k8s.instructions.md @@ -51,4 +51,4 @@ Use these Kubernetes conventions in this project. - [Kubernetes API reference](https://kubernetes.io/docs/reference/kubernetes-api/) - + diff --git a/.github/instructions/markdown.instructions.md b/.github/instructions/markdown.instructions.md index 7cf32ad1..58785b84 100644 --- a/.github/instructions/markdown.instructions.md +++ b/.github/instructions/markdown.instructions.md @@ -51,4 +51,4 @@ Use these Markdown conventions in this project. 1. Keep examples accurate and runnable — a broken example is worse than no example. - + diff --git a/.github/instructions/python.instructions.md b/.github/instructions/python.instructions.md index 48db4d44..c2defbcc 100644 --- a/.github/instructions/python.instructions.md +++ b/.github/instructions/python.instructions.md @@ -42,4 +42,4 @@ Use these Python conventions in this project. 1. Do not silence lint/type errors unless there is a documented, task-specific reason. - + diff --git a/.github/instructions/rancher.instructions.md b/.github/instructions/rancher.instructions.md index 0457e71b..aa6420b1 100644 --- a/.github/instructions/rancher.instructions.md +++ b/.github/instructions/rancher.instructions.md @@ -44,4 +44,4 @@ Use these Rancher conventions in this project. - [Fleet docs](https://fleet.rancher.io/) - + diff --git a/.github/instructions/security.instructions.md b/.github/instructions/security.instructions.md index 8d5f82c9..75e4eec3 100644 --- a/.github/instructions/security.instructions.md +++ b/.github/instructions/security.instructions.md @@ -42,4 +42,4 @@ Apply these security policies in this project. 1. Isolate privileged logic; keep it minimal, auditable, and separate from business logic. - + diff --git a/.github/instructions/terraform.instructions.md b/.github/instructions/terraform.instructions.md index fb5e44f9..4a2aac4e 100644 --- a/.github/instructions/terraform.instructions.md +++ b/.github/instructions/terraform.instructions.md @@ -60,4 +60,4 @@ Use these Terraform conventions in this project. - [tfsec](https://aquasecurity.github.io/tfsec/) · [checkov](https://www.checkov.io/) - + diff --git a/.github/instructions/terragrunt.instructions.md b/.github/instructions/terragrunt.instructions.md index d184fc60..4f048b50 100644 --- a/.github/instructions/terragrunt.instructions.md +++ b/.github/instructions/terragrunt.instructions.md @@ -57,4 +57,4 @@ Use these Terragrunt conventions in this project. - [Terragrunt CLI reference](https://terragrunt.gruntwork.io/docs/reference/cli-options/) - + diff --git a/.github/instructions/testing.instructions.md b/.github/instructions/testing.instructions.md index c1ffbe2b..fee33639 100644 --- a/.github/instructions/testing.instructions.md +++ b/.github/instructions/testing.instructions.md @@ -43,4 +43,4 @@ Use these testing conventions in this project. 1. Treat flaky tests as bugs; do not merge code with known test reliability issues. - + diff --git a/.github/instructions/typescript.instructions.md b/.github/instructions/typescript.instructions.md index f1692d2e..dfca9b4b 100644 --- a/.github/instructions/typescript.instructions.md +++ b/.github/instructions/typescript.instructions.md @@ -49,4 +49,4 @@ Use these TypeScript conventions in this project. 1. Do not suppress lint or type errors with inline disable comments unless there is a documented, task-specific reason. - + diff --git a/.github/prompts/api-design-review.prompt.md b/.github/prompts/api-design-review.prompt.md index 22225756..ae5bb9df 100644 --- a/.github/prompts/api-design-review.prompt.md +++ b/.github/prompts/api-design-review.prompt.md @@ -55,4 +55,4 @@ List fields or objects that are missing required constraints, descriptions, or e - top priority fix in one sentence - + diff --git a/.github/prompts/architecture-risk.prompt.md b/.github/prompts/architecture-risk.prompt.md index 838adbbc..9f1c9156 100644 --- a/.github/prompts/architecture-risk.prompt.md +++ b/.github/prompts/architecture-risk.prompt.md @@ -52,4 +52,4 @@ List security-specific risks not covered above: auth boundaries, sensitive data - one-sentence rationale - + diff --git a/.github/prompts/code-review.prompt.md b/.github/prompts/code-review.prompt.md index 4c6ceec0..524bc914 100644 --- a/.github/prompts/code-review.prompt.md +++ b/.github/prompts/code-review.prompt.md @@ -51,4 +51,4 @@ End with: - Biggest remaining risk: one sentence - + diff --git a/.github/prompts/dependency-audit.prompt.md b/.github/prompts/dependency-audit.prompt.md index 33f9fc3b..0bcca348 100644 --- a/.github/prompts/dependency-audit.prompt.md +++ b/.github/prompts/dependency-audit.prompt.md @@ -60,4 +60,4 @@ List packages with unusual provenance concerns: abandoned maintainers, single-ma Ordered list of actions by priority (critical first). - + diff --git a/.github/prompts/incident-timeline.prompt.md b/.github/prompts/incident-timeline.prompt.md index d92f4ada..53f4ab5a 100644 --- a/.github/prompts/incident-timeline.prompt.md +++ b/.github/prompts/incident-timeline.prompt.md @@ -57,4 +57,4 @@ For each action: List the minimum controls needed to reduce repeat probability. - + diff --git a/.github/prompts/migration-safety.prompt.md b/.github/prompts/migration-safety.prompt.md index 514d14e5..522e0462 100644 --- a/.github/prompts/migration-safety.prompt.md +++ b/.github/prompts/migration-safety.prompt.md @@ -52,4 +52,4 @@ List missing migration tests (forward, backward, data invariants, load-sensitive - biggest remaining risk in one sentence - + diff --git a/.github/prompts/release-readiness.prompt.md b/.github/prompts/release-readiness.prompt.md index f99b15a3..4f3be2b1 100644 --- a/.github/prompts/release-readiness.prompt.md +++ b/.github/prompts/release-readiness.prompt.md @@ -46,4 +46,4 @@ For each expected artifact that is missing, flag it explicitly as: MISSING — [ One clear next step for the team. - + diff --git a/.github/skills/adr/SKILL.md b/.github/skills/adr/SKILL.md index 7c36e1b7..6cca82c4 100644 --- a/.github/skills/adr/SKILL.md +++ b/.github/skills/adr/SKILL.md @@ -180,4 +180,4 @@ is a kebab-case title. After writing, state the file path and summary so the architect or product role can review. - + diff --git a/.github/skills/analyse/SKILL.md b/.github/skills/analyse/SKILL.md index 69762c96..1d287fa8 100644 --- a/.github/skills/analyse/SKILL.md +++ b/.github/skills/analyse/SKILL.md @@ -214,4 +214,4 @@ State conclusions with confidence level: ``` - + diff --git a/.github/skills/architecture/SKILL.md b/.github/skills/architecture/SKILL.md index 58a1ead2..fdd0a1b8 100644 --- a/.github/skills/architecture/SKILL.md +++ b/.github/skills/architecture/SKILL.md @@ -278,4 +278,4 @@ For each significant structural decision made during this review (technology cho - Update `docs/architecture/overview.md` to reflect the final decisions. - + diff --git a/.github/skills/aws-cli/SKILL.md b/.github/skills/aws-cli/SKILL.md index c25b92d7..4e183038 100644 --- a/.github/skills/aws-cli/SKILL.md +++ b/.github/skills/aws-cli/SKILL.md @@ -374,4 +374,4 @@ aws ce get-cost-and-usage \ - [AWS CLI named profiles](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) - + diff --git a/.github/skills/cicd/SKILL.md b/.github/skills/cicd/SKILL.md index 81b957a3..a30bb66f 100644 --- a/.github/skills/cicd/SKILL.md +++ b/.github/skills/cicd/SKILL.md @@ -221,4 +221,4 @@ Configure these in GitHub → Settings → Branches. - [GitHub-hosted runners](https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners) - + diff --git a/.github/skills/cloudformation/SKILL.md b/.github/skills/cloudformation/SKILL.md index 0b735c72..ba455d79 100644 --- a/.github/skills/cloudformation/SKILL.md +++ b/.github/skills/cloudformation/SKILL.md @@ -344,4 +344,4 @@ AppSecurityGroup: - [AWS SAM documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/) - + diff --git a/.github/skills/code-review/SKILL.md b/.github/skills/code-review/SKILL.md index 60c3a844..13c71e6f 100644 --- a/.github/skills/code-review/SKILL.md +++ b/.github/skills/code-review/SKILL.md @@ -218,4 +218,4 @@ Confidence: [HIGH/MEDIUM/LOW — explain if not HIGH] ``` - + diff --git a/.github/skills/codeql/SKILL.md b/.github/skills/codeql/SKILL.md index ffd1863e..62ea71b5 100644 --- a/.github/skills/codeql/SKILL.md +++ b/.github/skills/codeql/SKILL.md @@ -251,4 +251,4 @@ GITHUB_TOKEN= codeql github upload-results \ - [Supported languages and frameworks](https://docs.github.com/en/code-security/code-scanning/introduction-to-code-scanning/codeql-code-scanning-for-compiled-languages) - + diff --git a/.github/skills/concise/SKILL.md b/.github/skills/concise/SKILL.md index 188e8b07..5dcf750e 100644 --- a/.github/skills/concise/SKILL.md +++ b/.github/skills/concise/SKILL.md @@ -147,4 +147,4 @@ Current mode unchanged: - [ ] User confirmation/status returned in deterministic format - + diff --git a/.github/skills/consult/SKILL.md b/.github/skills/consult/SKILL.md index 8a3a2a34..64648ce0 100644 --- a/.github/skills/consult/SKILL.md +++ b/.github/skills/consult/SKILL.md @@ -218,4 +218,4 @@ reason: [one sentence] ``` - + diff --git a/.github/skills/container/SKILL.md b/.github/skills/container/SKILL.md index ea38b368..756a3886 100644 --- a/.github/skills/container/SKILL.md +++ b/.github/skills/container/SKILL.md @@ -153,4 +153,4 @@ For production-like local testing, write a separate `docker-compose.prod.yml` wi - [Docker official images](https://hub.docker.com/search?image_filter=official) - + diff --git a/.github/skills/conventional-commit/SKILL.md b/.github/skills/conventional-commit/SKILL.md index d5dde6b3..dc3cd65f 100644 --- a/.github/skills/conventional-commit/SKILL.md +++ b/.github/skills/conventional-commit/SKILL.md @@ -155,4 +155,4 @@ Remaining changes: If commit is blocked, report exact reason and proposed fix. - + diff --git a/.github/skills/debug/SKILL.md b/.github/skills/debug/SKILL.md index 1f2d656e..c59aac07 100644 --- a/.github/skills/debug/SKILL.md +++ b/.github/skills/debug/SKILL.md @@ -258,4 +258,4 @@ Prevention: [any follow-up items] ``` - + diff --git a/.github/skills/dependabot/SKILL.md b/.github/skills/dependabot/SKILL.md index a269f4de..442de499 100644 --- a/.github/skills/dependabot/SKILL.md +++ b/.github/skills/dependabot/SKILL.md @@ -320,4 +320,4 @@ updates: - [Dependabot security updates](https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/about-dependabot-security-updates) - + diff --git a/.github/skills/dependency/SKILL.md b/.github/skills/dependency/SKILL.md index 26681ca3..77b33f6c 100644 --- a/.github/skills/dependency/SKILL.md +++ b/.github/skills/dependency/SKILL.md @@ -318,4 +318,4 @@ Action items (priority order): - [PyPI / npm / crates.io / Maven Central](https://pypi.org) (replace with the relevant registry) - + diff --git a/.github/skills/design/SKILL.md b/.github/skills/design/SKILL.md index c62109d2..1a56bcf6 100644 --- a/.github/skills/design/SKILL.md +++ b/.github/skills/design/SKILL.md @@ -243,4 +243,4 @@ Output a complete design document to `docs/design/overview.md` or `openapi.yaml` ``` - + diff --git a/.github/skills/docs/SKILL.md b/.github/skills/docs/SKILL.md index fa75166e..14940fc3 100644 --- a/.github/skills/docs/SKILL.md +++ b/.github/skills/docs/SKILL.md @@ -151,4 +151,4 @@ Skipped (n/a): ``` - + diff --git a/.github/skills/explore/SKILL.md b/.github/skills/explore/SKILL.md index b46d037d..980543d1 100644 --- a/.github/skills/explore/SKILL.md +++ b/.github/skills/explore/SKILL.md @@ -224,4 +224,4 @@ Stack: [language, framework, runtime versions] ``` - + diff --git a/.github/skills/gdpr/SKILL.md b/.github/skills/gdpr/SKILL.md index d1ae2c91..a81650e6 100644 --- a/.github/skills/gdpr/SKILL.md +++ b/.github/skills/gdpr/SKILL.md @@ -246,4 +246,4 @@ Use `@example.com` for all test email addresses. - [EDPB guidelines](https://www.edpb.europa.eu/our-work-tools/general-guidance/guidelines-recommendations-best-practices_en) - + diff --git a/.github/skills/gh-issues/SKILL.md b/.github/skills/gh-issues/SKILL.md index 692ec903..c6257a47 100644 --- a/.github/skills/gh-issues/SKILL.md +++ b/.github/skills/gh-issues/SKILL.md @@ -230,4 +230,4 @@ https://github.com///issues/ - [GitHub Issues documentation](https://docs.github.com/en/issues) - + diff --git a/.github/skills/gh-release/SKILL.md b/.github/skills/gh-release/SKILL.md index b8e7465b..43f58835 100644 --- a/.github/skills/gh-release/SKILL.md +++ b/.github/skills/gh-release/SKILL.md @@ -214,4 +214,4 @@ If blocked, report exact blocker and required user action. - [GitHub Releases documentation](https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases) - + diff --git a/.github/skills/guardrails/SKILL.md b/.github/skills/guardrails/SKILL.md index b5f1702d..57b6e706 100644 --- a/.github/skills/guardrails/SKILL.md +++ b/.github/skills/guardrails/SKILL.md @@ -72,4 +72,4 @@ Activate careful mode for this session. Two behaviors are now enabled. Explicitly ask to "disable guardrails". - + diff --git a/.github/skills/helm/SKILL.md b/.github/skills/helm/SKILL.md index 74ad155a..cfd00779 100644 --- a/.github/skills/helm/SKILL.md +++ b/.github/skills/helm/SKILL.md @@ -140,4 +140,4 @@ Practices: - [Chart best practices](https://helm.sh/docs/chart_best_practices/) - + diff --git a/.github/skills/incident/SKILL.md b/.github/skills/incident/SKILL.md index 4d88c90a..bdea20fc 100644 --- a/.github/skills/incident/SKILL.md +++ b/.github/skills/incident/SKILL.md @@ -251,4 +251,4 @@ Next: invoke @#rca and @#postmortem to produce written artifacts. ``` - + diff --git a/.github/skills/inspect/SKILL.md b/.github/skills/inspect/SKILL.md index 944d397e..c01344a9 100644 --- a/.github/skills/inspect/SKILL.md +++ b/.github/skills/inspect/SKILL.md @@ -155,4 +155,4 @@ Confirm for changed paths: ``` - + diff --git a/.github/skills/k8s/SKILL.md b/.github/skills/k8s/SKILL.md index 780b5af1..fc8357bc 100644 --- a/.github/skills/k8s/SKILL.md +++ b/.github/skills/k8s/SKILL.md @@ -144,4 +144,4 @@ Common failure classes: - [Kubernetes API reference](https://kubernetes.io/docs/reference/kubernetes-api/) - + diff --git a/.github/skills/migrate/SKILL.md b/.github/skills/migrate/SKILL.md index 93474c9b..707dd7f4 100644 --- a/.github/skills/migrate/SKILL.md +++ b/.github/skills/migrate/SKILL.md @@ -320,4 +320,4 @@ Pre-deploy checklist: ``` - + diff --git a/.github/skills/onboard/SKILL.md b/.github/skills/onboard/SKILL.md index 63dd1cd9..22088b3d 100644 --- a/.github/skills/onboard/SKILL.md +++ b/.github/skills/onboard/SKILL.md @@ -302,4 +302,4 @@ Gaps remaining (if any): ``` - + diff --git a/.github/skills/openapi/SKILL.md b/.github/skills/openapi/SKILL.md index 5ab15749..ad0f5af6 100644 --- a/.github/skills/openapi/SKILL.md +++ b/.github/skills/openapi/SKILL.md @@ -403,4 +403,4 @@ Summary: [N critical, N warnings, N info] - [Redocly CLI (linting)](https://redocly.com/docs/cli/) - + diff --git a/.github/skills/performance/SKILL.md b/.github/skills/performance/SKILL.md index 0211b1aa..65aad9a2 100644 --- a/.github/skills/performance/SKILL.md +++ b/.github/skills/performance/SKILL.md @@ -242,4 +242,4 @@ For each bottleneck identified: ``` - + diff --git a/.github/skills/postmortem/SKILL.md b/.github/skills/postmortem/SKILL.md index 2d90f74b..196ff0ae 100644 --- a/.github/skills/postmortem/SKILL.md +++ b/.github/skills/postmortem/SKILL.md @@ -184,4 +184,4 @@ Status: Draft — ready for team review ``` - + diff --git a/.github/skills/pr/SKILL.md b/.github/skills/pr/SKILL.md index 24a1b8ce..1baf28eb 100644 --- a/.github/skills/pr/SKILL.md +++ b/.github/skills/pr/SKILL.md @@ -151,4 +151,4 @@ Next steps depend on the repository CI/CD configuration: ``` - + diff --git a/.github/skills/rancher/SKILL.md b/.github/skills/rancher/SKILL.md index ed7f64a8..e91629ef 100644 --- a/.github/skills/rancher/SKILL.md +++ b/.github/skills/rancher/SKILL.md @@ -113,4 +113,4 @@ Checks: - [Fleet documentation](https://fleet.rancher.io/) - + diff --git a/.github/skills/rca/SKILL.md b/.github/skills/rca/SKILL.md index 8f91c154..11d3bef7 100644 --- a/.github/skills/rca/SKILL.md +++ b/.github/skills/rca/SKILL.md @@ -207,4 +207,4 @@ Status: Draft — ready for review ``` - + diff --git a/.github/skills/refactor/SKILL.md b/.github/skills/refactor/SKILL.md index da59d2a5..20c6563a 100644 --- a/.github/skills/refactor/SKILL.md +++ b/.github/skills/refactor/SKILL.md @@ -372,4 +372,4 @@ Behavior changed: No ``` - + diff --git a/.github/skills/release-notes/SKILL.md b/.github/skills/release-notes/SKILL.md index 079defd6..0d4a9f1b 100644 --- a/.github/skills/release-notes/SKILL.md +++ b/.github/skills/release-notes/SKILL.md @@ -147,4 +147,4 @@ Prepend a new entry at the top of `CHANGELOG.md`: Keep existing entries intact. - + diff --git a/.github/skills/requirements/SKILL.md b/.github/skills/requirements/SKILL.md index a9f7856e..e2b48f67 100644 --- a/.github/skills/requirements/SKILL.md +++ b/.github/skills/requirements/SKILL.md @@ -199,4 +199,4 @@ Write all findings to `docs/product/requirements.md`: After writing, summarize what was decided so the architect role can start. - + diff --git a/.github/skills/secret-scan/SKILL.md b/.github/skills/secret-scan/SKILL.md index 00dc2f9e..ccf9870d 100644 --- a/.github/skills/secret-scan/SKILL.md +++ b/.github/skills/secret-scan/SKILL.md @@ -240,4 +240,4 @@ credential formats. - [Supported secret patterns](https://docs.github.com/en/code-security/secret-scanning/introduction/supported-secret-scanning-patterns) - + diff --git a/.github/skills/security/SKILL.md b/.github/skills/security/SKILL.md index f8689152..139e3039 100644 --- a/.github/skills/security/SKILL.md +++ b/.github/skills/security/SKILL.md @@ -295,4 +295,4 @@ Scope: [full/diff/dependency/config] - [STRIDE threat modeling (Microsoft)](https://learn.microsoft.com/en-us/azure/security/develop/threat-modeling-tool-threats) - + diff --git a/.github/skills/terraform/SKILL.md b/.github/skills/terraform/SKILL.md index 0ec9dc4d..87cd0440 100644 --- a/.github/skills/terraform/SKILL.md +++ b/.github/skills/terraform/SKILL.md @@ -335,4 +335,4 @@ Run `terraform plan` after every state operation to verify the outcome. - [tfsec rules](https://aquasecurity.github.io/tfsec/latest/checks/aws/) · [checkov checks](https://www.checkov.io/5.Policy%20Index/terraform.html) - + diff --git a/.github/skills/terragrunt/SKILL.md b/.github/skills/terragrunt/SKILL.md index 26929fec..30adf6a8 100644 --- a/.github/skills/terragrunt/SKILL.md +++ b/.github/skills/terragrunt/SKILL.md @@ -307,4 +307,4 @@ Use `--terragrunt-non-interactive` in CI to prevent hanging on prompts. - [Gruntwork module registry](https://www.gruntwork.io/) - + diff --git a/.github/skills/threat-model/SKILL.md b/.github/skills/threat-model/SKILL.md index 33ff969a..7cf3a8b3 100644 --- a/.github/skills/threat-model/SKILL.md +++ b/.github/skills/threat-model/SKILL.md @@ -245,4 +245,4 @@ For each high-priority threat include: - Final report is written to `docs/architecture/threat-model.md`. - + diff --git a/.github/skills/verify/SKILL.md b/.github/skills/verify/SKILL.md index eb4dfcd9..8cc68a33 100644 --- a/.github/skills/verify/SKILL.md +++ b/.github/skills/verify/SKILL.md @@ -266,4 +266,4 @@ scope: [path/component/full] ``` - + diff --git a/.github/skills/vision/SKILL.md b/.github/skills/vision/SKILL.md index bd3370d0..dd60c8be 100644 --- a/.github/skills/vision/SKILL.md +++ b/.github/skills/vision/SKILL.md @@ -206,4 +206,4 @@ For each finding: explain the tradeoff, give an opinionated recommendation, ask Present as: "Overall assessment: [READY/NEEDS REVISION/SCOPE CHANGE] because [1-2 sentence reason]." - + diff --git a/.vstack/config.yaml b/.vstack/config.yaml index 483fa9e2..01c0ff42 100644 --- a/.vstack/config.yaml +++ b/.vstack/config.yaml @@ -121,6 +121,7 @@ workflow: - role: architect gate: required hitl: always + depends_on: [product] handoffs: prompt: > Architecture outputs are approved. Assess the current state and produce @@ -130,6 +131,7 @@ workflow: - role: designer gate: optional hitl: on-change + depends_on: [product] handoffs: prompt: > Design outputs are approved. Assess the current state and implement @@ -140,6 +142,7 @@ workflow: - role: engineer gate: required hitl: always + depends_on: [architect, designer] handoffs: prompt: > Implementation is approved. Assess the current state and verify the @@ -148,6 +151,7 @@ workflow: - role: tester gate: required hitl: always + depends_on: [engineer] handoffs: prompt: > Verification outputs are approved. Assess the current state and prepare @@ -156,5 +160,6 @@ workflow: - role: release gate: required hitl: always + depends_on: [tester] handoffs: prompt: "" diff --git a/.vstack/vstack.json b/.vstack/vstack.json index 674647f6..b578d167 100644 --- a/.vstack/vstack.json +++ b/.vstack/vstack.json @@ -1,316 +1,316 @@ { "manifest_version": 2, "hash_algorithm": "sha256", - "vstack_version": "3.1.1.post2.dev0+4d3419b", - "installed_at": "2026-05-11T20:59:07.400274+00:00", + "vstack_version": "3.2.0", + "installed_at": "2026-05-12T21:30:20.387171+00:00", "artifacts": { "skills": [ { "name": "adr", "file": "skills/adr/SKILL.md", "version": "20260421003", - "checksum": "1def4de23e0bb887392fdc79f5a354416ed6851b4d9584411d2cb04a280f2259", + "checksum": "3680cda5cb1cb4eb857e62ff04ca21dfc4f4054e4bfdf53708523e81264b1f7f", "checksum_algorithm": "sha256" }, { "name": "analyse", "file": "skills/analyse/SKILL.md", "version": "20260421004", - "checksum": "abfc4634c6a0123f95bf7a397e7c0334615c3c86da4cb02f18cd904fd33df917", + "checksum": "0f2081b3758d57a299cadf8d27b5174d649aeb0e047dd083136dfb52e862b2a2", "checksum_algorithm": "sha256" }, { "name": "architecture", "file": "skills/architecture/SKILL.md", "version": "20260421005", - "checksum": "381c9ba6e33f2334f96227336c3cadab0ccdc78cf256c43a579fe31fde2689a9", + "checksum": "eedad88826672a810c981210ab9bb6c2d2f319574512c571bdb07e5c630729c7", "checksum_algorithm": "sha256" }, { "name": "aws-cli", "file": "skills/aws-cli/SKILL.md", "version": "20260502033", - "checksum": "484f61c7e9686f8f45e6e0e470bec05e638269ce760e9f029e19aa65a282647e", + "checksum": "177cb3605d6477391ba141ff6aa1eea38531c73334e60948dc00fc2a04785310", "checksum_algorithm": "sha256" }, { "name": "cicd", "file": "skills/cicd/SKILL.md", "version": "20260421006", - "checksum": "c58754d85c160836e96820770be3d7c2d3959091032c3e0eb65fb9898886a515", + "checksum": "14e41222a89bb42ec06057be54d5faec679b1e692b45d63770deb4fece28f206", "checksum_algorithm": "sha256" }, { "name": "cloudformation", "file": "skills/cloudformation/SKILL.md", "version": "20260502032", - "checksum": "a7576e06214a0e549d216818417677feae79d29b26485ddd7124c40e0349c51b", + "checksum": "f232b22b00073b7a944534d0d6ce768f77e69a62043917c39ff231199fd9d703", "checksum_algorithm": "sha256" }, { "name": "code-review", "file": "skills/code-review/SKILL.md", "version": "20260421007", - "checksum": "48d6d8f5100875cd24540a8df8a3a8a1eccfcf353f8d649e9437d94ffa8374c0", + "checksum": "ab478e07d91f770b97308f19c6ce25529adafaca76838b1b1863b7bac7a53680", "checksum_algorithm": "sha256" }, { "name": "codeql", "file": "skills/codeql/SKILL.md", "version": "20260502026", - "checksum": "87594771b5e46fd7a43d1a3d50528511358bc4094f336027ca5f00cdd0e13059", + "checksum": "139876ae155333377891df2cb33254b1993208220c827382ae97f3eade479320", "checksum_algorithm": "sha256" }, { "name": "concise", "file": "skills/concise/SKILL.md", "version": "20260421008", - "checksum": "2f92b948f975a5d418d0ec8204a55faf20a5f2576dee426d7f96e954501aa7ff", + "checksum": "8d7916afe8f95090c53b9d80e9c2b75ed33b12a033b5d29435ab40d622df4488", "checksum_algorithm": "sha256" }, { "name": "consult", "file": "skills/consult/SKILL.md", "version": "20260421009", - "checksum": "b760c5df32f6922dfc96d9ed7ed73715ac10b58d1570879476f9cf75c2a2bbd7", + "checksum": "3d45d5b65cb69dd7d92ae0867f36cdb8a94ff066dc2a9255a642591f055293e5", "checksum_algorithm": "sha256" }, { "name": "container", "file": "skills/container/SKILL.md", "version": "20260421010", - "checksum": "9493acae27f345fe49553db6aa91b24a4575c4c31525989a79e3e70bcbfd9dff", + "checksum": "62184fc600a92a01154a6981b9bf381ce728104df040c38a536e0d26d99f30da", "checksum_algorithm": "sha256" }, { "name": "conventional-commit", "file": "skills/conventional-commit/SKILL.md", "version": "20260502024", - "checksum": "401ea58fe12834d7786ce25eb142886020516eea2af8bf29c2a1cabcacb8e805", + "checksum": "876c9b5b0a681a59371ab65e472bd83769cd9238b7ae114bfa45739390b6b613", "checksum_algorithm": "sha256" }, { "name": "debug", "file": "skills/debug/SKILL.md", "version": "20260421011", - "checksum": "3b62b8faf8c7c6316c915cff3bac03d363c0347b8ab6fbfe07cb23d0b266d58f", + "checksum": "6143f82cc71307033de52ec46b212596cc941e113c9dede2e7a83667b3ce4141", "checksum_algorithm": "sha256" }, { "name": "dependabot", "file": "skills/dependabot/SKILL.md", "version": "20260502027", - "checksum": "509d421bae968bd6ee4364108a5cdb38bc4db3e931007db9b8f9faac218d1877", + "checksum": "91481833dd17ef38cbfc86774d5dd166451ce2ac8761f54227c14f0cd1c4d94a", "checksum_algorithm": "sha256" }, { "name": "dependency", "file": "skills/dependency/SKILL.md", "version": "20260421012", - "checksum": "b70c7b3005052ae09d95763a17d1116f1353c5bdfbcef92f200822724123fc71", + "checksum": "770b6e736b7a905fd5e520a871c21587c3fa7efba9b844fc6fb736a14fefdfe2", "checksum_algorithm": "sha256" }, { "name": "design", "file": "skills/design/SKILL.md", "version": "20260421013", - "checksum": "26d0130b03dd7710462763d015559d4df898682236a7619df999a3e338a90efa", + "checksum": "004f9da848d3a8dd57511e694b5f74de005fcce95d38bb6c3bbb756012fa56b0", "checksum_algorithm": "sha256" }, { "name": "docs", "file": "skills/docs/SKILL.md", "version": "20260421014", - "checksum": "7c13ddd443a33a854ce7f02fba9e50d6bfb60ecdd09e58edea4580d821d64cc6", + "checksum": "b84c7d06cae49c8e39e7e4db515224cedc9320058c9705dff4d31908c7f2fcb8", "checksum_algorithm": "sha256" }, { "name": "explore", "file": "skills/explore/SKILL.md", "version": "20260421015", - "checksum": "699c3a1db16163e887c3e377f9618de8341cf77997bae6df4ad95f260be229ba", + "checksum": "a9042886c58d3b0e17f78b9bfd1cc89116de89bf6ee2c011f041af69193640e2", "checksum_algorithm": "sha256" }, { "name": "gdpr", "file": "skills/gdpr/SKILL.md", "version": "20260502029", - "checksum": "796687e2877fb9af481fe4935ef185e93a17b179aa435bc696924bf91a9082d2", + "checksum": "4652e7ed56b66f57ba0c0229db7925d814a7a422bb7a582b376d57fff23895b4", "checksum_algorithm": "sha256" }, { "name": "gh-issues", "file": "skills/gh-issues/SKILL.md", "version": "20260502025", - "checksum": "69e9f37144f418819028387f9769581b270c8797992df8fc09cf32db272249f6", + "checksum": "5efb56c6fb5dab6465d1dacdbb3d2101a954bac2493cd21a5b88ea64a82332d0", "checksum_algorithm": "sha256" }, { "name": "gh-release", "file": "skills/gh-release/SKILL.md", "version": "20260502023", - "checksum": "2c47e28a986239626a252d6d435e7a13d8a1d39284f14f7965336adee1ad7f00", + "checksum": "7f07970df0dc6f3f09d1322c29e53e2f4e30db2abdfacc04552a1bf3b5bdfe8d", "checksum_algorithm": "sha256" }, { "name": "guardrails", "file": "skills/guardrails/SKILL.md", "version": "20260421016", - "checksum": "49bda4a6d23dbd3e272e034baa85daea3adbac226782a3921f0716b05c98cf0e", + "checksum": "ad26f458c7155ab4e540f4aa4bc49915c152adde51e762de9b87fad65d0da33b", "checksum_algorithm": "sha256" }, { "name": "helm", "file": "skills/helm/SKILL.md", "version": "20260502037", - "checksum": "bd9057d8af61b4f1dacc637bfff80475a1ab594080dd88535f6d3f2c4a8258b5", + "checksum": "d24368f0780d8e87e6183a47adab7ffab6b662b23582e4154f1e21703bb6841e", "checksum_algorithm": "sha256" }, { "name": "incident", "file": "skills/incident/SKILL.md", "version": "20260503002", - "checksum": "891fb9afe19b86010cbb8a810b47bd148c0f0df531d302e2b6d1ba91ae67d747", + "checksum": "22848f73937ca593d26297d6d7d6be5cba89a25e05d4c845d79e1eb504086d21", "checksum_algorithm": "sha256" }, { "name": "inspect", "file": "skills/inspect/SKILL.md", "version": "20260421018", - "checksum": "e4fd6f8cbc837ac052cffa487f9cce14d8c0990e3f6285c6913ce44244dca6a8", + "checksum": "ab252c148126029890475f7d7a6b39dea885590096a0fc973a240e273694d02b", "checksum_algorithm": "sha256" }, { "name": "k8s", "file": "skills/k8s/SKILL.md", "version": "20260502036", - "checksum": "f5319450a41d97e90a85f7eabd4fcabacef8288664fb5d5c884addc4892676a9", + "checksum": "4054d20ebe77f5e0e8ad269f3270f799caa4a92fc918ebb01c954ed816a73d47", "checksum_algorithm": "sha256" }, { "name": "migrate", "file": "skills/migrate/SKILL.md", "version": "20260421019", - "checksum": "f29f99ec9a5d324bb2d1628b8add0fac57cda66471633a1c23c66a46fdee02c6", + "checksum": "c2966f6b626ab20aa3919a22c2aee5a677afbd0e0b9b35278367b49f1369026e", "checksum_algorithm": "sha256" }, { "name": "onboard", "file": "skills/onboard/SKILL.md", "version": "20260421020", - "checksum": "b2c4ac2679a31515d0ea6a1161076aeea19ef5b41f058f93471e077d098e9412", + "checksum": "e58531d74a54654dd1098cce9f9bad4220c3918b9330cfe8c903d026625c40bf", "checksum_algorithm": "sha256" }, { "name": "openapi", "file": "skills/openapi/SKILL.md", "version": "20260421021", - "checksum": "e7fda43ee964e2e31efd7f82dd7b11a28679fbe9f20b9a8faf6975719e518a68", + "checksum": "00dd6560b086bfa8506adad89b9fade5c3774b4bb4fbcb3082c2c74db820dc98", "checksum_algorithm": "sha256" }, { "name": "performance", "file": "skills/performance/SKILL.md", "version": "20260421022", - "checksum": "9c27e5eda062cbd881d2073f0de4dac41b971adb024f1e1e0f28dd44393e8784", + "checksum": "f83f4da23e96849a9407020a00ab10b159724c3c38a5a503ed1702211b2f19d0", "checksum_algorithm": "sha256" }, { "name": "postmortem", "file": "skills/postmortem/SKILL.md", "version": "20260503001", - "checksum": "9197bed5f5bebb64988c639e4feb5ed264c72738d176875a0ce5d063b1e1d3aa", + "checksum": "3cf943671659e19ac726987e49883cc1a934713b5c17454c87aaa23c755d4194", "checksum_algorithm": "sha256" }, { "name": "pr", "file": "skills/pr/SKILL.md", "version": "20260502013", - "checksum": "9bcb8180cc74e8fa956818f3b5d27112fd73c289d966e8c2bb0a53109b9274ed", + "checksum": "13cd06ee9621eec04e14cc7ec6e42e92c4bc309f9a7ede271e42c4d1f1379733", "checksum_algorithm": "sha256" }, { "name": "rancher", "file": "skills/rancher/SKILL.md", "version": "20260502038", - "checksum": "26c67ca9a32e778f4b574fab91b33d00b425bc9a7ceb7177fb1558ebe1222951", + "checksum": "9d1ede54f900248f2d7f29433960c929cc0e194f40fb463e8abdab4aa4612b1c", "checksum_algorithm": "sha256" }, { "name": "rca", "file": "skills/rca/SKILL.md", "version": "20260503001", - "checksum": "f7e6a0fc4950136bf0165ba1051685c5b297779ea97aa76f1ca0631f593401b1", + "checksum": "4a24b71f315a39721c5e8d9355bd6397db2cb38b4b1804b37c4f948d9fe5655b", "checksum_algorithm": "sha256" }, { "name": "refactor", "file": "skills/refactor/SKILL.md", "version": "20260421023", - "checksum": "69b28c4a57134f29c6c59366b06ea7cf3468cc0db219d120567657abb0765801", + "checksum": "0185a7be9ad06bab25fa5a249b490d1a4a8144c93fc6ae09bd596259c81f657f", "checksum_algorithm": "sha256" }, { "name": "release-notes", "file": "skills/release-notes/SKILL.md", "version": "20260502014", - "checksum": "9b11a8bfc973ce1faf139cd90190e187955ee2334f7694049e39922f2e5d71c7", + "checksum": "071535ca88414f7f295d11d3d156bea31327aaa97499dcad74d411a2059a20d6", "checksum_algorithm": "sha256" }, { "name": "requirements", "file": "skills/requirements/SKILL.md", "version": "20260421024", - "checksum": "0a741ec2a7a899affef84a613d7a97d501feaf14e44355f3633aa199375c7afe", + "checksum": "d0ca7be9b2d14ef25b18b4e6363f8168abc646671e765686b544c870ad7fac68", "checksum_algorithm": "sha256" }, { "name": "secret-scan", "file": "skills/secret-scan/SKILL.md", "version": "20260502028", - "checksum": "7ee0aadca4d6dac2d8c301ab51105a11587275ef5ed2700d50b786380040d29f", + "checksum": "78776b0284d4135598107f7e3c0514abb5574a0e8a2925d637cfab1105a8e04e", "checksum_algorithm": "sha256" }, { "name": "security", "file": "skills/security/SKILL.md", "version": "20260421025", - "checksum": "ea7fc8189f6187a2dfa99114d1463d31b0afe147c0853406d80f90e15da5fd3c", + "checksum": "f053730a9dae9024cd2562a576a58691c452845bc9fa7aa5f09491ceda463ff8", "checksum_algorithm": "sha256" }, { "name": "terraform", "file": "skills/terraform/SKILL.md", "version": "20260502030", - "checksum": "6666debeef5f59910a0374c811744f3d29c7695a59ee141935c832bcc020c069", + "checksum": "2951cc833ce8d6a3bb0cbc06d1428d12671a0a9144fb2ad35af9d848ebb3947a", "checksum_algorithm": "sha256" }, { "name": "terragrunt", "file": "skills/terragrunt/SKILL.md", "version": "20260502031", - "checksum": "518577f40465f9ef94b90244deb3886206179c32ea533548267fe0b9ebda7190", + "checksum": "acdd65b959bc69554f84f4174c061ed687c74001583a6820359313b554bff5cf", "checksum_algorithm": "sha256" }, { "name": "threat-model", "file": "skills/threat-model/SKILL.md", "version": "20260502021", - "checksum": "a5d4d0f78a020b259337c446ad2a4abed37da7e6cfd70f9bbf3733f9e06f40f5", + "checksum": "2896a157595ca01ee63fdff007b675229851c5b6d10ee7f6232d6e9c4fdbd2c3", "checksum_algorithm": "sha256" }, { "name": "verify", "file": "skills/verify/SKILL.md", "version": "20260421026", - "checksum": "c242872c3542a4aaa54d1ce062e0432df6edde0a8678ac953e4729b5a93fb144", + "checksum": "351d314924337a41d80e16fdc5f6958be070873f756d9c9d2ab845aabc0aa14f", "checksum_algorithm": "sha256" }, { "name": "vision", "file": "skills/vision/SKILL.md", "version": "20260421027", - "checksum": "9dce0d40a2d80e8b5f521b0f36761667acbaf9b9a8e53f68f00118d27f8f90a4", + "checksum": "a3aaeb6e8b900e5fe62f118fdbd94a68aee894c82872e0e855788efb05f5e6bf", "checksum_algorithm": "sha256" } ], @@ -319,49 +319,49 @@ "name": "architect", "file": "agents/architect.agent.md", "version": "20260503022", - "checksum": "e097b021cee7e3a7bab7d75512a372b5aee073fd3d01ad28c4a7c582b569eebc", + "checksum": "50bd0a014734ce1b687897918e193587274902d6a3b3c721f71428b881df5568", "checksum_algorithm": "sha256" }, { "name": "designer", "file": "agents/designer.agent.md", "version": "20260503024", - "checksum": "b7240cc02e1da79f962384ed5ac02ac769211f85c8f5976b8e8251168eb9deb7", + "checksum": "820a907a8aa2f28b0a6fc82c4fcf32b8208b8365d4c05884682de9ebf5c240c6", "checksum_algorithm": "sha256" }, { "name": "engineer", "file": "agents/engineer.agent.md", "version": "20260503024", - "checksum": "91ce873180353b58f41873a8ec2e4a448cc9f08871b81bc785fd182e0e1d1cde", + "checksum": "580c52364faf44be4ff03a537adb8d67ba3b0c4a75858e10bc9978f006c5466f", "checksum_algorithm": "sha256" }, { "name": "planner", "file": "agents/planner.agent.md", "version": "20260510001", - "checksum": "18402e3cd9653e38943f23cb391147ede02b659b76847856ad678668c89b9f39", + "checksum": "5ac8d17ab4d45ed1fdac092c0ddc268e2a9e6018fe7da7c9395bbf39a271c056", "checksum_algorithm": "sha256" }, { "name": "product", "file": "agents/product.agent.md", "version": "20260503021", - "checksum": "1bf2088c0c1d2750ede7e9c87d6e10dc4016db7f2aacce217be37ec8f4bc58b9", + "checksum": "f2b6daf2408b2849d25d1da272228f91e5594068f0644b34847a9426ada554f8", "checksum_algorithm": "sha256" }, { "name": "release", "file": "agents/release.agent.md", "version": "20260503020", - "checksum": "823cdefe1ee2aa3b4700da629a83f1efee0c8503cf0d214926d489a5b85720ea", + "checksum": "91c5532268cdb68c5e09567a2a2a9a2c996f89fcd0ba00685000a04c49db40ce", "checksum_algorithm": "sha256" }, { "name": "tester", "file": "agents/tester.agent.md", "version": "20260503026", - "checksum": "2ab7d38e1bc24d530aaa784e8852ddd9f2c68c6d163466d6d608711062403197", + "checksum": "ae2bad718957221b1193575b456b23234928a787eb5a31daf77cb33fb630edf2", "checksum_algorithm": "sha256" } ], @@ -414,84 +414,84 @@ "name": "git", "file": "instructions/git.instructions.md", "version": "20260421001", - "checksum": "b0b866596988dab9e3b48b6cf1cb8de82b46cddfdcfe57214ee85308c2cf0e13", + "checksum": "2dc5aec79789109f7f0e188aaae1f769aea32c1cb968d8d961b3d8a116bf0dc6", "checksum_algorithm": "sha256" }, { "name": "helm", "file": "instructions/helm.instructions.md", "version": "20260502040", - "checksum": "b1b5d2e535cf6a30fa86121c3e58081e77149242a16dabdb153291e4349f3015", + "checksum": "21d70e900c1bece55d4eb03330715fc422c3a53bfcf18a92bacbeebbc6ef1acd", "checksum_algorithm": "sha256" }, { "name": "java", "file": "instructions/java.instructions.md", "version": "20260502001", - "checksum": "663847977d195426234282780c53660fad2d5b06767e2848ec1afbef379e699a", + "checksum": "5dded5e2cdaa9f49c64dc6c73a738ed2ba7ab1e4abdb766efbac6041a122f7d2", "checksum_algorithm": "sha256" }, { "name": "k8s", "file": "instructions/k8s.instructions.md", "version": "20260502039", - "checksum": "95a7d12d5518eab2d91a20f5fe84e6f66af52dfb21b8ecb256d6b050144d8f03", + "checksum": "c88684119cb50cff4d7e4212dce99eacd6999ca0f274cf8b4465b871c03cefed", "checksum_algorithm": "sha256" }, { "name": "markdown", "file": "instructions/markdown.instructions.md", "version": "20260502002", - "checksum": "901ba40d993be4477057626039a4f4c6c0c6a49561429af5cb4cac016ebe3be2", + "checksum": "aa7705c69a42101ccb17f2250454d2d81a9302539d5b35a32dc45e449df42337", "checksum_algorithm": "sha256" }, { "name": "python", "file": "instructions/python.instructions.md", "version": "20260421002", - "checksum": "346e468cbf8249a8be1ae8881e1ac849ca0646e07ce044dc8e0e5947357293b6", + "checksum": "0b0294a49923cfccdadc3cc19ec8e4942569f45a5a70f26c0560cfdf7ef42698", "checksum_algorithm": "sha256" }, { "name": "rancher", "file": "instructions/rancher.instructions.md", "version": "20260502041", - "checksum": "b0ea1821e3deac543599a2b838cb50d73cc663cffe95aecf7455279721cab294", + "checksum": "5515a9ddc993b31bb036da3c1c6d425db2000cd6b92c43ab6691cc91d314a64f", "checksum_algorithm": "sha256" }, { "name": "security", "file": "instructions/security.instructions.md", "version": "20260502003", - "checksum": "a247eb547961349f52032152ed4ff1b9d77d6c268db8251d803a87ab31dd0ca5", + "checksum": "da1e5363a4ef104bdac48bd117b3f391e1039bbaf711c2225b9d5628cd7c2c71", "checksum_algorithm": "sha256" }, { "name": "terraform", "file": "instructions/terraform.instructions.md", "version": "20260502034", - "checksum": "f0ac9c36bba8053351b1993418b4ae16f2db49b2bddf69e62ee1638f2a649908", + "checksum": "2a89d46487d316870c5ffb08a381e714c07d04651140ac84261b587ae14a036f", "checksum_algorithm": "sha256" }, { "name": "terragrunt", "file": "instructions/terragrunt.instructions.md", "version": "20260502035", - "checksum": "37bb85ec3f2ab1c1bcaedd05da7f4df15e28a0ee32f74307ed53ad4733c028a5", + "checksum": "4c778e2e6d003cd5b9e13023fcc19df7a251668415dbc2076990225da4a02660", "checksum_algorithm": "sha256" }, { "name": "testing", "file": "instructions/testing.instructions.md", "version": "20260502004", - "checksum": "11dd196e151c37d59585b77e7e95dddd2c395cf38b210ed2a9b88c83ccd0d756", + "checksum": "8a25627464ab7f5086b3600050d4977d39a4aecd4247c6e4e48ea1dd0f069441", "checksum_algorithm": "sha256" }, { "name": "typescript", "file": "instructions/typescript.instructions.md", "version": "20260502005", - "checksum": "8fa6c892d0556be68e324e005bbb681024b7d41dcd89ba26c70874998d1d78bd", + "checksum": "96b749496ac328ccb66786aef21ac0b585a46ddb80196aa2d9575e328a811c97", "checksum_algorithm": "sha256" } ], @@ -500,49 +500,49 @@ "name": "api-design-review", "file": "prompts/api-design-review.prompt.md", "version": "20260502006", - "checksum": "1cc8cc182c052dd7e5d5c253a9549609f243a184b71d74aa6c092b9270692488", + "checksum": "f616ae468527ce32afe1be951fb186ee95ae52450a7ab4fcca556c4e1f467eed", "checksum_algorithm": "sha256" }, { "name": "architecture-risk", "file": "prompts/architecture-risk.prompt.md", "version": "20260502007", - "checksum": "4bc2830e1914c2a13cf849a8d67bc3e18355b4a1c24f001feaf88876c7131ba4", + "checksum": "6e47a741bcf48a7f4c645b827dbe8ec3356c4d78ecafc284b9f1c51a832fdea1", "checksum_algorithm": "sha256" }, { "name": "code-review", "file": "prompts/code-review.prompt.md", "version": "20260502008", - "checksum": "02d98a41849a1cbb92c642a917850d9d689e3a4713dbb8b4e4ae5b22fe0fa801", + "checksum": "b3a537fb098588a2920929954820e09a0830104e89131eda479fe1c82c49d824", "checksum_algorithm": "sha256" }, { "name": "dependency-audit", "file": "prompts/dependency-audit.prompt.md", "version": "20260502009", - "checksum": "22c5d3f7f8ead6d750bf89fe1aa61c4a458fa7de3e78f3956cf363222965c8ac", + "checksum": "86f5c563746148be8a2c0be4d008af3dac22c6450c8b05954c3ef198d1e943a9", "checksum_algorithm": "sha256" }, { "name": "incident-timeline", "file": "prompts/incident-timeline.prompt.md", "version": "20260502010", - "checksum": "45729a09df502e63d0542a03690e9557ba6eb1f88bb50d4898233e749c179b58", + "checksum": "2bb3473cd31cb38022307a0f3c44d48b381f06549cf6b6b40ff78b907e347dee", "checksum_algorithm": "sha256" }, { "name": "migration-safety", "file": "prompts/migration-safety.prompt.md", "version": "20260502011", - "checksum": "71f9f7ec5f1d714cd5d80b0582544a7bcc84a26cf5e3325b0846c9879a33fe14", + "checksum": "46544d76acbcca10e82591c97589a816b479367bbad8ec89e8b7a52c34ff292c", "checksum_algorithm": "sha256" }, { "name": "release-readiness", "file": "prompts/release-readiness.prompt.md", "version": "20260502012", - "checksum": "ee729edd2e79b1c44ced2ca085422c0e3b170201c3dae6a67d14a6b1c861f87a", + "checksum": "f036668d9b73f709cb069f39f27b99c9a332aa6fed3e7ebf9731d56e92afbb14", "checksum_algorithm": "sha256" } ] diff --git a/CHANGELOG.md b/CHANGELOG.md index d1fce9da..124d66aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,28 +4,25 @@ ## [3.2.0](https://github.com/eschaar/vstack/compare/3.1.1...3.2.0) (2026-05-12) - ### Features -* **config:** migrate items keys and stabilize workflow defaults ([0b77f68](https://github.com/eschaar/vstack/commit/0b77f68ff4940b9be44b9c901dceb83eb3bc154c)) -* **hooks:** add configurable baseline ([951c9c1](https://github.com/eschaar/vstack/commit/951c9c12e5d3afcb1ddca34a1c01f4c98297ce48)) -* **hooks:** add hooks ADR, design, and multi-template baseline ([2d46ac2](https://github.com/eschaar/vstack/commit/2d46ac21fed9b623915050356703d0336a89470f)) - +- **config:** migrate items keys and stabilize workflow defaults ([0b77f68](https://github.com/eschaar/vstack/commit/0b77f68ff4940b9be44b9c901dceb83eb3bc154c)) +- **hooks:** add configurable baseline ([951c9c1](https://github.com/eschaar/vstack/commit/951c9c12e5d3afcb1ddca34a1c01f4c98297ce48)) +- **hooks:** add hooks ADR, design, and multi-template baseline ([2d46ac2](https://github.com/eschaar/vstack/commit/2d46ac21fed9b623915050356703d0336a89470f)) ### Fixes -* **hooks:** decouple retention and harden ci automation ([375698f](https://github.com/eschaar/vstack/commit/375698fc2f658bd8f88c4a96b60b790d8b52cc55)) - +- **hooks:** decouple retention and harden ci automation ([375698f](https://github.com/eschaar/vstack/commit/375698fc2f658bd8f88c4a96b60b790d8b52cc55)) ### Maintenance -* **artifacts:** regenerate installed github artifacts ([f5c44dd](https://github.com/eschaar/vstack/commit/f5c44dd9578f5942adf64fd4753df8c11f5b3b72)) -* **deps:** bump mypy and types-pyyaml ([2f2e459](https://github.com/eschaar/vstack/commit/2f2e459c538df3bfcb12218dbe859708dbc5498e)) -* **deps:** refresh dependency and action baselines ([2e5f0c0](https://github.com/eschaar/vstack/commit/2e5f0c046d3f52d17c01e5d13413470693470b6c)) -* **deps:** refresh poetry lockfile ([e182195](https://github.com/eschaar/vstack/commit/e1821957e88787a289effafaf18b7946205ccc32)) -* **gitignore:** drop legacy tmp-test-home ignore ([b031408](https://github.com/eschaar/vstack/commit/b03140808569e221cdd69325b801aa97cfcf7fb5)) -* **gitignore:** ignore local tmp test home ([4ef0cdf](https://github.com/eschaar/vstack/commit/4ef0cdf6e909a72889a1c1463faf6154ec6b2c2d)) -* regenerate generated artifacts ([aa206ba](https://github.com/eschaar/vstack/commit/aa206ba1ead62e34b72f523971fe08f51ffe3b1e)) +- **artifacts:** regenerate installed github artifacts ([f5c44dd](https://github.com/eschaar/vstack/commit/f5c44dd9578f5942adf64fd4753df8c11f5b3b72)) +- **deps:** bump mypy and types-pyyaml ([2f2e459](https://github.com/eschaar/vstack/commit/2f2e459c538df3bfcb12218dbe859708dbc5498e)) +- **deps:** refresh dependency and action baselines ([2e5f0c0](https://github.com/eschaar/vstack/commit/2e5f0c046d3f52d17c01e5d13413470693470b6c)) +- **deps:** refresh poetry lockfile ([e182195](https://github.com/eschaar/vstack/commit/e1821957e88787a289effafaf18b7946205ccc32)) +- **gitignore:** drop legacy tmp-test-home ignore ([b031408](https://github.com/eschaar/vstack/commit/b03140808569e221cdd69325b801aa97cfcf7fb5)) +- **gitignore:** ignore local tmp test home ([4ef0cdf](https://github.com/eschaar/vstack/commit/4ef0cdf6e909a72889a1c1463faf6154ec6b2c2d)) +- regenerate generated artifacts ([aa206ba](https://github.com/eschaar/vstack/commit/aa206ba1ead62e34b72f523971fe08f51ffe3b1e)) ## [3.1.1](https://github.com/eschaar/vstack/compare/3.1.0...3.1.1) (2026-05-10) diff --git a/README-pypi.md b/README-pypi.md index 298bd190..4623256c 100644 --- a/README-pypi.md +++ b/README-pypi.md @@ -230,7 +230,41 @@ Execution semantics: - `workflow.stages` order is the canonical progression order. - `agentic` is stage-sequential by default: planner advances one stage at a time in configured order. -- Parallelization is still possible inside a stage (independent subtasks), but cross-stage progression remains ordered. +- Set `depends_on` to unlock a DAG topology and let planner run independent branches in parallel. + +**Parallel stages with `depends_on`:** By default each stage implicitly depends on the previous one. Add `depends_on` to declare explicit predecessors. The canonical vstack DAG (seeded automatically by `vstack install`): + +```yaml +workflow: + mode: agentic + version: 1 + stages: + - role: product + gate: required + hitl: always + - role: architect + gate: required + hitl: always + depends_on: [product] + - role: designer + gate: optional + hitl: on-change + depends_on: [product] # runs in parallel with architect + - role: engineer + gate: required + hitl: always + depends_on: [architect, designer] # waits for both + - role: tester + gate: required + hitl: always + depends_on: [engineer] + - role: release + gate: required + hitl: always + depends_on: [tester] +``` + +`depends_on: []` marks a root stage. Absent `depends_on` falls back to sequential. Circular dependencies are caught at install/init time. Handoff target semantics: @@ -241,12 +275,37 @@ Handoff target semantics: Mode quickstart in Copilot Agent Mode: +In `agentic` mode, `@planner` is the primary entry point. Start every session with `@planner` and let it drive all stage transitions automatically. + | Mode | Start here | First prompt example | | --------- | ------------------------ | ------------------------------------------------------- | | `agentic` | `@planner` | `@planner Run the workflow for this repository change.` | | `manual` | `@product` | `@product Define requirements for this change.` | | `hybrid` | `@planner` or `@product` | `@planner Run the workflow for this repository change.` | +What planner does: reads `workflow.stages` and `depends_on`, invokes each role agent as a subagent at the right time, runs independent branches in parallel when `depends_on` permits, pauses at each gate for human approval, and reports a structured stage outcome after each step. Valid role names: `product`, `architect`, `designer`, `engineer`, `tester`, `release`. + +Agentic runbook (copy/paste): + +```text +@planner Run the workflow for this repository change. +@planner Show current stage status, ready stages, blocked stages, and next action. +@planner Continue with all ready stages in parallel where workflow.depends_on allows it. +@planner Pause at required HITL gates and ask for approval before advancing. +@planner Finalize with a release-readiness summary and list changed artifacts. +``` + +Troubleshooting: why planner is not running stages in parallel + +- Check `workflow.mode` in `.vstack/config.yaml`: parallel orchestration requires `agentic` (or planner-led `hybrid`). +- Validate stage dependencies: run `vstack validate` to catch invalid roles, self-dependencies, and cycles. +- Inspect `depends_on` shape: a stage runs only when all listed predecessors are `ready` or `skipped`. +- Check implicit sequential fallback: if `depends_on` is omitted, the stage depends on the previous stage. +- Check optional stage behavior: `gate: optional` can be skipped when unaffected, reducing apparent parallel fan-out. +- Check blockers in stage report: any `blocked` predecessor prevents dependent stages from becoming ready. +- Check mixed execution path: in `hybrid`, mixing manual handoffs and planner in one session can mask parallel readiness. +- Confirm you started with planner: in `agentic`, begin with `@planner`, not a worker role agent. + Usage guidance: - Use `agentic` when you want one deterministic orchestration path. diff --git a/README.md b/README.md index 0551f907..9ca283b4 100644 --- a/README.md +++ b/README.md @@ -573,7 +573,51 @@ Execution semantics: - `workflow.stages` order is the canonical progression order. - `agentic` is stage-sequential by default: planner advances one stage at a time in configured order. -- Parallelization is still possible inside a stage (independent subtasks), but cross-stage progression remains ordered. +- Parallelization is still possible inside a stage (independent subtasks), but cross-stage progression remains ordered by default. +- Set `depends_on` to unlock a DAG topology and let planner run independent branches in parallel. + +**Parallel stages with `depends_on`:** + +By default, each stage implicitly depends on the one before it (fully sequential). Add `depends_on` to any stage to declare explicit predecessors and create a dependency graph. Planner evaluates which stages are ready and can run independent branches in parallel. + +The canonical vstack DAG — seeded automatically by `vstack install`: + +```yaml +workflow: + mode: agentic + version: 1 + stages: + - role: product + gate: required + hitl: always + - role: architect + gate: required + hitl: always + depends_on: [product] + - role: designer + gate: optional + hitl: on-change + depends_on: [product] # runs in parallel with architect + - role: engineer + gate: required + hitl: always + depends_on: [architect, designer] # waits for both + - role: tester + gate: required + hitl: always + depends_on: [engineer] + - role: release + gate: required + hitl: always + depends_on: [tester] +``` + +Rules: + +- `depends_on: []` — marks the stage as a root (no predecessors). +- `depends_on` absent — stage implicitly depends on the previous stage (sequential fallback). +- Planner reads `depends_on` at runtime; no code change needed. +- Circular dependencies are detected at install/init time. Handoff target semantics: @@ -584,12 +628,49 @@ Handoff target semantics: Mode quickstart in Copilot Agent Mode: +> **In `agentic` mode, `@planner` is the primary entry point.** Start every session +> with `@planner` and let it drive all stage transitions automatically. + | Mode | Start here | First prompt example | | --------- | ------------------------ | ------------------------------------------------------- | | `agentic` | `@planner` | `@planner Run the workflow for this repository change.` | | `manual` | `@product` | `@product Define requirements for this change.` | | `hybrid` | `@planner` or `@product` | `@planner Run the workflow for this repository change.` | +What planner does: + +- Reads `workflow.stages` and `depends_on` from your project config. +- Invokes each role agent as a subagent at the right time. +- Runs independent branches in parallel when `depends_on` permits. +- Pauses at each gate for human-in-the-loop approval (when `hitl: always` or `hitl: on-change`). +- Skips `gate: optional` stages that are not affected by the current change. +- Reports a structured stage outcome after each step: status, changes made, blockers, next action. + +Which roles planner knows: +`product`, `architect`, `designer`, `engineer`, `tester`, `release`. +These are the only valid role names in `workflow.stages`. + +Agentic runbook (copy/paste): + +```text +@planner Run the workflow for this repository change. +@planner Show current stage status, ready stages, blocked stages, and next action. +@planner Continue with all ready stages in parallel where workflow.depends_on allows it. +@planner Pause at required HITL gates and ask for approval before advancing. +@planner Finalize with a release-readiness summary and list changed artifacts. +``` + +Troubleshooting: why planner is not running stages in parallel + +- Check `workflow.mode` in `.vstack/config.yaml`: parallel orchestration requires `agentic` (or planner-led `hybrid`). +- Validate stage dependencies: run `vstack validate` to catch invalid roles, self-dependencies, and cycles. +- Inspect `depends_on` shape: a stage runs only when all listed predecessors are `ready` or `skipped`. +- Check implicit sequential fallback: if `depends_on` is omitted, the stage depends on the previous stage. +- Check optional stage behavior: `gate: optional` can be skipped when unaffected, reducing apparent parallel fan-out. +- Check blockers in stage report: any `blocked` predecessor prevents dependent stages from becoming ready. +- Check mixed execution path: in `hybrid`, mixing manual handoffs and planner in one session can mask parallel readiness. +- Confirm you started with planner: in `agentic`, begin with `@planner`, not a worker role agent. + Hybrid operating rule: - Choose one path per session (planner-led or manual handoffs) and stay on it. diff --git a/docs/architecture/adr/028-dag-dependency-semantics.md b/docs/architecture/adr/028-dag-dependency-semantics.md new file mode 100644 index 00000000..5344484d --- /dev/null +++ b/docs/architecture/adr/028-dag-dependency-semantics.md @@ -0,0 +1,107 @@ +# ADR-028: DAG dependency semantics for multi-agentic workflow stages + +> Maintained by: **architect** role + +**date:** 2026-05-12 +**status:** accepted +**depends on:** ADR-023 (workflow contract), ADR-024 (subagent orchestration) + +## context + +ADR-023 introduced `workflow.stages` as the project source of truth for stage order, +gates, and handoffs. The initial behavior remained sequential: each stage progressed to +the next stage in declared order. + +That model is deterministic and simple, but it cannot represent branching workflows where +multiple stages can start after a shared prerequisite. It also cannot represent explicit +joins where a downstream stage waits for several upstream stages. + +The roadmap already identified a DAG model as the next step. We now need to make DAG +semantics first-class in workflow configuration while preserving compatibility for existing +projects that rely on sequential behavior. + +## decision + +### 1. Add optional `depends_on` to `workflow.stages` + +Each stage may define `depends_on: [role, ...]` in `.vstack/config.yaml`. + +Rules: + +1. Dependency names must reference existing stage roles. +1. A stage cannot depend on itself. +1. Duplicate dependency entries are normalized to one entry. +1. Blank dependency entries are ignored. + +### 2. Keep sequential compatibility as default + +If a stage omits `depends_on`, vstack applies an implicit dependency on the previous +stage in `workflow.stages` order. + +This preserves legacy behavior without requiring migration for existing repositories. + +### 3. Validate the full workflow graph before generation + +Workflow parsing now validates: + +1. Stage roles are non-empty. +1. Stage roles are unique. +1. `handoffs[].agent` targets refer to existing stage roles. +1. `depends_on` references are valid and non-self. +1. The full stage graph is acyclic. + +Invalid configuration fails fast with actionable error messages. + +### 4. Make handoff resolution DAG-aware + +Worker handoff target resolution now derives downstream roles from dependency edges, +not only from linear next-stage indexing. + +Behavior: + +1. If a stage has no explicit handoffs and has multiple downstream dependents, fallback + handoffs are generated for each dependent in configured stage order. +1. If explicit handoffs omit `agent`, target resolution defaults to the first downstream + dependent in configured stage order. +1. Existing manual/agentic/hybrid mode semantics are unchanged. + +## alternatives considered + +### A. Keep strictly sequential stage semantics + +Rejected. This cannot model branching and join dependencies and blocks DAG progression. + +### B. Introduce a separate `workflow.dependencies` top-level map + +Rejected. It duplicates role declarations and increases drift risk between stage and +dependency definitions. + +### C. Treat DAG as planner-only runtime logic without schema support + +Rejected. Without schema-level dependency data, planner behavior would be implicit, +non-portable, and hard to validate. + +## rationale + +This decision gives vstack a minimal but robust DAG contract: + +1. Configuration remains backward compatible. +1. Validation is strict enough to prevent ambiguous or unsafe orchestration graphs. +1. Worker artifact generation can express dependency-aware transitions. +1. Planner evolution toward parallel stage scheduling is unblocked. + +The design intentionally separates two concerns: + +1. **Configuration semantics** (this ADR): dependency graph, validation, and handoff target rules. +1. **Runtime scheduling policy** (follow-on work): how planner executes ready stages in parallel + and how join/failure behavior is handled. + +## impact on the Option B pipeline + +This ADR upgrades the workflow contract from linear progression to a validated DAG model. +It enables planner implementations to compute ready stages from dependency completion and +support multi-agentic execution patterns while keeping deterministic compatibility behavior +for existing projects. + +Planner runtime layer scheduling and explicit join policy controls remain follow-on work, +not part of this ADR's shipped scope. diff --git a/docs/architecture/adr/029-multi-agentic-execution-model.md b/docs/architecture/adr/029-multi-agentic-execution-model.md new file mode 100644 index 00000000..da75f2d2 --- /dev/null +++ b/docs/architecture/adr/029-multi-agentic-execution-model.md @@ -0,0 +1,160 @@ +# ADR-029: Multi-agentic execution model — DAG as primary coordination model + +> Maintained by: **architect** role + +**date:** 2026-05-12 +**status:** accepted +**depends on:** ADR-028 (DAG dependency semantics), ADR-009 (role model), ADR-004 (Option A to B pipeline) + +## context + +vstack models software delivery as a pipeline of role agents: product, architect, designer, +engineer, tester, release. As the system evolves toward multi-agentic execution (Option B), +a coordination model must be chosen that: + +1. Expresses dependencies between stages without requiring a runtime scheduler today. +1. Scales from sequential single-agent execution to concurrent multi-agent execution without + breaking existing configurations. +1. Stays debuggable and transparent to the developer — the developer must be able to reason + about why an agent was invoked and in what order. +1. Runs inside VS Code Agent Mode, which is chat-turn-based, not event-reactive. + +Four coordination models were evaluated: + +- **Sequential** — stages run in declared order, no branching or concurrency. +- **DAG** — stages declare explicit dependencies; ready stages execute when all dependencies complete. +- **Event-driven** — agents react to artifact changes (file writes, state transitions) without + central coordination. +- **Orchestration tree** — a root planner decomposes work into sub-planners, which in turn spawn + leaf agents. + +## decision + +**Use the DAG model as the primary coordination contract for vstack workflows.** + +The decision follows from two separate concerns kept intentionally separate: + +1. **Configuration semantics** (shipped in ADR-028): `depends_on` per stage, validated DAG, + backward-compatible sequential default. +1. **Runtime scheduling policy** (follow-on work): how the planner executes ready stages, + handles joins, and manages failure. + +The DAG model is adopted because: + +1. Dependencies are explicit in `workflow.stages` config — there is no hidden runtime inference. +1. The schema is backward compatible: stages without `depends_on` continue sequential behavior. +1. Validation is strict and fast-fail: cycles, unknown targets, and self-references are caught + at install time. +1. It is compatible with VS Code Agent Mode's turn-based execution model. +1. Evolution to parallel scheduling does not require configuration changes — the dependency data + is already present. + +## alternatives considered + +### A. Sequential ordering (status quo before ADR-028) + +Rejected as the long-term model for the following reasons: + +- Cannot express that architect and designer can both start once product is done. +- Cannot express that engineer waits for both architect and designer before starting. +- Any parallelism would require duplicating stages or changing the ordering heuristic. + +Retained as the backward-compatible default when `depends_on` is omitted. + +### B. Event-driven coordination + +In an event-driven model, agents react to artifact changes rather than explicit stage +progression. For example, the engineer agent would start when `architecture.md` and +`design.md` appear on disk, and the tester would start when code changes are written. + +**Why not adopted:** + +1. VS Code Agent Mode is chat-turn-based. Agents are invoked explicitly by the user or the + planner, not by file-system events. There is no ambient watcher. +1. Without a runtime trigger mechanism, agents have no signal to start. The question + "how does the engineer know to build?" has no answer in a pure event-driven model + inside VS Code. +1. Artifact contracts between agents would need to be defined and versioned separately + from stage configuration. This increases surface area without clear benefit at current scale. +1. Non-determinism: if two agents produce the same artifact type, the event fan-out is + ambiguous. Cycle prevention requires the same graph analysis as the DAG model, but + without the explicit declaration. +1. Debugging is harder: "why did the tester not start?" requires inspecting artifact state + rather than reading a dependency declaration. + +Event-driven coordination remains a viable model for external system integrations (triggering +vstack stages from CI artifact uploads, for example), but it is not the right default for the +VS Code-native execution model. + +### C. Orchestration tree (hierarchical planner) + +In an orchestration tree, the root planner decomposes work into sub-planners, which in turn +spawn leaf agents. This enables recursive decomposition: a `tester-planner` could invoke +`tester(security)`, `tester(performance)`, and `tester(functional)` as independent sub-agents. + +**Why not adopted as the primary model:** + +1. Requires a runtime planner capable of recursive agent spawning, which is not available in + the current single-call VS Code Agent Mode execution model. +1. Context propagation through the tree is lossy: each sub-agent receives only a slice of the + full project context. +1. Cost and latency scale with tree depth: a three-level tree multiplies model calls significantly. +1. Error propagation requires explicit join and failure-handling policy at every tree node. +1. The planner agent already exists in vstack; its evolution toward hierarchical orchestration + is architecturally possible but is a follow-on milestone. + +**Role-variant invocation (e.g. tester(security), tester(performance)):** + +The orchestration tree pattern is the correct model when a single role must be invoked +multiple times in different contexts within the same workflow. This applies to any role +whose prompt explicitly permits self-decomposition, including architect, product, designer, +engineer, tester, and release when the work can be merged cleanly. Two strategies are available: + +1. **Internal orchestration (recommended now):** The `tester` agent orchestrates its own + sub-specializations using skill invocations within a single agent turn. No schema change + required. The `tester` agent reads its context and calls `security`, `performance`, and + functional verification skills in sequence or passes them as sub-tasks. + +1. **Stage variants (future):** Introduce a `variant` or `context` field alongside `role` + to create distinct stage identities (`tester/security`, `tester/performance`). This + requires changing the stage identity key from `role` to `role + variant`, updating the + duplicate-role validation rule, and updating handoff resolution. An ADR must be written + before implementing this. + +The current duplicate-role validation in `_validate_workflow_stages` explicitly rejects +configs with the same role appearing more than once. This is intentional: it prevents +ambiguous dependency edges until stage variants are formally supported. + +## rationale + +The DAG model provides the right balance of expressiveness and simplicity for vstack's +current execution context: + +| Property | Sequential | DAG | Event-driven | Orchestration tree | +| ----------------------------------------- | ---------- | --- | ------------ | ------------------ | +| Explicit dependency declaration | No | Yes | No | Partial | +| Cycle detection at install time | N/A | Yes | No (runtime) | Partial | +| Compatible with chat-turn execution | Yes | Yes | No | Partial | +| Backward compatible with existing configs | Yes | Yes | No | No | +| Enables future parallel scheduling | No | Yes | Yes | Yes | +| Debuggable by reading config alone | Yes | Yes | No | Partial | +| Supports role-variant invocation | No | No | Yes | Yes | + +The DAG model covers all current requirements and does not block future evolution. +Event-driven and orchestration tree remain valid expansion paths for specific use cases: + +- **Event-driven:** external trigger integration (CI artifact events → vstack stage start). +- **Orchestration tree:** recursive role decomposition when role-variant stage support is added. + +## impact on the Option B pipeline + +This ADR establishes the coordination semantics that the Option B planner runtime will +implement. When parallel scheduling is added: + +1. The planner reads `depends_on` from workflow config to compute ready stages. +1. Ready stages (all dependencies complete) can be dispatched concurrently. +1. Join behavior (waiting for all incoming edges) is implicit in the dependency graph. +1. No configuration migration is required for existing projects. + +This decision also defines the boundary for the next evolution step: if role-variant support +is required, an ADR must extend the stage identity model before code changes are made. diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index 792863c3..83c77c9f 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -1,7 +1,7 @@ # vstack — architecture > Maintained by: **architect** role\ -> Last updated: 2026-04-26 +> Last updated: 2026-05-12 ## overview @@ -208,6 +208,15 @@ flowchart LR Each role is a separate model call. Output artifacts from one role become the input context for the next, and progression only happens after explicit user approval at each stage gate. +Workflow dependency semantics: + +- `workflow.stages` defines the canonical role order. +- `depends_on` (optional) defines explicit DAG prerequisites per stage. +- Missing `depends_on` preserves sequential compatibility by depending on the previous stage. +- Invalid references, self-dependencies, duplicate stage roles, and graph cycles are rejected. +- In `agentic` mode, planner orchestration can schedule multiple ready stages when the + dependency graph allows parallel progression. + ```mermaid flowchart TD P[product] --> GP{User approves Product output} @@ -225,6 +234,7 @@ flowchart TD ``` See `docs/architecture/adr/023-workflow-contract.md`, `docs/architecture/adr/024-subagent-orchestration.md`, +`docs/architecture/adr/028-dag-dependency-semantics.md`, `docs/architecture/adr/029-multi-agentic-execution-model.md`, `docs/architecture/adr/010-artifact-flow.md`, and `docs/design/workflow.md` for pipeline and gate detail. ______________________________________________________________________ @@ -234,30 +244,34 @@ ______________________________________________________________________ All significant architectural decisions are recorded in `docs/architecture/adr/`. See individual files for context, decision, alternatives, and rationale. -| ADR | Title | Status | Notes | -| --- | ---------------------------------------------------- | ---------- | --------------------------------------- | -| 001 | VS Code-native variant | accepted | | -| 002 | Artifact naming and compatibility policy | accepted | | -| 003 | Backend-first verify | accepted | | -| 004 | Direct execution and orchestrated pipeline | superseded | Superseded by ADR-024 | -| 005 | VS Code prompt format | accepted | | -| 006 | No runtime dependency on external binaries | accepted | | -| 007 | Python runtime | accepted | | -| 008 | Agents over prompts | accepted | | -| 009 | 6-role agent model | accepted | | -| 010 | Artifact flow | accepted | | -| 011 | Skill restructure | accepted | | -| 012 | Flat templates and install-time generation | accepted | | -| 013 | Policy vs procedure boundary for instructions/skills | accepted | | -| 014 | Manifest schema versioning and explicit upgrade gate | accepted | | -| 015 | Conservative install-by-default | superseded | Superseded by ADR-020 | -| 016 | Atomic manifest writes | accepted | | -| 017 | Checksum backfill on upgrade | accepted | | -| 018 | Skill genericity boundary | accepted | | -| 019 | `.vstack/` project-scope directory | accepted | Introduced `.vstack/` directory | -| 020 | `install` and `init` command semantics | accepted | Breaking change; supersedes ADR-015 | -| 021 | Config-driven artifact paths in agent config | accepted | Machine-readable artifact ownership | -| 022 | Selective exclude filter in `.vstack/config.yaml` | accepted | Agents cannot be excluded (atomic unit) | -| 023 | Workflow contract in `.vstack/config.yaml` | accepted | Pipeline order, gate, hitl, handoffs | -| 024 | Subagent orchestration via VS Code native subagents | accepted | Supersedes ADR-004; planner coordinator | -| 025 | PyYAML as sole runtime dependency | accepted | Replaces hand-rolled frontmatter parser | +| ADR | Title | Status | Notes | +| --- | ---------------------------------------------------- | ---------- | ------------------------------------------ | +| 001 | VS Code-native variant | accepted | | +| 002 | Artifact naming and compatibility policy | accepted | | +| 003 | Backend-first verify | accepted | | +| 004 | Direct execution and orchestrated pipeline | superseded | Superseded by ADR-024 | +| 005 | VS Code prompt format | accepted | | +| 006 | No runtime dependency on external binaries | accepted | | +| 007 | Python runtime | accepted | | +| 008 | Agents over prompts | accepted | | +| 009 | 6-role agent model | accepted | | +| 010 | Artifact flow | accepted | | +| 011 | Skill restructure | accepted | | +| 012 | Flat templates and install-time generation | accepted | | +| 013 | Policy vs procedure boundary for instructions/skills | accepted | | +| 014 | Manifest schema versioning and explicit upgrade gate | accepted | | +| 015 | Conservative install-by-default | superseded | Superseded by ADR-020 | +| 016 | Atomic manifest writes | accepted | | +| 017 | Checksum backfill on upgrade | accepted | | +| 018 | Skill genericity boundary | accepted | | +| 019 | `.vstack/` project-scope directory | accepted | Introduced `.vstack/` directory | +| 020 | `install` and `init` command semantics | accepted | Breaking change; supersedes ADR-015 | +| 021 | Config-driven artifact paths in agent config | accepted | Machine-readable artifact ownership | +| 022 | Selective exclude filter in `.vstack/config.yaml` | accepted | Agents cannot be excluded (atomic unit) | +| 023 | Workflow contract in `.vstack/config.yaml` | accepted | Pipeline order, gate, hitl, handoffs | +| 024 | Subagent orchestration via VS Code native subagents | accepted | Supersedes ADR-004; planner coordinator | +| 025 | PyYAML as sole runtime dependency | accepted | Replaces hand-rolled frontmatter parser | +| 026 | Docs artifact migration policy | accepted | Versioned docs relocation maps | +| 027 | Repository hooks as first-class artifact type | accepted | Hook templates and manifest ownership | +| 028 | DAG dependency semantics for workflow stages | accepted | `depends_on`, cycle safety, compatibility | +| 029 | Multi-agentic execution model | accepted | DAG chosen; event-driven and tree compared | diff --git a/docs/design/agents.md b/docs/design/agents.md index 4cfbdbfc..375f2dd7 100644 --- a/docs/design/agents.md +++ b/docs/design/agents.md @@ -1,7 +1,7 @@ # vstack — agents > Maintained by: **designer** role\ -> Last updated: 2026-05-03\ +> Last updated: 2026-05-13\ > VS Code docs: [custom agents](https://code.visualstudio.com/docs/copilot/customization/custom-agents) · [agents overview](https://code.visualstudio.com/docs/copilot/agents/overview) ## what are agents? @@ -24,6 +24,20 @@ Generation is mode-aware via `.vstack/config.yaml` `workflow.mode`: In `hybrid`, the UI exposes both progression paths (planner and handoff buttons). Use it only when your process explicitly allows both. +Planner orchestration also reads `workflow.stages[*].depends_on` when deciding which +roles are ready. The generated agents remain VS Code custom agents, but the workflow +controller can fan out independent stages in parallel when the DAG permits it. + +```mermaid +flowchart LR + A[workflow.mode] --> B{mode} + B -->|agentic| C[planner generated] + B -->|manual| D[planner omitted] + B -->|hybrid| E[planner generated + worker handoffs] + C --> F[planner reads depends_on] + F --> G[ready set / parallel branches] +``` + Canonical names are the source of truth. Historical or compatibility aliases should remain exceptional and temporary. See `docs/architecture/adr/002-artifact-naming-and-compatibility-policy.md`. diff --git a/docs/design/workflow.md b/docs/design/workflow.md index dd88bf44..45996a50 100644 --- a/docs/design/workflow.md +++ b/docs/design/workflow.md @@ -1,7 +1,7 @@ # vstack — workflow > Maintained by: **designer** role\ -> Last updated: 2026-05-03 +> Last updated: 2026-05-12 ## overview @@ -16,6 +16,7 @@ For authoring boundaries between reusable guidance mechanisms: - [instructions.md](./instructions.md) - [skills.md](./skills.md) - [013-instructions-vs-skills-boundary.md](../architecture/adr/013-instructions-vs-skills-boundary.md) +- [028-dag-dependency-semantics.md](../architecture/adr/028-dag-dependency-semantics.md) ______________________________________________________________________ @@ -38,16 +39,38 @@ Mode semantics: Execution semantics: - `workflow.stages` order is the canonical progression order. -- `agentic` is stage-sequential by default: planner advances one stage at a time in configured order. -- Parallelization may still happen inside a stage (independent subtasks), but cross-stage progression remains ordered. +- `depends_on` is optional per stage. +- When `depends_on` is omitted, the stage implicitly depends on the previous stage. +- When `depends_on` is set, it is the source of truth for stage prerequisites. +- A stage becomes ready only when all dependencies are complete. +- Multiple ready stages can be orchestrated in parallel by the planner in `agentic` mode. + +Dependency semantics (`depends_on`): + +- `depends_on` must reference existing stage role names. +- `depends_on` cannot include the stage role itself. +- The resulting stage graph must be acyclic. +- Empty or blank dependency entries are ignored. + +Backward compatibility: + +- Existing configs without `depends_on` continue to run in the same sequential order. +- DAG behavior is opt-in by adding `depends_on` explicitly. Handoff target semantics: - `handoffs.prompt` defines the transition prompt text. -- If `handoffs.agent` is omitted, the target defaults to the next role in `workflow.stages`. +- If `handoffs.agent` is omitted, the target defaults to the primary downstream dependent role. - `handoffs.agent` may be set explicitly to override default targeting in `manual`/`hybrid`. - In `agentic`, worker handoff buttons are omitted; planner controls progression. +For branching DAGs: + +- When a stage has no explicit handoff entries and has multiple downstream dependents, + fallback handoffs are emitted for each downstream dependent in configured stage order. +- When a stage has explicit handoff entries with no `agent`, the target defaults to the + first downstream dependent in configured stage order. + ```mermaid flowchart LR A[workflow.mode] --> B{mode} @@ -62,6 +85,17 @@ flowchart LR E --> E2[worker handoffs generated] ``` +```mermaid +flowchart TD + P[product] --> A[architect] + P --> D[designer] + A --> E[engineer] + D --> E + A --> T[tester] + E --> R[release] + T --> R +``` + Hybrid caution: - Hybrid intentionally exposes two progression paths in the UI. @@ -70,6 +104,93 @@ Hybrid caution: ______________________________________________________________________ +## multi-agentic coordination model + +vstack uses a **DAG (directed acyclic graph)** as its coordination model for stage +dependencies. This section describes why, and how alternative models compare. + +### models compared + +| Property | Sequential | DAG | Event-driven | Orchestration tree | +| ----------------------------------------- | ---------- | --- | ------------ | ------------------ | +| Explicit dependency declaration | No | Yes | No | Partial | +| Cycle detection at install time | N/A | Yes | No (runtime) | Partial | +| Compatible with VS Code chat-turn model | Yes | Yes | No | Partial | +| Backward compatible with existing configs | Yes | Yes | No | No | +| Enables future parallel scheduling | No | Yes | Yes | Yes | +| Debuggable by reading config alone | Yes | Yes | No | Partial | +| Supports role-variant invocation | No | No | Yes | Yes | + +### why not event-driven + +In an event-driven model, agents react to artifact changes — the engineer would start +when `architecture.md` appears on disk; the tester would start when new code is committed. + +This model does not fit VS Code Agent Mode for two reasons: + +1. **No ambient trigger mechanism.** Agents are invoked explicitly in chat turns. There + is no file-watcher or event bus. The engineer has no signal to start building; the tester + has no signal to start testing. Coordination collapses back to the planner invoking agents + explicitly — which is the DAG model in practice. + +1. **Debugging requires inspecting artifact state.** "Why did the tester not start?" cannot + be answered by reading a config file; it requires tracing which artifacts were written and + which triggers fired. DAG config answers this directly. + +Event-driven coordination remains a useful pattern for *external integrations* — for example, +triggering a vstack stage from a CI artifact upload event — but it is not the right default +for the VS Code-native execution model. + +### why not orchestration tree + +In an orchestration tree, a root planner spawns sub-planners, which in turn spawn leaf +agents. This enables recursive decomposition: a `tester-planner` could spawn +`tester(security)`, `tester(performance)`, and `tester(functional)` as separate agents. + +This is architecturally possible but not adopted as the primary model because: + +1. Each sub-agent receives only a slice of the full project context. Deeper trees lose + context rapidly and may produce incoherent outputs. +1. Cost and latency scale with tree depth. +1. Error propagation requires explicit join and failure-handling policy at every tree node. +1. The current VS Code execution model does not support recursive agent spawning natively. + +### role-variant invocation (tester(security), tester(performance)) + +When a single role must run in multiple contexts within one workflow, two strategies apply: + +This is not limited to `tester`; the same pattern can be used for architect, product, +designer, engineer, or release when that role prompt explicitly allows self-decomposition. + +1. **Internal orchestration (recommended):** The agent orchestrates its own sub-specializations + using skill invocations in a single turn. The `tester` agent calls the `security`, + `performance`, and functional verification skills in sequence internally. No config change + required. + +1. **Stage variants (future):** A `variant` field alongside `role` creates distinct stage + identities (`tester/security`, `tester/performance`). This requires extending the stage + identity key, updating duplicate-role validation, and an ADR before implementation. + +The current duplicate-role validation rejects configs where the same role appears more than +once. This is intentional: it prevents ambiguous dependency edges until stage variants are +formally defined. + +### evolution path + +The DAG model does not block future evolution: + +- **Parallel scheduling:** planner reads `depends_on` to compute ready stages and dispatches + them concurrently. No config migration required. +- **Event-driven integration:** external CI events can trigger planner invocations that then + follow the DAG execution model internally. +- **Orchestration tree:** role-variant support, once specified via ADR, extends the current + DAG model with variant-qualified stage identities. + +See [ADR-029](../architecture/adr/029-multi-agentic-execution-model.md) for the full +decision rationale and alternatives analysis. + +______________________________________________________________________ + ## repository automation (GitHub Actions) The repository uses a split workflow model so each automation concern is isolated diff --git a/docs/product/requirements.md b/docs/product/requirements.md index 085a9738..1ced105d 100644 --- a/docs/product/requirements.md +++ b/docs/product/requirements.md @@ -94,7 +94,7 @@ ______________________________________________________________________ - 6 delivery agent roles: `product`, `architect`, `designer`, `engineer`, `tester`, `release`, plus a `planner` coordinator agent. -- 27 backend-oriented skills with canonical names enforced at source-verify time. +- 42 skills across the role set with canonical names enforced at source-verify time. ### FR-11 — workflow mode semantics @@ -104,13 +104,20 @@ ______________________________________________________________________ - In `manual`, planner is not generated and worker handoff buttons are shown. - In `hybrid`, planner is generated and worker handoff buttons are shown. +### FR-12 — workflow DAG semantics + +- `workflow.stages[*].depends_on` is optional and declares explicit stage prerequisites. +- When `depends_on` is omitted, the stage falls back to sequential dependency on the previous stage. +- Invalid dependency references, self-dependencies, duplicate stage roles, and cycles are rejected. +- In `agentic`, the planner may schedule multiple ready stages in parallel when the DAG permits it. + ______________________________________________________________________ ## non-functional requirements | ID | Requirement | | ----- | ------------------------------------------------------------------------------------------------------ | -| NFR-1 | No runtime dependencies beyond the Python standard library. | +| NFR-1 | One runtime dependency only: PyYAML (`pyyaml>=6.0`) for YAML frontmatter parsing. | | NFR-2 | Python 3.11–3.14 compatibility. | | NFR-3 | Manifest writes are atomic: write to a temporary file, then replace atomically. | | NFR-4 | All public behavior exercised by automated tests (pytest). CI gate enforces test pass. | @@ -129,7 +136,7 @@ ______________________________________________________________________ 1. `vstack verify --target DIR` reports zero errors on a clean install. 1. `vstack validate` exits 0 when all source templates resolve cleanly. 1. Locally modified tracked files are preserved on re-install by default (FR-4). -1. All 27 canonical skill names are present after a full install. +1. All 42 canonical skill names are present after a full install. 1. `vstack manifest upgrade` migrates a legacy manifest without data loss. ______________________________________________________________________ diff --git a/docs/product/roadmap.md b/docs/product/roadmap.md index 81aef9fd..97427edc 100644 --- a/docs/product/roadmap.md +++ b/docs/product/roadmap.md @@ -1,43 +1,43 @@ # vstack — roadmap > Maintained by: **product** role\ -> Last updated: 2026-05-06 +> Last updated: 2026-05-13 ______________________________________________________________________ ## feature status table -| Feature | Version | Status | Notes | -| ---------------------------------------- | ------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| foundation | v1.0.0 | shipped | Core template-driven install model is in place | -| backend-first verification | v1.0.0 | shipped | Verify/inspect focus on contracts, observability, security | -| VS Code agent migration | v1.x | shipped | Native `.github/agents/*.agent.md` output format implemented | -| role model + doc restructure | v1.1.0 | shipped | 6-role model, agent templates, and docs baseline established | -| new skill scaffolding | v2.2.0 | shipped | 42-skill set with canonical naming | -| agent skill wiring | v2.2.0 | shipped | Role-to-skill mapping, handoffs, and concise modes wired into all agents | -| CLI modularisation | v2.0.0 | shipped | 12 focused CLI modules; BaseCommand + CommandContext contract | -| manifest package | v2.0.0 | shipped | Dedicated `manifest/` package; atomic writes (ADR-016) | -| mypy type checking | v2.0.0 | shipped | Full mypy coverage enforced in CI; 100% test coverage gate | -| manifest schema versioning | v2.0.0 | shipped | `manifest_version: 2`; upgrade path via `manifest upgrade` (ADR-014) | -| checksum backfill | v2.0.0 | shipped | `manifest upgrade --backfill` adds SHA-256 for VSTACK-META-tagged files (ADR-017) | -| conservative install | v2.0.0 | shipped | Untracked files never overwritten; checksum-gated update (ADR-015, superseded by ADR-020) | -| dry-run install | v2.1.0 | shipped | `vstack install --dry-run` previews actions; type/name selectors in summary | -| project-scope directory | v3.0.0 | shipped | `.vstack/` directory: `config.yaml`, manifest, delta templates (ADR-019) | -| install/init command semantics | v3.0.0 | shipped | `install` = first-run setup; `init` = idempotent CI regeneration (ADR-020, breaking change) | -| manifest relocation | v3.0.0 | shipped | `vstack.json` moves from `.github/` to `.vstack/`; migration via `manifest upgrade` (ADR-014) | -| selective install | v3.0.0 | shipped | Per-type and per-name exclusions via `exclude:` in `.vstack/config.yaml`; agents always installed (ADR-022) | -| agent hooks support | t.b.d. | shipped | First-class `hook` artifact type: generate `.github/hooks/.json` from templates and track in manifest | -| new skills (next batch) | t.b.d. | candidate | `spaces`: set up Copilot Spaces; `copilot-admin`: manage Copilot settings via `gh api` | -| team customization layer | t.b.d. | candidate | Custompacks on top of vstack defaults; agents non-removable, skills fully overridable; overlay merge model | -| workflow contract source-of-truth | t.b.d. | shipped | `workflow:` block in `.vstack/config.yaml`; `gate`, `hitl`, `handoffs` schema; `vstack migrate` command (ADR-023, ADR-026) | -| optional orchestrated role pipeline | v3.2.0 | shipped | `planner` coordinator agent implemented with mode-aware generation; default mode is `agentic` (`manual` and `hybrid` also supported) | -| parallel workflow via DAG model | v3.3.0 | candidate | Directed Acyclic Graph orchestration: run stages in parallel where safe; serialize only on data dependencies | -| multi-IDE support (IntelliJ first) | t.b.d. | candidate | Not planned before current model stabilizes | -| heavy agent runtime framework | — | not planned | Keeps runtime lightweight and transparent | -| cloud control plane dependency | — | not planned | Keeps operation local/offline-capable | -| VS Code extension packaging | — | not planned | Not required for current install model | -| browser automation as default dependency | — | not planned | Backend/microservice-first remains default | -| install target directory override | — | not planned | Won't implement unless a concrete tool incompatibility with `.github/` arises | +| Feature | Version | Status | Notes | +| ---------------------------------------- | ------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| foundation | v1.0.0 | shipped | Core template-driven install model is in place | +| backend-first verification | v1.0.0 | shipped | Verify/inspect focus on contracts, observability, security | +| VS Code agent migration | v1.x | shipped | Native `.github/agents/*.agent.md` output format implemented | +| role model + doc restructure | v1.1.0 | shipped | 6-role model, agent templates, and docs baseline established | +| new skill scaffolding | v2.2.0 | shipped | 42-skill set with canonical naming | +| agent skill wiring | v2.2.0 | shipped | Role-to-skill mapping, handoffs, and concise modes wired into all agents | +| CLI modularisation | v2.0.0 | shipped | 12 focused CLI modules; BaseCommand + CommandContext contract | +| manifest package | v2.0.0 | shipped | Dedicated `manifest/` package; atomic writes (ADR-016) | +| mypy type checking | v2.0.0 | shipped | Full mypy coverage enforced in CI; 100% test coverage gate | +| manifest schema versioning | v2.0.0 | shipped | `manifest_version: 2`; upgrade path via `manifest upgrade` (ADR-014) | +| checksum backfill | v2.0.0 | shipped | `manifest upgrade --backfill` adds SHA-256 for VSTACK-META-tagged files (ADR-017) | +| conservative install | v2.0.0 | shipped | Untracked files never overwritten; checksum-gated update (ADR-015, superseded by ADR-020) | +| dry-run install | v2.1.0 | shipped | `vstack install --dry-run` previews actions; type/name selectors in summary | +| project-scope directory | v3.0.0 | shipped | `.vstack/` directory: `config.yaml`, manifest, delta templates (ADR-019) | +| install/init command semantics | v3.0.0 | shipped | `install` = first-run setup; `init` = idempotent CI regeneration (ADR-020, breaking change) | +| manifest relocation | v3.0.0 | shipped | `vstack.json` moves from `.github/` to `.vstack/`; migration via `manifest upgrade` (ADR-014) | +| selective install | v3.0.0 | shipped | Per-type and per-name exclusions via `exclude:` in `.vstack/config.yaml`; agents always installed (ADR-022) | +| workflow contract source-of-truth | v3.1.0 | shipped | `workflow:` block in `.vstack/config.yaml`; `gate`, `hitl`, `handoffs` schema; `vstack migrate` command (ADR-023, ADR-026) | +| agent hooks support | v3.2.0 | shipped | First-class `hook` artifact type: generate `.github/hooks/.json` from templates and track in manifest | +| optional orchestrated role pipeline | v3.2.0 | shipped | `planner` coordinator agent implemented with mode-aware generation; default mode is `agentic` (`manual` and `hybrid` also supported) | +| parallel workflow via DAG model | t.b.d. | candidate | `depends_on` DAG semantics implemented in code; awaiting a release tag before being promoted to shipped | +| new skills (next batch) | t.b.d. | candidate | `spaces`: set up Copilot Spaces; `copilot-admin`: manage Copilot settings via `gh api` | +| team customization layer | t.b.d. | candidate | Deferred major update after VS Code-first model proves itself; custompacks, overlay merge rules, and install profiles all add major maintenance surface | +| multi-IDE support (IntelliJ first) | t.b.d. | candidate | Deferred until vstack proves stable in VS Code; likely a major follow-up because it needs separate targets, schemas, and more maintenance | +| heavy agent runtime framework | — | not planned | Keeps runtime lightweight and transparent | +| cloud control plane dependency | — | not planned | Keeps operation local/offline-capable | +| VS Code extension packaging | — | not planned | Not required for current install model | +| browser automation as default dependency | — | not planned | Backend/microservice-first remains default | +| install target directory override | — | not planned | Won't implement unless a concrete tool incompatibility with `.github/` arises | ______________________________________________________________________ @@ -155,7 +155,7 @@ ______________________________________________________________________ ______________________________________________________________________ -### agent hooks support [shipped — t.b.d.] +### agent hooks support [shipped — v3.2.0] GitHub Copilot agents support a repository-level hooks mechanism: shell commands defined in `.github/hooks/.json` that execute at key points during an agent session — @@ -225,9 +225,23 @@ Ref: [GitHub — Administer GitHub Copilot for your team](https://docs.github.co ### team customization layer [candidate — t.b.d.] -Teams want to put their own layer on top of vstack — tuning agents to company context, -replacing generic skills with company-specific ones, and bundling those changes as a -reusable custompack that travels with the project. +This is a later major update, not a near-term roadmap item. The idea is valid, but it +adds another product layer on top of a system that is still proving itself in its +VS Code-first form. + +Teams may eventually want to tune agents to company context, replace generic skills with +company-specific ones, and bundle those changes as a reusable custompack that travels +with the project. That remains a plausible direction, but only after the core model has +earned more real-world validation. + +Why this is deferred: + +- It adds a second overlay system on top of the base template model. +- It requires custompack install, status, and uninstall semantics. +- It introduces source-aware manifest tracking and merge rules. +- It increases maintenance cost for every future agent, skill, and workflow change. +- It can introduce breaking changes if the base model evolves. +- It should only be revisited if real project demand still exists after VS Code adoption is proven. Rules: @@ -253,11 +267,11 @@ Overlay model (template source priority): - Conflict resolution is deterministic: custompack wins for skills, instructions, and prompts; agents are merged (custompack fields override, core structure preserved) - Source-aware checksums in `vstack.json` so updates can be applied safely per origin -- Longer term: `vstack install --profile company` for named install profiles +- Longer term, if still justified: `vstack install --profile company` for named install profiles Ref: [GitHub — Customize Copilot for your project](https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/customize-copilot-overview) -### workflow contract source-of-truth [shipped — t.b.d.] +### workflow contract source-of-truth [shipped — v3.1.0] Shipped in this release: @@ -275,7 +289,7 @@ Not yet implemented (deferred to orchestrated pipeline milestone): - Generator-level cross-role validation of input/output chains - Central read-only contract export for external orchestrator consumption -### parallel workflow via DAG model [candidate — v3.3.0] +### parallel workflow via DAG model [candidate — t.b.d.] **What is DAG?** (Directed Acyclic Graph) @@ -286,10 +300,19 @@ A DAG is a directed graph with no cycles that represents task dependencies. In t - **Parallel execution** happens when stages have no direct or transitive dependency between them - **Deterministic ordering** is preserved: a stage never starts until all its dependencies complete -**Current limitation:** +**Current behavior:** + +vstack now supports DAG dependencies through optional `depends_on` in `workflow.stages`. +When `depends_on` is omitted, behavior remains fully sequential and backward compatible. + +Validation and safety guarantees: -Today, vstack uses **linear/serial execution**: stages run strictly in canonical order (product → architect → -designer → engineer → tester → release). This is safe and deterministic, but inefficient: +- unknown dependency role references are rejected, +- self-dependencies are rejected, +- duplicate stage roles are rejected, +- cyclic dependency graphs are rejected. + +Sequential compatibility mode (no `depends_on`): ``` time → @@ -327,33 +350,18 @@ product ████ Potential wall-clock reduction: 6 stages → 3 stages (~50% faster). -**DAG-model implementation plan:** - -1. **Dependency schema** — extend `.vstack/config.yaml` `workflow.stages` with optional `depends_on: [list]` field - - - Default (omitted): inherits canonical ordering (backwards-compatible) - - Explicit deps: enables parallel scheduling - -1. **Scheduling algorithm** — topological sort of the DAG +**Current scope:** - - `planner` agent computes the schedule at orchestration start - - Stages are grouped into "layers": all stages in layer N can run in parallel; layer N+1 starts after layer N completes +1. Dependency schema support in `.vstack/config.yaml` via optional `depends_on` per stage. +1. DAG validation in CLI parsing and workflow graph checks. +1. Backward-compatible sequential defaults when `depends_on` is absent. +1. DAG-aware handoff target resolution for worker agents. -1. **Artifact locking** — prevent concurrent modification of shared output files +**Remaining work beyond this release:** - - Manifest locks at the artifact type level (e.g., only one agent can write to `.github/agents/`) - - Staged writes: all agents in a layer write to temp files; `planner` merges after layer completes - -1. **Join semantics** — configurable failure policy per stage layer - - - `all_success` (default): if any stage in a layer fails, the entire DAG fails - - `fail_fast`: same; no alternative in the initial version - - Future: `best_effort`, `skip_on_error` for optional quality gates - -1. **Output ordering** — ensure deterministic artifact ordering in generated files - - - Agent output is sorted by role name; same for skills, instructions - - Reproducible builds remain a design principle +1. Planner-level parallel dispatch scheduling and layer execution policy. +1. Explicit runtime join/failure strategy controls for parallel stage groups. +1. Additional integration tests for parallel orchestration execution traces. **Backwards compatibility:** @@ -403,11 +411,10 @@ workflow: **Next steps:** -1. Write ADR-027 (DAG-model orchestration with dependency schema) -1. Design `depends_on` schema and topological sort algorithm -1. Implement `planner` layer-grouping logic -1. Add integration tests for parallel stage execution and failure scenarios -1. Update config schema docs and examples +1. Implement planner runtime layer scheduling for parallel-ready stages. +1. Add explicit join policy knobs for DAG layer completion. +1. Expand orchestration integration tests for multi-stage parallel traces. +1. Add user-facing troubleshooting guidance for DAG misconfiguration recovery. ### optional orchestrated role pipeline [shipped — v3.2.0] @@ -426,8 +433,23 @@ Shipped: ### multi-IDE support [candidate — t.b.d.] -IntelliJ is the first candidate beyond VS Code. Not planned until after the current model stabilizes. -Requires separate template sets, different generator targets, and different frontmatter schemas. +This is a future major update, not a near-term roadmap item. vstack is intentionally +VS Code-first, and the current model still needs to prove itself before we can judge +whether support for other IDEs is worth the added complexity. + +IntelliJ is the first possible expansion target, but only after the VS Code model is +stable and the product direction still makes the investment worthwhile. + +Why this is deferred: + +- It requires separate template sets and generator targets per IDE. +- It likely needs IDE-specific frontmatter schemas and capability mapping. +- It increases maintenance burden across every future agent, hook, and workflow change. +- It raises the risk of breaking changes when the VS Code model evolves. +- The AI tooling landscape may shift before a second IDE target becomes strategically useful. + +Current decision: keep this as a later, optional major update and revisit it only after +vstack has established traction in the VS Code workflow first. ______________________________________________________________________ diff --git a/docs/product/vision.md b/docs/product/vision.md index 4e9e19d9..348fbfcd 100644 --- a/docs/product/vision.md +++ b/docs/product/vision.md @@ -1,7 +1,7 @@ # vstack — vision > Maintained by: **product** role\ -> Last updated: 2026-03-28 +> Last updated: 2026-05-13 ## what is vstack @@ -62,9 +62,9 @@ ______________________________________________________________________ release) define who produces what. Each role owns a specific set of artifacts. Agents communicate through files on disk. -1. **Pipeline-ready.** Today each skill runs in a single model call. If needed later, - the system can move to an orchestrated multi-role pipeline by adding a runner, - without rewriting skills. +1. **Pipeline-ready.** The planner/DAG orchestration model now handles multi-role + execution inside VS Code. If coordination needs to evolve further, it should extend + the existing workflow contract rather than forcing a rewrite of skills. ______________________________________________________________________ diff --git a/docs/reports/test-report.md b/docs/reports/test-report.md index 1bee4905..90411192 100644 --- a/docs/reports/test-report.md +++ b/docs/reports/test-report.md @@ -1,8 +1,8 @@ # Test Report -**Branch:** `feat/workflow_update`\ -**Date:** 2026-05-06\ -**Scope:** Full repository — `.vstack/` project-scope directory (ADR-019); `vstack install`/`vstack init` command semantics (ADR-020); manifest relocation from `.github/` to `.vstack/` (ADR-021); selective install with `exclude:` filter (ADR-022); `artifacts.root` config override; `.vstack/.gitignore` seeding; agent `artifacts:` section generation; ADR terminology update (Option A/B → direct execution/orchestrated pipeline); roadmap cleanup (version column, candidate rationalisation); gh-issues skill MCP-first guidance. CLI refactor, manifest backfill, and test suite structure remain in scope as prior context. +**Branch:** `feature/workflow-dag-validation`\ +**Date:** 2026-05-13\ +**Scope:** Full repository — workflow DAG semantics (`depends_on` validation, sequential fallback compatibility, planner-ready stage scheduling); agent parallel delegation policy; planner template orchestration; roadmap/version alignment; product/docs consistency updates; runtime version fallback fix; historical validation context retained for continuity. ______________________________________________________________________ @@ -10,7 +10,7 @@ ______________________________________________________________________ | Dimension | Result | | ------------- | ------------------------------------------ | -| Functional | **PASS** — 428/428 tests green | +| Functional | **PASS** — 635/635 tests green | | Lint / Style | **PASS** — ruff clean | | Type checking | **PASS** — mypy clean (51 files, 0 errors) | | Coverage | **PASS** — 100.00% (fail-under=100) | @@ -27,7 +27,7 @@ ______________________________________________________________________ platform: linux, Python 3.13.12-final-0 runner: pytest 9.0.3 + pytest-cov 7.1.0 command: pytest -q -428 passed in 5.90s +635 passed in 21.03s ``` All tests pass. No flaky, skipped, or xfail tests observed. @@ -36,7 +36,7 @@ ______________________________________________________________________ ## Coverage Summary -Total: 100.00% — 0 missed statements across 2,058 measured +Total: 100.00% — 0 missed statements across 2,806 measured `fail-under=100` is configured in `pyproject.toml`. This gate is **passing**. @@ -57,6 +57,8 @@ Blockage coverage findings were resolved by adding targeted unit tests for: - install/verify/uninstall error-path behavior - `manifest upgrade --backfill` branches: missing file, unreadable file, no VSTACK-META footer, existing checksum, unknown algorithm fallback +Recent additions also covered malformed `depends_on` inputs at parse and validation time so the workflow DAG no longer degrades silently when config types are wrong. + ______________________________________________________________________ ## Lint and Type Checking diff --git a/src/vstack/_templates/agents/_partials/agent-skill-boundary.md b/src/vstack/_templates/agents/_partials/agent-skill-boundary.md index 7a6b6f31..405b1e49 100644 --- a/src/vstack/_templates/agents/_partials/agent-skill-boundary.md +++ b/src/vstack/_templates/agents/_partials/agent-skill-boundary.md @@ -3,3 +3,5 @@ - **You (agent) = who/what/when** — decisions, scope, escalation, and handoffs within your role. - **Skills = how** — detailed procedures, checklists, and execution playbooks. - Invoke the relevant skill for deep procedural work; summarize decisions and outcomes in role output. +- **Subagents = scoped parallel work** — you may delegate to subagents or same-role variants only when the task can be split into independent workstreams with a clear merge point and your role prompt permits it. +- Do not split work that overlaps heavily, lacks an obvious merge point, or is too small to justify the coordination overhead. diff --git a/src/vstack/_templates/agents/architect/template.md b/src/vstack/_templates/agents/architect/template.md index 76f09a5f..b5262c75 100644 --- a/src/vstack/_templates/agents/architect/template.md +++ b/src/vstack/_templates/agents/architect/template.md @@ -37,6 +37,14 @@ You are a **senior software architect** acting as the **architect role**. You de - Capture significant structural choices in ADRs. - Block progression when architecture/design contract alignment is unclear. +## parallel delegation + +- If the scope naturally decomposes into independent architecture questions, you may split work across subagents or same-role variants. +- Good split candidates include separate ADRs, distinct failure-mode analyses, boundary decisions, and architecture overview updates when they do not depend on one another. +- Only split when each workstream has a clear merge point and the architectural conclusions are not mutually dependent. +- Do not split tightly coupled blueprint decisions that require one consistent system view. +- Make each delegated context explicit in the output so the resulting architecture baseline remains auditable. + ## communication style - Structured, opinionated, and evidence-based. diff --git a/src/vstack/_templates/agents/designer/template.md b/src/vstack/_templates/agents/designer/template.md index 17711f4b..a00da9c6 100644 --- a/src/vstack/_templates/agents/designer/template.md +++ b/src/vstack/_templates/agents/designer/template.md @@ -37,6 +37,14 @@ You are a **senior interaction designer** acting as the **designer role**. You t - Escalate structural implications before finalizing design items. - Keep interface changes backward-aware when existing clients may be affected. +## parallel delegation + +- If the design surface decomposes cleanly, you may split work across subagents or same-role variants. +- Good split candidates include API contracts, event schemas, state models, UX flows, and module boundaries when they do not share a mandatory merge decision. +- Only split when each design stream can be validated independently and recombined without ambiguity. +- Do not split tightly coupled interface decisions that require one coherent contract set. +- Make the merge point explicit so downstream implementation work sees one actionable design baseline. + ## communication style - Concrete and specification-oriented. diff --git a/src/vstack/_templates/agents/planner/template.md b/src/vstack/_templates/agents/planner/template.md index a60ed2cc..b228cb61 100644 --- a/src/vstack/_templates/agents/planner/template.md +++ b/src/vstack/_templates/agents/planner/template.md @@ -7,11 +7,20 @@ invoking role agents as subagents and enforcing explicit gate progression. ## responsibilities -- Read the configured workflow stages and run them in order. -- Invoke the correct role agent for each stage. +- Read the configured workflow stages and evaluate `depends_on` to determine execution order. +- Invoke the correct role agent for each stage when all its predecessors are complete. +- Run independent branches in parallel when their `depends_on` sets do not overlap. - Apply gate and human-in-the-loop policy at each transition. - Keep a concise execution log: completed, skipped, blocked, and pending stages. +## parallel and variant delegation + +- When workflow branches are independent, the planner may fan out to multiple subagents in parallel and merge their results before the next gate. +- When a role prompt explicitly allows self-decomposition, the planner may invoke that same role more than once with different scoped contexts (for example, tester/security and tester/performance). +- Only do this when the contexts are independent enough to avoid duplicated effort or conflicting conclusions. +- Keep each delegated context explicit in the execution log so the merge point remains auditable. +- Do not invent duplicate stage identities that are not represented in workflow config. + ## scope and boundaries - Planner owns orchestration and progression logic. @@ -27,7 +36,11 @@ invoking role agents as subagents and enforcing explicit gate progression. ## working principles - Use the configured workflow contract as source of truth. -- Execute one stage at a time unless the user asks otherwise. +- Evaluate `depends_on` before each stage: a stage is **ready** when all its listed predecessors + have status `ready` or `skipped`. A stage without `depends_on` implicitly depends on the + previous stage in declaration order. +- Run all ready stages before advancing past a gate boundary. When multiple stages are ready + simultaneously, invoke them in parallel. - Prefer explicit user confirmation at gate boundaries. - Keep summaries short, factual, and stage-oriented. @@ -49,15 +62,23 @@ invoking role agents as subagents and enforcing explicit gate progression. Execution model: -1. Load workflow stages from project config. +1. Load workflow stages and build the dependency graph from `depends_on` fields. + - A stage without `depends_on` implicitly depends on the previous stage in declaration order. + - `depends_on: []` marks a stage as a root with no predecessors. 1. Read `workflow.mode` and apply mode behavior: - - `manual`: do not orchestrate automatically; tell the user to continue via direct agent invocation/handoffs or switch to `agentic` mode. - - `agentic`: orchestrate stages sequentially and treat planner as the progression controller. + - `manual`: do not orchestrate automatically; tell the user to continue via direct agent + invocation/handoffs or switch to `agentic` mode. + - `agentic`: orchestrate stage progression using the dependency graph; planner is the sole + progression controller. - `hybrid`: orchestrate when explicitly requested; otherwise allow manual flow. -1. For each stage, invoke the mapped role agent as a subagent. -1. Capture stage result and evaluate gate policy. -1. Pause for user approval when required. -1. Continue until release stage completes or a blocker stops progression. +1. Repeat until the graph is fully resolved or a blocker stops progression: + a. Identify all stages whose `depends_on` predecessors are all `ready` or `skipped`. + These are the **ready set**. + b. Invoke all stages in the ready set. Stages with no unresolved predecessors may run + in parallel. + c. Collect stage reports and mark each stage `ready`, `skipped`, or `blocked`. + d. Evaluate gate and hitl policy. Pause for user approval where required before continuing. +1. Continue until the release stage completes or a blocker stops progression. When invoking a worker stage, require this structured stage report at the end: @@ -69,7 +90,9 @@ When invoking a worker stage, require this structured stage report at the end: ## success criteria -- Stage order follows configured workflow. +- Dependency graph was evaluated before each stage transition. +- All ready stages ran before each gate boundary advanced. +- Independent branches ran in parallel where `depends_on` permitted. - Gate progression decisions are explicit and auditable. - User always understands current stage and next action. @@ -92,7 +115,9 @@ that requires changes to upstream items, flag it and trigger a reverse handoff. ## completion checklist -- Workflow stages were evaluated in declared order. +- Dependency graph was evaluated; stages ran only after all predecessors were complete. +- All ready stages were identified before advancing past each gate. +- Independent branches ran in parallel where `depends_on` permitted. - Each stage has a clear outcome (`ready`, `blocked`, or `skipped`). - User approval points were respected. - Final summary includes completed work and pending actions. diff --git a/src/vstack/_templates/agents/product/template.md b/src/vstack/_templates/agents/product/template.md index d95e31a1..1878ce59 100644 --- a/src/vstack/_templates/agents/product/template.md +++ b/src/vstack/_templates/agents/product/template.md @@ -37,6 +37,14 @@ You are a **senior product manager** acting as the **product role**. You define - Prefer small, reviewable scope slices over broad ambiguous deliveries. - Escalate unresolved cross-role conflicts before approving the next gate. +## parallel delegation + +- If discovery naturally separates into independent tracks, you may split work across subagents or same-role variants. +- Good split candidates include vision, requirements, roadmap shaping, and release-scope analysis when they can be merged back into one acceptance story. +- Only split when the tracks are independent enough to avoid contradictory scope decisions. +- Do not split the final acceptance decision or any scope slice that requires a single integrated product judgment. +- Keep the merge point explicit so downstream roles receive one coherent baseline. + ## communication style - Be concise, explicit, and decision-oriented. diff --git a/src/vstack/_templates/agents/release/template.md b/src/vstack/_templates/agents/release/template.md index 41ab7bea..b8574bba 100644 --- a/src/vstack/_templates/agents/release/template.md +++ b/src/vstack/_templates/agents/release/template.md @@ -37,6 +37,14 @@ You are a **senior platform and release engineer** acting as the **release role* - Treat contradictory evidence as a blocker until reconciled. - Prioritize auditability and deterministic release records. +## parallel delegation + +- If evidence gathering or sign-off collection can be separated safely, you may split it across subagents or same-role variants. +- Good split candidates include independent baseline checks, artifact validation, and role-perspective review collection when the findings can be merged before the final release decision. +- Only split when the outputs are independent and the final release gate still remains a single coherent decision. +- Do not split the release verdict itself or any activity that would create conflicting acceptance signals. +- Record the merge point explicitly so the release record stays deterministic and auditable. + ## communication style - Gate-oriented and explicit about pass/fail state. diff --git a/src/vstack/_templates/agents/tester/template.md b/src/vstack/_templates/agents/tester/template.md index dee6fe2b..7d480141 100644 --- a/src/vstack/_templates/agents/tester/template.md +++ b/src/vstack/_templates/agents/tester/template.md @@ -38,6 +38,14 @@ You are a **senior QA, security, and reliability engineer** acting as the **test - Escalate immediately when required evidence cannot be produced. - Use explicit go/no-go language for release readiness. +## parallel delegation + +- If the verification scope spans independent dimensions, you may split the work into specialized subagents and run them in parallel. +- Good split candidates include security, performance, functional correctness, compatibility, and regression checks when those areas do not share critical setup or state. +- Only split when each subagent has a clearly bounded context and the results can be merged into one verdict. +- Do not split narrow or tightly coupled test scopes; the coordination overhead will outweigh the benefit. +- Make the subagent context explicit in the report so the merge step is reproducible. + ## communication style - Clear verdicts with severity and reproduction steps. diff --git a/src/vstack/_templates/project/.vstack/config.yaml b/src/vstack/_templates/project/.vstack/config.yaml index 77b01103..048e3404 100644 --- a/src/vstack/_templates/project/.vstack/config.yaml +++ b/src/vstack/_templates/project/.vstack/config.yaml @@ -135,9 +135,12 @@ # # Execution order: # - Stage order in workflow.stages is the canonical progression order. -# - agentic is stage-sequential by default (planner advances per stage in order). -# - Parallel work can still happen inside a stage (for independent subtasks), -# but cross-stage progression remains ordered by workflow.stages. +# - Without depends_on, each stage implicitly depends on the previous stage +# (fully sequential, backward-compatible behavior). +# - Set depends_on explicitly to enable DAG orchestration and parallel branches. +# Example: architect and designer can both depend on product. +# - Use depends_on: [] to make a stage an independent root. +# - In agentic mode planner can evaluate ready stages from dependency completion. # # Handoffs: # - handoffs.prompt is the message used when transitioning to the next stage. @@ -156,6 +159,7 @@ # gate: required — stage always runs # gate: optional — stage may be skipped when its domain is unaffected # gate: skip — stage is never executed (explicit opt-out) +# depends_on: [] — optional list of prerequisite stage roles # # hitl: always — pipeline pauses for human approval before handoff (default for required) # hitl: on-change — pipeline pauses only when the stage made changes (default for optional) @@ -188,15 +192,20 @@ workflow: - role: architect gate: required hitl: always + depends_on: [product] - role: designer gate: optional hitl: on-change + depends_on: [product] - role: engineer gate: required hitl: always + depends_on: [architect, designer] - role: tester gate: required hitl: always + depends_on: [engineer] - role: release gate: required hitl: always + depends_on: [tester] diff --git a/src/vstack/agents/generator.py b/src/vstack/agents/generator.py index 592053a6..162eaaa6 100644 --- a/src/vstack/agents/generator.py +++ b/src/vstack/agents/generator.py @@ -260,16 +260,37 @@ def _resolve_handoffs(self, agent_role: str, handoff_prompt: str) -> list[dict[s } ] - roles = [s["role"] for s in self.workflow_stages] - try: - idx = roles.index(agent_role) - except ValueError: - return [] - if idx >= len(roles) - 1: + stage_by_role = { + str(stage.get("role", "")).strip(): stage for stage in self.workflow_stages + } + if agent_role not in stage_by_role: return [] - next_role = roles[idx + 1] - stage_handoffs: list[dict[str, str]] = self.workflow_stages[idx].get("handoffs", []) + dependencies_by_role: dict[str, list[str]] = {} + ordered_roles = [str(stage.get("role", "")).strip() for stage in self.workflow_stages] + role_set = set(ordered_roles) + for index, role in enumerate(ordered_roles): + stage = stage_by_role.get(role, {}) + if "depends_on" in stage: + raw_depends_on = stage.get("depends_on", []) + depends_on = raw_depends_on if isinstance(raw_depends_on, list) else [] + normalized = [] + for dep in depends_on: + dep_role = str(dep).strip() + if dep_role and dep_role in role_set and dep_role not in normalized: + normalized.append(dep_role) + dependencies_by_role[role] = normalized + elif index > 0: + dependencies_by_role[role] = [ordered_roles[index - 1]] + else: + dependencies_by_role[role] = [] + + next_roles = [ + role for role in ordered_roles if agent_role in dependencies_by_role.get(role, []) + ] + primary_next_role = next_roles[0] if next_roles else "" + + stage_handoffs: list[dict[str, str]] = stage_by_role[agent_role].get("handoffs", []) if not isinstance(stage_handoffs, list): stage_handoffs = [] @@ -278,12 +299,15 @@ def _resolve_handoffs(self, agent_role: str, handoff_prompt: str) -> list[dict[s if not stage_handoffs: if not handoff_prompt.strip(): return [] + if not next_roles: + return [] return [ { "label": f"Go to next stage: {next_role.capitalize()}", "agent": next_role, "prompt": handoff_prompt.strip(), } + for next_role in next_roles ] result: list[dict[str, str]] = [] @@ -293,7 +317,9 @@ def _resolve_handoffs(self, agent_role: str, handoff_prompt: str) -> list[dict[s if not isinstance(h, dict): continue h_agent = str(h.get("agent", "") or "").strip() - target_agent = h_agent or next_role + target_agent = h_agent or primary_next_role + if not target_agent: + continue # Apply per-agent handoff_prompt override to the first handoff that # targets the natural next stage (no explicit agent override). diff --git a/src/vstack/cli/interface.py b/src/vstack/cli/interface.py index afcf2445..c2e25630 100644 --- a/src/vstack/cli/interface.py +++ b/src/vstack/cli/interface.py @@ -192,9 +192,10 @@ def _read_workflow_stages(install_dir: Path | None) -> list[dict]: contains no valid stages. Each returned dict has at minimum a ``role`` key. Optional keys are - ``gate``, ``hitl``, and ``handoffs`` (a list of handoff dicts, each with - at minimum a ``prompt`` key and optionally ``agent`` and ``label`` - overrides). Unknown extra keys in the stage dict are silently ignored. + ``gate``, ``hitl``, ``depends_on``, and ``handoffs`` (a list of handoff + dicts, each with at minimum a ``prompt`` key and optionally ``agent`` + and ``label`` overrides). Unknown extra keys in the stage dict are + silently ignored. """ if install_dir is None: return [] @@ -209,19 +210,205 @@ def _read_workflow_stages(install_dir: Path | None) -> list[dict]: if not isinstance(stages_raw, list): return [] result: list[dict] = [] - for item in stages_raw: + for stage_index, item in enumerate(stages_raw): if isinstance(item, dict) and isinstance(item.get("role"), str): handoffs = CommandLineInterface._parse_stage_handoffs(item) + normalized_role = item["role"].strip() stage: dict = { - "role": item["role"], + "role": normalized_role, "gate": str(item.get("gate", "required")), "handoffs": handoffs, } if "hitl" in item: stage["hitl"] = str(item["hitl"]) + if "depends_on" in item: + raw_depends_on = item.get("depends_on", []) + if not isinstance(raw_depends_on, list): + raise ValueError( + "Invalid workflow config: " + f"workflow.stages[{stage_index}].depends_on must be a list" + ) + depends_on: list[str] = [] + for dep_index, dep in enumerate(raw_depends_on): + if not isinstance(dep, str): + raise ValueError( + "Invalid workflow config: " + f"workflow.stages[{stage_index}].depends_on[{dep_index}] must be a string" + ) + dep_name = dep.strip() + if dep_name and dep_name not in depends_on: + depends_on.append(dep_name) + stage["depends_on"] = depends_on result.append(stage) + CommandLineInterface._validate_workflow_stages(result) return result + @staticmethod + def _validate_workflow_stages(stages: list[dict]) -> None: + """Validate parsed workflow stages for consistency and graph safety. + + Validation rules: + + - Every stage role must be a non-empty string after trimming. + - Stage roles must be unique. + - Explicit handoff targets (``handoffs[].agent``) must reference an + existing stage role. + - The implied workflow graph must be acyclic. + + The graph includes explicit handoff edges and implicit sequential + fallback edges used when a stage has no explicit handoffs. + """ + if not stages: + return + + role_to_index: dict[str, int] = {} + roles: list[str] = [] + for index, stage in enumerate(stages): + role = str(stage.get("role", "")).strip() + if not role: + raise ValueError( + f"Invalid workflow config: workflow.stages[{index}].role must be a non-empty string" + ) + if role in role_to_index: + previous_index = role_to_index[role] + raise ValueError( + "Invalid workflow config: duplicate stage role " + f"'{role}' at workflow.stages[{index}] " + f"(already defined at workflow.stages[{previous_index}])" + ) + role_to_index[role] = index + roles.append(role) + + role_set = set(roles) + dependencies_by_role: dict[str, list[str]] = {} + edges: dict[str, set[str]] = {role: set() for role in roles} + + for stage_index, stage in enumerate(stages): + stage_role = roles[stage_index] + if "depends_on" in stage: + raw_depends_on = stage.get("depends_on", []) + if not isinstance(raw_depends_on, list): + raise ValueError( + "Invalid workflow config: " + f"workflow.stages[{stage_index}].depends_on must be a list" + ) + depends_on = raw_depends_on + else: + # Backward compatibility: without depends_on, keep canonical + # sequential dependency semantics. + depends_on = [roles[stage_index - 1]] if stage_index > 0 else [] + + normalized_depends_on: list[str] = [] + for dep_index, dep in enumerate(depends_on): + if not isinstance(dep, str): + raise ValueError( + "Invalid workflow config: " + f"workflow.stages[{stage_index}].depends_on[{dep_index}] must be a string" + ) + dep_role = str(dep).strip() + if not dep_role: + continue + if dep_role not in role_set: + raise ValueError( + "Invalid workflow config: " + f"workflow.stages[{stage_index}].depends_on[{dep_index}] " + f"references unknown stage role '{dep_role}'" + ) + if dep_role == stage_role: + raise ValueError( + "Invalid workflow config: " + f"workflow.stages[{stage_index}].depends_on[{dep_index}] " + "cannot reference the same stage role" + ) + if dep_role not in normalized_depends_on: + normalized_depends_on.append(dep_role) + + dependencies_by_role[stage_role] = normalized_depends_on + for dep_role in normalized_depends_on: + edges[dep_role].add(stage_role) + + dependents_by_role: dict[str, list[str]] = {role: [] for role in roles} + for role in roles: + for dependent in roles: + if role in dependencies_by_role.get(dependent, []): + dependents_by_role[role].append(dependent) + + for stage_index, stage in enumerate(stages): + source_role = roles[stage_index] + next_roles = dependents_by_role.get(source_role, []) + primary_next_role = next_roles[0] if next_roles else "" + raw_handoffs = stage.get("handoffs", []) + handoffs = raw_handoffs if isinstance(raw_handoffs, list) else [] + + if not handoffs: + for next_role in next_roles: + edges[source_role].add(next_role) + continue + + for handoff_index, handoff in enumerate(handoffs): + if not isinstance(handoff, dict): + continue + + prompt = str(handoff.get("prompt", "") or "").strip() + target_override = str(handoff.get("agent", "") or "").strip() + + if target_override and target_override not in role_set: + raise ValueError( + "Invalid workflow config: " + f"workflow.stages[{stage_index}].handoffs[{handoff_index}].agent " + f"references unknown stage role '{target_override}'" + ) + + # Empty prompts do not generate handoff edges at runtime. + if not prompt: + continue + + target_role = target_override or primary_next_role + if target_role: + edges[source_role].add(target_role) + + cycle = CommandLineInterface._find_workflow_cycle(edges) + if cycle: + cycle_path = " -> ".join(cycle) + raise ValueError( + f"Invalid workflow config: cycle detected in workflow graph: {cycle_path}" + ) + + @staticmethod + def _find_workflow_cycle(edges: dict[str, set[str]]) -> list[str]: + """Return a cycle path when the workflow graph contains a cycle.""" + unvisited = 0 + visiting = 1 + visited = 2 + + state: dict[str, int] = {node: unvisited for node in edges} + path_stack: list[str] = [] + + def visit(node: str) -> list[str] | None: + state[node] = visiting + path_stack.append(node) + + for neighbor in edges.get(node, set()): + neighbor_state = state.get(neighbor, unvisited) + if neighbor_state == visiting: + start_index = path_stack.index(neighbor) + return path_stack[start_index:] + [neighbor] + if neighbor_state == unvisited: + cycle = visit(neighbor) + if cycle is not None: + return cycle + + path_stack.pop() + state[node] = visited + return None + + for node in edges: + if state[node] == unvisited: + cycle = visit(node) + if cycle is not None: + return cycle + return [] + @staticmethod def _read_workflow_mode(install_dir: Path | None) -> str: """Read ``workflow.mode`` from ``.vstack/config.yaml`` when available. diff --git a/src/vstack/constants.py b/src/vstack/constants.py index 84723988..f476265a 100644 --- a/src/vstack/constants.py +++ b/src/vstack/constants.py @@ -71,9 +71,46 @@ def _head_semver_tag() -> str | None: return max(tags, key=_version_tuple) +def _nearest_semver_tag() -> str | None: + """Return the nearest plain semver tag reachable from HEAD, if available. + + Uses ``git describe --tags --abbrev=0`` to find the most recent reachable + tag on any branch, not just tags that point exactly at HEAD. This gives + a useful version string in development checkouts where HEAD is ahead of + the last release tag. + + Only runs git inside the vstack source checkout. + """ + repo_root = _vstack_repo_root() + if repo_root is None: + return None + try: + out = subprocess.check_output( # nosec B603 B607 + ["git", "-C", str(repo_root), "describe", "--tags", "--abbrev=0"], + stderr=subprocess.DEVNULL, + text=True, + ).strip() + except (FileNotFoundError, subprocess.CalledProcessError): + return None + + return out if _SEMVER_TAG_RE.fullmatch(out) else None + + def _resolve_version() -> str: - """Resolve the package version from git tags, package metadata, or fallback.""" - version = _head_semver_tag() or "" + """Resolve the package version from git tags, package metadata, or fallback. + + Resolution order: + + 1. Exact semver tag on HEAD — used on release commits in the source checkout. + 2. Nearest reachable semver tag (``git describe``) — gives the base version + on development branches where HEAD is ahead of the last release. + 3. Installed package metadata — used when running from a built distribution + (``pip install vstack``) where ``poetry-dynamic-versioning`` has already + embedded the real version string. + 4. Hard-coded ``"0.0.0"`` fallback — shallow clones, untagged repos, or + environments where neither git nor package metadata is available. + """ + version = _head_semver_tag() or _nearest_semver_tag() or "" if version: return version diff --git a/tests/vstack/agents/test_generator.py b/tests/vstack/agents/test_generator.py index 42785456..0989238b 100644 --- a/tests/vstack/agents/test_generator.py +++ b/tests/vstack/agents/test_generator.py @@ -519,6 +519,81 @@ def test_non_dict_handoff_entry_is_skipped(self) -> None: assert len(result) == 1 assert result[0]["agent"] == "tester" + def test_depends_on_branching_falls_back_to_all_dependents(self) -> None: + """Without explicit handoffs, DAG branching returns one fallback handoff per dependent.""" + stages: list[dict[str, Any]] = [ + {"role": "product", "gate": "required", "handoffs": []}, + { + "role": "architect", + "gate": "required", + "depends_on": ["product"], + "handoffs": [], + }, + { + "role": "designer", + "gate": "required", + "depends_on": ["product"], + "handoffs": [], + }, + ] + gen = AgentGenerator(workflow_stages=stages, workflow_mode="manual") + result = gen._resolve_handoffs("product", "Proceed.") + assert len(result) == 2 + assert [entry["agent"] for entry in result] == ["architect", "designer"] + + def test_depends_on_uses_primary_dependent_for_implicit_agent(self) -> None: + """Explicit handoff entries without agent default to the first dependent in stage order.""" + stages: list[dict[str, Any]] = [ + { + "role": "product", + "gate": "required", + "handoffs": [{"prompt": "Go.", "agent": "", "label": ""}], + }, + { + "role": "architect", + "gate": "required", + "depends_on": ["product"], + "handoffs": [], + }, + { + "role": "designer", + "gate": "required", + "depends_on": ["product"], + "handoffs": [], + }, + ] + gen = AgentGenerator(workflow_stages=stages, workflow_mode="manual") + result = gen._resolve_handoffs("product", "") + assert len(result) == 1 + assert result[0]["agent"] == "architect" + + def test_fallback_prompt_returns_empty_when_no_dependent_stage_exists(self) -> None: + """Fallback prompt returns no handoff when the DAG has no downstream dependent stage.""" + stages: list[dict[str, Any]] = [ + {"role": "product", "gate": "required", "handoffs": []}, + { + "role": "architect", + "gate": "required", + "depends_on": [], + "handoffs": [], + }, + ] + gen = AgentGenerator(workflow_stages=stages, workflow_mode="manual") + assert gen._resolve_handoffs("product", "Proceed") == [] + + def test_explicit_handoff_without_agent_is_skipped_without_dependent(self) -> None: + """Implicit target resolution skips explicit handoffs when no primary dependent exists.""" + stages: list[dict[str, Any]] = [ + { + "role": "product", + "gate": "required", + "depends_on": [], + "handoffs": [{"prompt": "Go.", "agent": "", "label": ""}], + } + ] + gen = AgentGenerator(workflow_stages=stages, workflow_mode="manual") + assert gen._resolve_handoffs("product", "") == [] + class TestBuildHandoffs: """Tests for AgentGenerator._build_handoffs (compatibility shim).""" diff --git a/tests/vstack/cli/test_interface.py b/tests/vstack/cli/test_interface.py index c9df9f41..d326469b 100644 --- a/tests/vstack/cli/test_interface.py +++ b/tests/vstack/cli/test_interface.py @@ -436,6 +436,260 @@ def test_returns_empty_when_stages_not_a_list(self, tmp_path: Path) -> None: result = CommandLineInterface._read_workflow_stages(tmp_path / ".github") assert result == [] + def test_parses_depends_on_list_when_present(self, tmp_path: Path) -> None: + """depends_on is parsed as a normalized string list when configured.""" + vstack_dir = tmp_path / ".vstack" + vstack_dir.mkdir() + (vstack_dir / "config.yaml").write_text( + "workflow:\n" + " stages:\n" + " - role: product\n" + " - role: designer\n" + " depends_on:\n" + " - product\n" + " - ' product '\n" + " - ''\n", + encoding="utf-8", + ) + + result = CommandLineInterface._read_workflow_stages(tmp_path / ".github") + assert result == [ + {"role": "product", "gate": "required", "handoffs": []}, + { + "role": "designer", + "gate": "required", + "handoffs": [], + "depends_on": ["product"], + }, + ] + + def test_raises_when_depends_on_is_not_a_list(self, tmp_path: Path) -> None: + """Scalar depends_on values fail fast instead of being treated as empty.""" + vstack_dir = tmp_path / ".vstack" + vstack_dir.mkdir() + (vstack_dir / "config.yaml").write_text( + "workflow:\n" + " stages:\n" + " - role: product\n" + " - role: designer\n" + " depends_on: product\n", + encoding="utf-8", + ) + + with pytest.raises(ValueError, match=r"depends_on must be a list"): + CommandLineInterface._read_workflow_stages(tmp_path / ".github") + + def test_raises_when_depends_on_contains_non_string_entry(self, tmp_path: Path) -> None: + """Mixed-type depends_on lists are rejected instead of being partially ignored.""" + vstack_dir = tmp_path / ".vstack" + vstack_dir.mkdir() + (vstack_dir / "config.yaml").write_text( + "workflow:\n" + " stages:\n" + " - role: product\n" + " - role: designer\n" + " depends_on:\n" + " - product\n" + " - 123\n", + encoding="utf-8", + ) + + with pytest.raises(ValueError, match=r"depends_on\[1\].*must be a string"): + CommandLineInterface._read_workflow_stages(tmp_path / ".github") + + def test_raises_when_stage_role_is_blank(self, tmp_path: Path) -> None: + """Blank stage role values fail with an actionable validation error.""" + vstack_dir = tmp_path / ".vstack" + vstack_dir.mkdir() + (vstack_dir / "config.yaml").write_text( + "workflow:\n stages:\n - role: ' '\n", + encoding="utf-8", + ) + + with pytest.raises(ValueError, match=r"workflow\.stages\[0\]\.role"): + CommandLineInterface._read_workflow_stages(tmp_path / ".github") + + def test_raises_when_stage_roles_are_duplicated(self, tmp_path: Path) -> None: + """Duplicate stage roles are rejected to avoid ambiguous graph edges.""" + vstack_dir = tmp_path / ".vstack" + vstack_dir.mkdir() + (vstack_dir / "config.yaml").write_text( + "workflow:\n stages:\n - role: product\n - role: product\n", + encoding="utf-8", + ) + + with pytest.raises(ValueError, match="duplicate stage role 'product'"): + CommandLineInterface._read_workflow_stages(tmp_path / ".github") + + def test_raises_when_handoff_targets_unknown_stage(self, tmp_path: Path) -> None: + """Unknown explicit handoff agent targets are rejected.""" + vstack_dir = tmp_path / ".vstack" + vstack_dir.mkdir() + (vstack_dir / "config.yaml").write_text( + "workflow:\n" + " stages:\n" + " - role: product\n" + " handoffs:\n" + " prompt: Next\n" + " agent: nonexistent\n" + " - role: architect\n", + encoding="utf-8", + ) + + with pytest.raises(ValueError, match=r"references unknown stage role 'nonexistent'"): + CommandLineInterface._read_workflow_stages(tmp_path / ".github") + + def test_raises_when_workflow_graph_contains_cycle(self, tmp_path: Path) -> None: + """Cycle detection rejects workflow handoff graphs that loop.""" + vstack_dir = tmp_path / ".vstack" + vstack_dir.mkdir() + (vstack_dir / "config.yaml").write_text( + "workflow:\n" + " stages:\n" + " - role: product\n" + " handoffs:\n" + " prompt: To architect\n" + " agent: architect\n" + " - role: architect\n" + " handoffs:\n" + " prompt: Back to product\n" + " agent: product\n", + encoding="utf-8", + ) + + with pytest.raises(ValueError, match=r"cycle detected in workflow graph"): + CommandLineInterface._read_workflow_stages(tmp_path / ".github") + + def test_raises_when_depends_on_references_unknown_stage(self, tmp_path: Path) -> None: + """depends_on entries must reference known stage roles.""" + vstack_dir = tmp_path / ".vstack" + vstack_dir.mkdir() + (vstack_dir / "config.yaml").write_text( + "workflow:\n" + " stages:\n" + " - role: product\n" + " - role: engineer\n" + " depends_on:\n" + " - missing\n", + encoding="utf-8", + ) + + with pytest.raises(ValueError, match=r"depends_on\[0\].*unknown stage role 'missing'"): + CommandLineInterface._read_workflow_stages(tmp_path / ".github") + + def test_depends_on_error_reports_raw_yaml_stage_index(self, tmp_path: Path) -> None: + """depends_on shape errors use the YAML stage position even when earlier stages are skipped.""" + vstack_dir = tmp_path / ".vstack" + vstack_dir.mkdir() + (vstack_dir / "config.yaml").write_text( + "workflow:\n" + " stages:\n" + " - role: 123\n" + " - role: engineer\n" + " depends_on: product\n", + encoding="utf-8", + ) + + with pytest.raises(ValueError, match=r"workflow\.stages\[1\]\.depends_on must be a list"): + CommandLineInterface._read_workflow_stages(tmp_path / ".github") + + def test_raises_when_depends_on_self_reference(self, tmp_path: Path) -> None: + """depends_on entries cannot reference the stage itself.""" + vstack_dir = tmp_path / ".vstack" + vstack_dir.mkdir() + (vstack_dir / "config.yaml").write_text( + "workflow:\n stages:\n - role: tester\n depends_on:\n - tester\n", + encoding="utf-8", + ) + + with pytest.raises(ValueError, match=r"depends_on\[0\].*cannot reference the same stage"): + CommandLineInterface._read_workflow_stages(tmp_path / ".github") + + def test_raises_when_validate_sees_non_string_depends_on_entry(self) -> None: + """Validator rejects malformed non-string depends_on values.""" + with pytest.raises(ValueError, match=r"depends_on\[0\].*must be a string"): + CommandLineInterface._validate_workflow_stages( + [ + { + "role": "product", + "handoffs": [], + "depends_on": [123], + }, + ] + ) + + def test_raises_when_depends_on_graph_contains_cycle(self, tmp_path: Path) -> None: + """Dependency cycles in depends_on are rejected.""" + vstack_dir = tmp_path / ".vstack" + vstack_dir.mkdir() + (vstack_dir / "config.yaml").write_text( + "workflow:\n" + " stages:\n" + " - role: product\n" + " depends_on:\n" + " - architect\n" + " - role: architect\n" + " depends_on:\n" + " - product\n", + encoding="utf-8", + ) + + with pytest.raises(ValueError, match=r"cycle detected in workflow graph"): + CommandLineInterface._read_workflow_stages(tmp_path / ".github") + + +class TestValidateWorkflowStages: + """Tests for CommandLineInterface._validate_workflow_stages.""" + + def test_accepts_linear_workflow_without_explicit_handoffs(self) -> None: + """A plain stage list without handoffs is treated as a valid acyclic flow.""" + CommandLineInterface._validate_workflow_stages( + [ + {"role": "product", "handoffs": []}, + {"role": "architect", "handoffs": []}, + ] + ) + + def test_ignores_non_dict_handoff_entries(self) -> None: + """Non-dict handoff entries are ignored without failing validation.""" + CommandLineInterface._validate_workflow_stages( + [ + {"role": "product", "handoffs": ["invalid-entry"]}, + {"role": "architect", "handoffs": []}, + ] + ) + + def test_ignores_empty_prompt_handoff_edges(self) -> None: + """Handoffs with blank prompts do not contribute graph edges.""" + CommandLineInterface._validate_workflow_stages( + [ + { + "role": "product", + "handoffs": [{"prompt": " ", "agent": "architect", "label": ""}], + }, + {"role": "architect", "handoffs": []}, + ] + ) + + def test_ignores_blank_depends_on_entries(self) -> None: + """Blank depends_on entries are ignored during dependency normalization.""" + CommandLineInterface._validate_workflow_stages( + [ + {"role": "product", "handoffs": []}, + {"role": "architect", "handoffs": [], "depends_on": [" ", "product"]}, + ] + ) + + def test_raises_when_validate_sees_scalar_depends_on(self) -> None: + """Validator rejects malformed scalar depends_on values.""" + with pytest.raises(ValueError, match=r"depends_on must be a list"): + CommandLineInterface._validate_workflow_stages( + [ + {"role": "product", "handoffs": []}, + {"role": "architect", "handoffs": [], "depends_on": "product"}, + ] + ) + class TestReadWorkflowMode: """Tests for CommandLineInterface._read_workflow_mode.""" diff --git a/tests/vstack/test_constants.py b/tests/vstack/test_constants.py index abe17494..fa666b1b 100644 --- a/tests/vstack/test_constants.py +++ b/tests/vstack/test_constants.py @@ -80,22 +80,71 @@ def test_head_semver_tag_returns_highest_plain_semver(self, monkeypatch) -> None assert constants_module._head_semver_tag() == "1.0.2" - def test_version_uses_installed_package_metadata_when_git_tag_missing( + def test_nearest_semver_tag_returns_none_outside_vstack_repo(self, monkeypatch) -> None: + """Test that git is not consulted for nearest tag outside the vstack source tree.""" + monkeypatch.setattr(constants_module, "_vstack_repo_root", lambda: None) + called = [] + + def _record_call(*_args, **_kwargs) -> str: + called.append(1) + return "" + + monkeypatch.setattr(subprocess, "check_output", _record_call) + + assert constants_module._nearest_semver_tag() is None + assert not called, "git must not be invoked outside the vstack repo" + + def test_nearest_semver_tag_returns_none_when_git_describe_fails(self, monkeypatch) -> None: + """Test that a failing git describe returns None cleanly.""" + monkeypatch.setattr(constants_module, "_vstack_repo_root", lambda: Path("/fake/root")) + + def _raise(*_args, **_kwargs) -> str: + raise subprocess.CalledProcessError(returncode=128, cmd=["git", "describe"]) + + monkeypatch.setattr(subprocess, "check_output", _raise) + + assert constants_module._nearest_semver_tag() is None + + def test_nearest_semver_tag_returns_none_for_non_semver_tag(self, monkeypatch) -> None: + """Test that a non-semver tag from git describe is ignored.""" + monkeypatch.setattr(constants_module, "_vstack_repo_root", lambda: Path("/fake/root")) + monkeypatch.setattr(subprocess, "check_output", lambda *_a, **_kw: "v1.2.3\n") + + assert constants_module._nearest_semver_tag() is None + + def test_nearest_semver_tag_returns_plain_semver(self, monkeypatch) -> None: + """Test that a plain semver tag from git describe is returned.""" + monkeypatch.setattr(constants_module, "_vstack_repo_root", lambda: Path("/fake/root")) + monkeypatch.setattr(subprocess, "check_output", lambda *_a, **_kw: "3.1.1\n") + + assert constants_module._nearest_semver_tag() == "3.1.1" + + def test_version_uses_installed_package_metadata_when_git_tags_missing( self, monkeypatch ) -> None: - """Test that installed metadata is used when no semver tag points at HEAD.""" + """Test that installed metadata is used when no semver tag is reachable.""" monkeypatch.setattr(constants_module, "_head_semver_tag", lambda: None) + monkeypatch.setattr(constants_module, "_nearest_semver_tag", lambda: None) monkeypatch.setattr(constants_module, "_pkg_version", lambda _name: "1.0.1") assert constants_module._resolve_version() == "1.0.1" def test_version_prefers_head_semver_tag(self, monkeypatch) -> None: - """Test that a semver tag on HEAD wins over package metadata.""" + """Test that a semver tag on HEAD wins over nearest tag and package metadata.""" monkeypatch.setattr(constants_module, "_head_semver_tag", lambda: "1.0.1") + monkeypatch.setattr(constants_module, "_nearest_semver_tag", lambda: "1.0.0") monkeypatch.setattr(constants_module, "_pkg_version", lambda _name: "9.9.9") assert constants_module._resolve_version() == "1.0.1" + def test_version_uses_nearest_tag_when_head_has_no_exact_tag(self, monkeypatch) -> None: + """Test that nearest reachable tag is used when HEAD has no exact semver tag.""" + monkeypatch.setattr(constants_module, "_head_semver_tag", lambda: None) + monkeypatch.setattr(constants_module, "_nearest_semver_tag", lambda: "3.1.1") + monkeypatch.setattr(constants_module, "_pkg_version", lambda _name: "9.9.9") + + assert constants_module._resolve_version() == "3.1.1" + def test_version_fallback_when_package_metadata_missing(self, monkeypatch) -> None: """Test that version fallback when git and package metadata are unavailable.""" @@ -104,6 +153,7 @@ def _raise_not_found(_: str) -> str: raise importlib_metadata.PackageNotFoundError monkeypatch.setattr(constants_module, "_head_semver_tag", lambda: None) + monkeypatch.setattr(constants_module, "_nearest_semver_tag", lambda: None) monkeypatch.setattr(constants_module, "_pkg_version", _raise_not_found) assert constants_module._resolve_version() == "0.0.0"