Validate Artefacts
Purpose / When to Activate
Activate:
- After generating Epics
- After generating Stories
- Before any Delivery planning or execution
This is the mandatory gate between Discovery and Delivery. No Story proceeds to Delivery without passing this check.
Process
Epic Validation
- Expresses a user outcome (not a feature or technical task)
- Aligns with product direction
- Avoids technical implementation detail
- Clearly scoped with no hidden assumptions
Story Validation
- Maps to a parent Epic
- Includes measurable acceptance criteria
- Is unambiguous and executable
- Respects governance rules
- Avoids solution design
- Has
related_decsfield in frontmatter (list or explicit empty[]) - Has
skills_invokedfield in frontmatter (must list the skill IDs that were read to produce it) required_skillsfield is optional. If present, it must be a YAML list (e.g.required_skills: [name-a, name-b]). A bare string, map, or other non-list type returns BLOCKED:required_skills must be a YAML list (got: <type>).- Each string entry in
required_skillsmust resolve to an existing custom skill via the project skills index (.gaai/project/skills/skills-index.yaml) or directly as.gaai/project/skills/<track>/<name>/SKILL.md. A non-resolving entry returns BLOCKED:required_skills entry '<name>' not found in project skills. - Each entry in
required_skillsmust have at least one AC in the story body that names the observable output of that skill. An entry with no co-declared output AC returns BLOCKED:required_skills entry '<name>' has no bound output AC — add an AC naming the observable output the skill must produce. - Absence of
required_skills(field missing orrequired_skills: []) does not affect any other validation. Non-regression for stories that do not use custom skills.
Story Scope Pre-Flight (Plan contract symmetry)
The Plan phase agent enforces hard scope caps before producing an execution plan:
>10 distinct files modified OR created, >6 acceptance criteria, or >300 lines of implementation code projected all trigger a plan-block + decomposition demand and waste a full Delivery cycle (worktree spin, branch chore commit, then PLAN exit non-zero with plan-blocked.md). To prevent this Discovery → Refined → Plan-blocked round-trip, this skill enforces the same caps at the Discovery gate. The Refined contract MUST guarantee Plan-feasibility upstream.
LOC counting basis (coordinated decision — mirrored verbatim in the Plan agent prompt; change both together or neither): the ≤300 LOC cap counts implementation LOC only; test-file LOC is excluded. Test files still count toward the ≤10-file cap and must appear in the File Inventory marked (test). Basis rationale: the cap is calibrated on single-pass implementation coherence; test code mirrors the implementation it covers, so a tests-included basis would make any properly-tested new service unshippable (250-line service + ~280-line test always exceeds 300 combined) while empirical deliveries (e.g. a 246-impl/298-test billing story) pass reliably. Grossly disproportionate test scope (>3× impl LOC) is flagged for review, not blocked. A test file is one containing only automated test code and test-only fixtures/helpers, following the project's test naming convention (e.g. *.test.*, *.spec.*, tests/ or __tests__/ directories). Any file imported or executed by production code counts as implementation regardless of name or marker.
Cap enforcement is hard, not advisory.
| Cap | Threshold | Source of truth in story |
|---|---|---|
| Files | ≤ 10 distinct files modified or created | ## File Inventory section (mandatory for any multi-file story) |
| Acceptance criteria | ≤ 6 | counted from ## Acceptance Criteria bullets |
| LOC projection | ≤ 300 implementation LOC only — rows marked (test) are excluded from the sum |
optional loc_estimate line in File Inventory, summed over non-test rows |
Multi-file detection (when File Inventory is mandatory):
A story requires a ## File Inventory section if BOTH of the following are true (concrete signals — vibe-only triggers produce false-positives on legitimate single-file refactor stories):
- (signal 1 — language) Title or body contains at least one of: "migrate ... to", "centralise", "eliminate ... regex", "scan-and-replace", "audit across", "every consumer", "all sites", "all callers", "across the codebase", or analogous explicit cross-cutting framing
- (signal 2 — concrete references) The story body OR acceptance criteria reference at least 2 distinct file paths (e.g.
path/to/foo.sh+path/to/bar.sh). A single-file refactor mentioning one file fails this signal and is NOT subject to the File Inventory requirement.
When BOTH signals match, the story body MUST contain a ## File Inventory section listing each file to be modified or created, with a 1-line scope note per file, an optional loc_estimate per row, and the literal marker (test) on any test-file row (test rows are excluded from the LOC cap but count toward the file cap). Stories matching the detection criteria without this section are BLOCKED — the validator cannot compute the file cap without an explicit list.
A ## File Inventory listing more than 10 files is BLOCKED. Discovery must decompose before re-running this skill. This is the PLAN contract symmetry — any story that would plan-block at Delivery MUST be caught here instead. There is no escape hatch in V1: the cap mirrors the Plan agent's hard cap exactly, and the Plan agent does not honor exemption flags. Truly atomic cross-file refactors are vanishingly rare ; if encountered, raise the cap in BOTH this skill AND the Plan agent prompt as a single coordinated decision.
Single-file stories (one of the two detection signals does not match) may omit the File Inventory section. AC and LOC caps still apply.
Rationale (why this lives in the Discovery gate, not in Plan only):
The Plan phase agent is the executor — its scope caps protect single-pass implementation reliability. But by the time PLAN runs, the wrapper has already spun a worktree, made a chore commit, and consumed a retry slot. Mirroring the caps here closes the gap: Discovery never produces a Refined story that PLAN would block. Defense-in-depth at the wrapper exit trap (reconcile to status=blocked when plan-blocked.md is detected) catches any edge case this skill misses.
impl_model Field Validation (optional field — E94)
The impl_model field is optional. Stories without it validate exactly as before (non-regression guarantee).
Valid values: ['primary', 'secondary'] — checked as a list lookup, not an if/else chain, so V2 extension (e.g., tertiary) is a one-line change.
| Condition | Verdict |
|---|---|
impl_model absent (story frontmatter or backlog entry) |
PASS — default behavior |
impl_model: primary |
PASS |
impl_model: secondary (any tier) |
PASS — Tier × impl_model hard-gate retired ; daemon hard-gate previously removed ahead of formal decision. Fallback to primary on secondary failure is preserved via the universal-cascade fallback policy. |
Any other value (e.g. tertiary, claude-opus-4-6, "") |
FAIL — impl_model must be 'primary' or 'secondary' (got: '<value>') |
impl_model in frontmatter AND backlog entry with different values |
FAIL — impl_model mismatch: frontmatter=<X>, backlog=<Y> |
Field location rules (AC6 — canonical resolution):
- Canonical source: the backlog entry (
active.backlog.yaml). Delivery readsimpl_modelfrom the backlog at claim time. - Advisory source: story frontmatter (
.story.md). Allows Discovery to author the tag authoritatively. - If only one source is present → accept it.
- If both are present and agree → PASS.
- If both are present and differ → FAIL with mismatch message above.
Tier × impl_model compatibility (skill-side gate retired)
Current routing doctrine :
- Default routing :
impl_modelABSENT →secondarywhen env-configured (elseprimary, OSS non-regression) - Fallback : secondary spawn failure (any class) → primary subprocess via the universal-cascade fallback policy (preserved)
- Tier 2 stories needing primary's stronger reasoning : declare
impl_model: primaryexplicitly per story author judgment. The current decision does NOT auto-coerce Tier 2 to primary (former hard-gate behavior is removed).
This skill now :
- Enforces only the
primary | secondaryvalue list (lines 65-71) - Enforces frontmatter / backlog parity
- No longer pre-blocks any
tier × impl_modelcombination (the prior tier-aware hard-gate is retired)
Setting impl_model: secondary explicitly: always passes. Useful for cost-optimal stories where the author has verified the secondary model's context budget is comfortably sufficient OR is comfortable relying on the universal-cascade fallback.
Setting impl_model: primary explicitly: always passes. Useful for Tier 2+ stories needing stronger reasoning, security/compliance-critical work, or other sensitivity overrides.
Leaving impl_model ABSENT: routes to default (secondary when env-configured else primary). Recommended for Tier 1 stories.
Test fixtures: .gaai/core/skills/discovery/validate-artefacts/tests/impl_model.test.yaml
Cross-checks
- No Story exists without a parent Epic
- No scope contradictions with memory
- No rule violations
- Marketing artefacts (if present): hypothesis statuses align with Story acceptance criteria
- Strategy artefacts (if present): GTM phases align with Epic dependencies and gates
- Epic dependency propagation check: If the parent Epic's
## Dependenciessection lists other Epics, verify that every Story'sdepends_onincludes at least one terminal story from each listed Epic. A phasing constraint in Epic prose that is not encoded in storydepends_onis a FAIL — the daemon cannot enforce prose constraints, onlydepends_onfields.
Skill Attestation (Base Rule #2 Enforcement)
- Every artefact (Epic, Story, PRD) must have a
skills_invokedfield in its frontmatter - Epic artefacts must include
generate-epicsinskills_invoked - Story artefacts must include
generate-storiesinskills_invoked - PRD artefacts must include
create-prdinskills_invoked - An artefact with a missing or empty
skills_invokedfield is an automatic FAIL — the producing agent did not follow Base Rule #2 - This check exists because agents can produce format-correct artefacts from cached knowledge while silently skipping mandatory process steps defined in the skill file
Outputs
Validation Report — Discovery
Epics:
- E01: PASS | FAIL — reason
- E02: PASS | FAIL — reason
Stories:
- S01: PASS | FAIL — reason
- S02: PASS | FAIL — reason
Skill Attestation (Base Rule #2):
- E01: skills_invoked: [generate-epics] ✓ | MISSING ✗
- S01: skills_invoked: [generate-stories] ✓ | MISSING ✗
- S01: related_decs: [<DEC-id>] ✓ | MISSING ✗
Governance:
- rules respected: yes | no
- missing artefacts: none | list
- risks detected: none | list
Overall Status:
PASS | BLOCKED
Blocking Conditions
The skill MUST block progression if:
- Any Story lacks acceptance criteria
- Epics are solution-oriented rather than outcome-oriented
- Scope is unclear or ambiguous
- Governance rules are violated
- Contradictions exist between artefacts
- Any artefact is missing
skills_invokedin frontmatter (Base Rule #2 violation) - Any Story is missing
related_decsin frontmatter - A multi-file story (both detection signals match) is missing the
## File Inventorysection - A
## File Inventorylists more than 10 files (no V1 escape hatch — decompose) - Acceptance criteria count exceeds 6 (Plan contract symmetry)
- A
loc_estimatetotal over non-test File Inventory rows exceeds 300 (Plan contract symmetry — implementation LOC only; test rows are excluded from the cap but MUST still be listed and marked(test), and they count toward the ≤10-file cap) required_skillsentry present without a co-declared output AC in the story body (unbound contract — per the contract-carried delivery invariant)required_skillsentry does not resolve to an existing custom skill in.gaai/project/skills/(referential integrity failure)required_skillsvalue is not a YAML list (malformed field)
No partial approval. No silent warnings.
Non-Goals
This skill must NOT:
- Rewrite artefacts
- Invent missing content
- Make product decisions
- Soften failures
It validates — it does not fix. If Delivery can misunderstand it, Discovery is not done.
On BLOCKED verdict: the Discovery Agent must invoke refine-scope to resolve the identified gaps, then re-run this skill. Do not proceed to Delivery until the verdict is PASS.