Goal Contract Capture
You produce a FlowGoal contract — a durable completion contract that turns acceptance criteria + specification into a machine-readable, file-backed artifact. This skill is the 5th invoker of the existing specification-capture pattern; it wraps that skill (does NOT replace it) and adds the goal-specific fields needed for evaluator/Stop-hook enforcement.
Iron Law
No /flow:goal create without a complete contract. No .flow/goals/<id>.goal.yaml without an evaluator binding. The contract is the source of truth for the Stop hook and /flow:goal evaluate; gaps here cascade into silent premature completion.
Relationship to existing skills
This skill is a wrapper, not a fork:
| Existing skill |
What it gives us |
What this skill adds |
specification-capture |
non-goals, failure modes, interface contracts |
the specification block in the goal YAML |
criterion-verification-map |
per-AC verification commands + expected evidence |
the objective.acceptance_criteria[].verification_command field |
evidence-based-development |
ASSERTION/EVIDENCE/VERIFIED discipline |
the evidence_ref linking the AC back to a FlowEvidence sidecar |
If those skills have already produced their outputs (decision journal manifest entries), this skill reads from them rather than re-asking the user.
Inputs
The invoking command MUST pass:
- Goal id — typically
issue-{N}, pr-{N}-review, pr-{N}-address, or an ad-hoc slug. Validated against the schema's ^[a-z0-9][a-z0-9-]{0,63}$ pattern.
- Scope —
repo, branch, optionally issue and/or pr, optionally a journal path (.decisions/issue-{N}.md).
- Source — typically the GitHub issue body (parsed for AC) or the PR description (parsed for review/address scope).
- Invocation reason —
start | goal-create | review | address. Used to choose the right outcome template and the right evaluator.type.
Outputs
Single file: .flow/goals/<id>.goal.yaml conforming to plugins/flow/schemas/v1/goal.schema.json. Written via bin/flow-goal-record.sh (atomic, O_NOFOLLOW-defended).
Plus: one goal-created artifact appended to the linked decision journal via bin/journal-record.sh --type goal-created --metadata goal_id=<id> --metadata source=<src>.
Workflow
Pre-flight check — refuse if .flow/goals/<id>.goal.yaml already exists and lifecycle.status is not in {cancelled, failed}. Surface the existing goal to the user via the six-field escalation; never silently overwrite an active goal.
Compose metadata:
metadata:
id: <derived-from-invocation>
created_at: <ISO-8601 UTC now>
created_by: <invoking command, e.g. /flow:start>
owner: <git user.email or @me>
Compose scope — from inputs. Set journal to the linked decision journal path. Leave run_id empty until run-state-management wires FlowRun creation.
Compose objective — pull AC text + verification commands from the existing criterion-verification-map output. Each AC is one entry with status: pending and evidence_ref: null initially. The outcome is a one-sentence statement derived from invocation reason:
start → "Issue #{N} is implemented and verified."
goal-create → user-supplied via AskUserQuestion.
review → "PR #{N} review completed with findings posted or no-finding evidence recorded."
address → "All unresolved findings on PR #{N} are resolved, commented, or escalated."
Compose specification — lift from specification-capture's journal entries:
non_goals from the ## Specification > ### Non-goals body
failure_modes from ### Failure modes
interface_contracts from ### Interface contracts
If any block is empty, raise the six-field escalation per references/escalation-format.md — do NOT silently fill with placeholders.
Compose constraints — default to tdd_required: true, require_all_pass: true, no_calendar_estimates: true, no_tier3_without_confirmation: true. Add denied_paths from settings (flow.goals.denied_paths cascade key); add allowed_paths only if the goal narrows the tier model (e.g., a focused refactor).
Compose evaluator:
evaluator:
type: flow_verdict_judge # hybrid for fuzzy criteria; deterministic for command-only
command: /flow:goal evaluate
judge_agent: goal-evaluator-judge
evidence_bundle_format: plugins/flow/references/evidence-bundle-format.md
denied_context:
- implementation_rationale
- self_review_findings
Compose continuation — defaults: mode: flow_managed, on_incomplete: continue_next_activity, on_blocked: six_field_escalation, on_complete: mark_achieved, max_iterations: <from settings, default 20>.
Compose lifecycle — initial state:
lifecycle:
status: active
current_phase: <derived; start→explore, review→fan-out, address→categorize>
current_activity: <first activity id>
turns_evaluated: 0
last_evaluation:
result: incomplete
reason: "Goal created; evidence not yet collected."
at: <now>
Write atomically — call bin/flow-goal-record.sh with the composed YAML. Verify exit 0; surface stderr on any failure.
Record journal artifact — bin/journal-record.sh --issue {N} --type goal-created --metadata goal_id=<id> --metadata source=<src>.
Verify the artifact — read back .flow/goals/<id>.goal.yaml and validate against the schema (Python jsonschema if available). A schema mismatch here means the skill or the schema is broken; fail hard.
Per-invoker scope
| Invoker |
Captures |
Skips |
start |
All blocks; outcome derived from issue title |
nothing |
goal-create |
All blocks; outcome from AskUserQuestion |
journal manifest if no issue |
review |
Outcome + AC (= review checklist) + constraints |
specification (use issue's existing capture) |
address |
Outcome + AC (= one per unresolved PR comment) + constraints |
specification (inherit from review goal) |
Anti-patterns
- ❌ Silently overwriting an active goal — always surface and escalate.
- ❌ Filling in empty specification blocks with placeholders — escalate via six-field.
- ❌ Bypassing
bin/flow-goal-record.sh for "speed" — atomicity + symlink defense matters.
- ❌ Setting
lifecycle.status: achieved at creation — the only way to achieved is through /flow:goal evaluate.
Reuse map
plugins/flow/skills/specification-capture/SKILL.md — read the per-invoker scope table; this skill adds row 5 (goal-create).
plugins/flow/skills/criterion-verification-map/SKILL.md — read for the AC verification-command shape.
plugins/flow/schemas/v1/goal.schema.json — the canonical schema this skill writes against.
plugins/flow/bin/flow-goal-record.sh — atomic writer.
plugins/flow/bin/journal-record.sh — for the goal-created manifest artifact.
plugins/flow/references/escalation-format.md — six-field escalation for any empty/ambiguous block.
1---2name: goal-contract-capture3description: Capture a FlowGoal contract as a project-local `.flow/goals/<id>.goal.yaml` file with outcome, acceptance criteria (with verification commands), specification elements (non-goals, failure modes, interface contracts), constraints, evaluator binding, continuation policy, and lifecycle frontmatter. Use when /flow:start passes the Spec Validation Gate, when /flow:goal create is invoked, or when /flow:review and /flow:address need a completion contract for a PR. This skill MUST be consulted because acceptance criteria alone do not constitute a contract — without an evaluator binding, boundaries, and lifecycle state, downstream phases cannot detect premature completion, the Stop hook cannot enforce evidence, and goals cannot resume across sessions.4---5
6# Goal Contract Capture
7
8You produce a FlowGoal contract — a durable completion contract that turns acceptance criteria + specification into a machine-readable, file-backed artifact. This skill is the 5th invoker of the existing `specification-capture` pattern; it wraps that skill (does NOT replace it) and adds the goal-specific fields needed for evaluator/Stop-hook enforcement.
9
10## Iron Law
11
12**No `/flow:goal create` without a complete contract. No `.flow/goals/<id>.goal.yaml` without an evaluator binding. The contract is the source of truth for the Stop hook and `/flow:goal evaluate`; gaps here cascade into silent premature completion.**
13
14## Relationship to existing skills
15
16This skill is a **wrapper**, not a fork:
17
18| Existing skill | What it gives us | What this skill adds |
19|---|---|---|
20| `specification-capture` | non-goals, failure modes, interface contracts | the `specification` block in the goal YAML |
21| `criterion-verification-map` | per-AC verification commands + expected evidence | the `objective.acceptance_criteria[].verification_command` field |
22| `evidence-based-development` | ASSERTION/EVIDENCE/VERIFIED discipline | the `evidence_ref` linking the AC back to a FlowEvidence sidecar |
23
24If those skills have already produced their outputs (decision journal manifest entries), this skill **reads from them** rather than re-asking the user.
25
26## Inputs
27
28The invoking command MUST pass:
29
301. **Goal id** — typically `issue-{N}`, `pr-{N}-review`, `pr-{N}-address`, or an ad-hoc slug. Validated against the schema's `^[a-z0-9][a-z0-9-]{0,63}$` pattern.
312. **Scope** — `repo`, `branch`, optionally `issue` and/or `pr`, optionally a `journal` path (`.decisions/issue-{N}.md`).
323. **Source** — typically the GitHub issue body (parsed for AC) or the PR description (parsed for review/address scope).
334. **Invocation reason** — `start | goal-create | review | address`. Used to choose the right outcome template and the right `evaluator.type`.
34
35## Outputs
36
37Single file: `.flow/goals/<id>.goal.yaml` conforming to `plugins/flow/schemas/v1/goal.schema.json`. Written via `bin/flow-goal-record.sh` (atomic, O_NOFOLLOW-defended).
38
39Plus: one `goal-created` artifact appended to the linked decision journal via `bin/journal-record.sh --type goal-created --metadata goal_id=<id> --metadata source=<src>`.
40
41## Workflow
42
431. **Pre-flight check** — refuse if `.flow/goals/<id>.goal.yaml` already exists and `lifecycle.status` is not in `{cancelled, failed}`. Surface the existing goal to the user via the six-field escalation; never silently overwrite an active goal.
44
452. **Compose `metadata`**:
46 ```yaml
47 metadata:
48 id: <derived-from-invocation>
49 created_at: <ISO-8601 UTC now>
50 created_by: <invoking command, e.g. /flow:start>
51 owner: <git user.email or @me>
52 ```
53
543. **Compose `scope`** — from inputs. Set `journal` to the linked decision journal path. Leave `run_id` empty until `run-state-management` wires FlowRun creation.
55
564. **Compose `objective`** — pull AC text + verification commands from the existing `criterion-verification-map` output. Each AC is one entry with `status: pending` and `evidence_ref: null` initially. The `outcome` is a one-sentence statement derived from invocation reason:
57 - `start` → "Issue #{N} is implemented and verified."
58 - `goal-create` → user-supplied via AskUserQuestion.
59 - `review` → "PR #{N} review completed with findings posted or no-finding evidence recorded."
60 - `address` → "All unresolved findings on PR #{N} are resolved, commented, or escalated."
61
625. **Compose `specification`** — lift from `specification-capture`'s journal entries:
63 - `non_goals` from the `## Specification > ### Non-goals` body
64 - `failure_modes` from `### Failure modes`
65 - `interface_contracts` from `### Interface contracts`
66
67 If any block is empty, raise the six-field escalation per `references/escalation-format.md` — do NOT silently fill with placeholders.
68
696. **Compose `constraints`** — default to `tdd_required: true`, `require_all_pass: true`, `no_calendar_estimates: true`, `no_tier3_without_confirmation: true`. Add `denied_paths` from settings (`flow.goals.denied_paths` cascade key); add `allowed_paths` only if the goal narrows the tier model (e.g., a focused refactor).
70
717. **Compose `evaluator`**:
72 ```yaml
73 evaluator:
74 type: flow_verdict_judge # hybrid for fuzzy criteria; deterministic for command-only
75 command: /flow:goal evaluate
76 judge_agent: goal-evaluator-judge
77 evidence_bundle_format: plugins/flow/references/evidence-bundle-format.md
78 denied_context:
79 - implementation_rationale
80 - self_review_findings
81 ```
82
838. **Compose `continuation`** — defaults: `mode: flow_managed`, `on_incomplete: continue_next_activity`, `on_blocked: six_field_escalation`, `on_complete: mark_achieved`, `max_iterations: <from settings, default 20>`.
84
859. **Compose `lifecycle`** — initial state:
86 ```yaml
87 lifecycle:
88 status: active
89 current_phase: <derived; start→explore, review→fan-out, address→categorize>
90 current_activity: <first activity id>
91 turns_evaluated: 0
92 last_evaluation:
93 result: incomplete
94 reason: "Goal created; evidence not yet collected."
95 at: <now>
96 ```
97
9810. **Write atomically** — call `bin/flow-goal-record.sh` with the composed YAML. Verify exit 0; surface stderr on any failure.
99
10011. **Record journal artifact** — `bin/journal-record.sh --issue {N} --type goal-created --metadata goal_id=<id> --metadata source=<src>`.
101
10212. **Verify the artifact** — read back `.flow/goals/<id>.goal.yaml` and validate against the schema (Python jsonschema if available). A schema mismatch here means the skill or the schema is broken; fail hard.
103
104## Per-invoker scope
105
106| Invoker | Captures | Skips |
107|---|---|---|
108| `start` | All blocks; outcome derived from issue title | nothing |
109| `goal-create` | All blocks; outcome from AskUserQuestion | journal manifest if no issue |
110| `review` | Outcome + AC (= review checklist) + constraints | specification (use issue's existing capture) |
111| `address` | Outcome + AC (= one per unresolved PR comment) + constraints | specification (inherit from review goal) |
112
113## Anti-patterns
114
115- ❌ Silently overwriting an active goal — always surface and escalate.
116- ❌ Filling in empty specification blocks with placeholders — escalate via six-field.
117- ❌ Bypassing `bin/flow-goal-record.sh` for "speed" — atomicity + symlink defense matters.
118- ❌ Setting `lifecycle.status: achieved` at creation — the only way to `achieved` is through `/flow:goal evaluate`.
119
120## Reuse map
121
122- `plugins/flow/skills/specification-capture/SKILL.md` — read the per-invoker scope table; this skill adds row 5 (`goal-create`).
123- `plugins/flow/skills/criterion-verification-map/SKILL.md` — read for the AC verification-command shape.
124- `plugins/flow/schemas/v1/goal.schema.json` — the canonical schema this skill writes against.
125- `plugins/flow/bin/flow-goal-record.sh` — atomic writer.
126- `plugins/flow/bin/journal-record.sh` — for the `goal-created` manifest artifact.
127- `plugins/flow/references/escalation-format.md` — six-field escalation for any empty/ambiguous block.