Retrospective Schema and Events Reference
This reference documents the retrospective.yaml schema, proposal types, retrospective status
events, and synthesizer exit codes. For how to use the retrospective loop, see
How to Use the Retrospective Learning Loop.
retrospective.yaml schema
Retrospective records are stored at:
.kittify/missions/<mission_id>/retrospective.yaml
The file is keyed by canonical mission ULID, not display number. The schema is implemented by
specify_cli.retrospective.schema.RetrospectiveRecord.
| Field | Type | Required | Description |
|---|---|---|---|
schema_version |
string | Yes | Literal "1" |
mission |
object | Yes | Mission identity: mission_id, mid8, mission_slug, mission_type, timestamps |
mode |
object | Yes | Retrospective mode: autonomous or human_in_command, plus source signal |
status |
string | Yes | Persisted values: completed, skipped, or failed. pending exists in the model but the writer refuses to persist it. |
started_at |
ISO 8601 timestamp | Yes | When retrospective handling started |
completed_at |
ISO 8601 timestamp | Conditional | Required when status: completed |
actor |
object | Yes | Actor that authored the record |
helped |
list[Finding] | No | Artifacts, directives, tactics, tests, or context that helped |
not_helpful |
list[Finding] | No | Targets flagged as not helpful during the mission |
gaps |
list[Finding] | No | Governance or context gaps identified during the mission |
proposals |
list[Proposal] | No | Structured change proposals (see Proposal kinds below) |
provenance |
object | Yes | Record author, runtime version, write timestamp, schema version |
skip_reason |
string | Conditional | Required when status: skipped |
failure |
object | Conditional | Required when status: failed |
successor_mission_id |
ULID string | No | Follow-up mission identity when one is created |
Finding shape
helped, not_helpful, and gaps entries share the same shape:
| Field | Type | Description |
|---|---|---|
id |
string | Finding identifier unique within the record |
target.kind |
string | One of doctrine_directive, doctrine_tactic, doctrine_procedure, drg_edge, drg_node, glossary_term, prompt_template, test, context_artifact |
target.urn |
string | Target URN |
note |
string | Human-readable note, max 2000 characters |
provenance.source_mission_id |
ULID string | Mission that produced the evidence |
provenance.evidence_event_ids |
list[ULID] | Event IDs that support the finding |
provenance.actor |
object | Actor that captured the finding |
provenance.captured_at |
timestamp | Capture timestamp |
Proposal kinds
Each proposal in the proposals list has id, kind, payload, rationale, state, and
provenance. Proposal IDs and evidence event IDs are ULIDs. state.status is one of pending,
accepted, rejected, applied, or superseded.
The synthesizer applies the effective batch: accepted proposal IDs plus every
flag_not_helpful proposal. flag_not_helpful is auto-included in that batch, but no proposal
mutates project state unless the operator passes --apply.
add_glossary_term
Add a new term to the project glossary.
| Field | Required | Description |
|---|---|---|
payload.kind |
Yes | add_glossary_term |
payload.term_key |
Yes | Safe term key, e.g. lifecycle-terminus |
payload.definition |
Yes | Human-readable definition |
payload.definition_hash |
Yes | Hash of the proposed definition |
payload.related_terms |
No | Related term keys |
update_glossary_term
Update an existing glossary term's definition or scope.
| Field | Required | Description |
|---|---|---|
payload.kind |
Yes | update_glossary_term |
payload.term_key |
Yes | Existing term key to update |
payload.definition |
Yes | New definition |
payload.definition_hash |
Yes | Hash of the proposed definition |
payload.related_terms |
No | Related term keys |
flag_not_helpful
Flag a DRG or doctrine artifact as not helpful. This proposal kind is auto-included in the
effective apply batch, but it still writes only when --apply is used.
| Field | Required | Description |
|---|---|---|
payload.kind |
Yes | flag_not_helpful |
payload.target.kind |
Yes | Target kind |
payload.target.urn |
Yes | Target URN |
add_edge
Add a relationship in the project-local DRG overlay.
| Field | Required | Description |
|---|---|---|
payload.kind |
Yes | add_edge |
payload.edge.from_node |
Yes | Source node URN |
payload.edge.to_node |
Yes | Target node URN |
payload.edge.kind |
Yes | Edge kind |
The schema also defines remove_edge for compatibility, but the current apply path does not
provide an operator-facing apply handler for it.
rewire_edge
Replace one edge with another.
| Field | Required | Description |
|---|---|---|
payload.kind |
Yes | rewire_edge |
payload.edge_old |
Yes | Existing edge object |
payload.edge_new |
Yes | Replacement edge object |
synthesize_directive / synthesize_tactic / synthesize_procedure
Create or update project-local doctrine artifacts.
| Field | Required | Description |
|---|---|---|
payload.kind |
Yes | One of the three synthesize_* kinds |
payload.artifact_id |
Yes | Safe artifact identifier |
payload.body |
Yes | Artifact body |
payload.body_hash |
Yes | Hash of the body |
payload.scope.actions |
No | Action scope list |
payload.scope.profiles |
No | Profile scope list |
Synthesizer acceptance criteria
The synthesizer accepts a proposal when:
- All required fields are present and valid
- No conflicting proposal exists in the same batch (same target with different values)
- The referenced artifacts (evidence event IDs) still resolve in the event log
The synthesizer rejects a proposal when:
- Required fields are missing
- Referenced evidence event IDs no longer resolve in the source mission event log
- The term key or artifact ID is malformed
- A conflicting proposal exists (fail-closed: the entire conflicting set is rejected)
Retrospective status events
Retrospective lifecycle events are written to kitty-specs/<slug>/status.events.jsonl alongside
other mission lifecycle events. Filter by event name prefix retrospective. to isolate them.
| Event name | When emitted | Description |
|---|---|---|
retrospective.requested |
At mission terminus | The runtime requested a retrospective |
retrospective.started |
Facilitator dispatched | The retrospective facilitator began execution |
retrospective.proposal.generated |
Per proposal | One event per generated proposal (×N) |
retrospective.completed |
Facilitator finished | The retrospective completed successfully |
retrospective.skipped |
Operator skipped | The operator explicitly skipped (HiC mode only) |
retrospective.failed |
Facilitator failed | The facilitator encountered an error |
retrospective.proposal.applied |
Per applied proposal | One event per applied proposal (×N) |
retrospective.proposal.rejected |
Per rejected proposal | One event per proposal rejected during apply |
The retrospective.skipped event and the corresponding status: skipped in the YAML record are
both required. Neither alone is sufficient to record a valid skip.
Synthesizer exit codes
spec-kitty agent retrospect synthesize uses the following exit codes:
| Exit code | Meaning | Action required |
|---|---|---|
| 0 | Success — dry-run complete (no changes) or proposals applied successfully | None |
| 1 | Invalid project root, unresolvable mission handle, or other command error | Fix invocation/project state |
| 2 | I/O error reading retrospective data | Inspect filesystem permissions and paths |
| 3 | Retrospective record missing or malformed | Create or repair .kittify/missions/<mission_id>/retrospective.yaml |
| 4 | Conflicts present during --apply |
Resolve conflicts before applying |
| 5 | Stale evidence or invalid payload rejection during --apply |
Refresh evidence or fix proposal payloads |
The --apply flag is required for any mutations. Omitting --apply always exits 0 on a valid
dry-run regardless of proposal count or detected conflicts.
For the current exit code list, run:
uv run spec-kitty agent retrospect synthesize --help