Contracts

activation.md

Contract: SPDD/REASONS Activation Detection

Function

def is_spdd_reasons_active(repo_root: Path) -> bool: ...

Inputs

  • repo_root: project root containing .kittify/charter/.

Output

  • True iff the project's active charter selection contains AT LEAST ONE of:
  • paradigm structured-prompt-driven-development
  • tactic reasons-canvas-fill
  • tactic reasons-canvas-review
  • directive DIRECTIVE_038
  • False otherwise (including when no charter exists).

Failure modes

  • Missing .kittify/charter/: returns False (not an error).
  • Malformed governance.yaml: raises the same exception as existing charter loaders (do not swallow).
  • No paradigms section in governance.yaml: returns False (not an error).

Performance

  • Reads at most two YAML files (governance.yaml, directives.yaml). Must complete in <50ms typical.

Caching

  • May be cached per-process for the lifetime of a single CLI invocation. Must NOT persist across invocations.

Tests (acceptance for WP2)

CaseCharter selectionExpected
1empty/no charterFalse
2only directives outside the packFalse
3paradigm structured-prompt-driven-development selectedTrue
4tactic reasons-canvas-fill selected onlyTrue
5tactic reasons-canvas-review selected onlyTrue
6directive DIRECTIVE_038 selected onlyTrue
7malformed governance.yamlraises (not silently False)

charter-context.md

Contract: Charter Context Injection

Surface

spec-kitty charter context --action <action> --json (existing CLI command). Powered by src/charter/context.py:build_charter_context().

Behavior change

  • When is_spdd_reasons_active(repo_root) is False: the JSON text and context fields MUST equal the pre-feature output exactly (byte-or-semantic identical). This is verified by an "inactive baseline" snapshot fixture in tests/charter/test_charter_context_spdd_reasons.py.
  • When active: the "Action Doctrine" section gains an additional subsection titled "SPDD/REASONS Guidance (action: <action>)" appended after the existing tactic lines. Subsection content depends on action per the table in prompt-fragment.md.

Implementation seam

In src/charter/context.py, after _append_action_doctrine_lines() (line 537), add a single optional call:

if is_spdd_reasons_active(self.repo_root):
    _append_spdd_reasons_guidance(lines, mission, action)

Place the helper in a new module (src/doctrine/spdd_reasons/charter_context.py) so context.py only imports a single function.

Inactive guarantee

A regression test loads a fixture project without the pack and asserts that build_charter_context() returns identical bytes to the snapshot captured before this mission was implemented.

Performance

The added work is bounded by reading and formatting at most ≤4 artifact records per call. Total budget for the SPDD branch ≤50ms additional, keeping the call within the 2s target (NFR-002).

JSON shape (unchanged)

The top-level JSON keys (result, mode, text, context, references_count, action) are unchanged. Only the inner content of text and context grows when active.

prompt-fragment.md

Contract: Conditional Prompt Fragment Rendering

Marker convention

Each command template gains AT MOST ONE conditional block per applicable action, delimited by:

<!-- spdd:reasons-block:start -->
... markdown content describing the action-scoped REASONS guidance ...
<!-- spdd:reasons-block:end -->

Renderer behavior

For each command template that contains a spdd:reasons-block:

  • If is_spdd_reasons_active(repo_root) is True: keep the block content but strip the surrounding <!-- spdd:reasons-block:start --> and <!-- spdd:reasons-block:end --> marker lines. Content rendered verbatim.
  • If is_spdd_reasons_active(repo_root) is False: remove the marker lines AND every line strictly between them (including any blank line that was added solely to separate the block from surrounding text). Result must be byte-identical to the pre-feature template (verified via golden snapshot in tests/prompts/test_prompt_fragment_rendering.py).

Non-goals

  • No Jinja or other expression evaluation. Block contents are static markdown.
  • No nested blocks. Flat blocks only.

Acceptance tests (WP4)

CaseProject stateExpectation
1inactive (no SPDD selection)Five command-template outputs (specify, plan, tasks, implement, review) byte-identical to baseline snapshot.
2activeEach output contains the corresponding REASONS guidance block, no marker lines.
3active for specify onlyBlock scoped to Requirements/Entities appears in specify output; other templates may also include their action-scoped block.
4malformed marker (missing end)Raises a clear renderer error; does not silently truncate.

Action-scoped block content

TemplateBlock headlineSections referenced
specify.md"REASONS Guidance — Specify"Requirements, Entities
plan.md"REASONS Guidance — Plan"Approach, Structure
tasks.md"REASONS Guidance — Tasks"Operations, WP boundaries
implement.md"REASONS Guidance — Implement WP\<id\>"Full canvas (R, E, A, S, O, N, S)
review.md"REASONS Guidance — Review"Comparison surface (R, O, N, S) + drift classification

review-gate.md

Contract: Opt-in REASONS Review Gate

Activation

  • The gate is included in the reviewer prompt only when is_spdd_reasons_active(repo_root) is True.
  • When inactive, the reviewer prompt is byte-identical to the pre-feature output (covered by prompt-fragment.md).

Reviewer expectations (when active)

1. Locate kitty-specs/<mission>/reasons-canvas.md. If missing, instruct the reviewer to call the spec-kitty-spdd-reasons skill to author one before completing review (do not auto-approve in absence of canvas). 2. For each Requirement and Operation in the canvas, identify supporting evidence in the diff or note its absence. 3. Detect entities, files, or surfaces touched by the diff that are absent from the canvas Structure or Approach. 4. Verify Norms and Safeguards adherence. 5. Charter directives take precedence; if a charter directive conflicts with the canvas, follow the directive and add a deviation note.

Drift classification

Reviewer outputs one of:

OutcomeDefinitionAction
approvedNo divergence OR all divergences match Deviations entries.APPROVE
approved_with_deviationDivergence is acceptable; reviewer adds a Deviations entry.APPROVE + canvas update
canvas_update_neededCanvas was wrong; canvas should be revised before next WP.APPROVE conditionally; open canvas update task.
glossary_update_neededTerm drift surfaced; glossary should be revised.APPROVE conditionally; open glossary update task.
charter_follow_upSelection should change.APPROVE conditionally; open charter follow-up.
follow_up_missionOut-of-scope work surfaced; create a separate mission.APPROVE current scope; open follow-up.
scope_drift_blockOut-of-bounds undocumented work.REJECT.
safeguard_violation_blockSafeguard rule violated.REJECT.

Tests (WP5)

CaseProject stateDiffExpected outcome
1inactiveanyReviewer prompt unchanged from baseline.
2active, no canvasanyReviewer instructed to author canvas first.
3active, in-bounds diffmatches canvasapproved
4active, OOB undocumentednew file outside Structurescope_drift_block
5active, safeguard breachviolates a Safeguardsafeguard_violation_block
6active, OOB but recorded in Deviationsmatches Deviations entryapproved_with_deviation