Contracts

charter-context-resolver.md

Contract: build_charter_context with profile= load-bearing

Mission: wp-prompt-governance-payload-01KRR8HS Surface: charter.context.build_charter_context FRs covered: FR-001, FR-002, FR-003, FR-008 ATDD anchors: TestCharterContextResolverCompleteness::, TestImplementPromptContainsActionableGovernance::, TestProfileDirectivesSurfacedInWpPrompt::, TestPromptReferencesAuthorityPaths::


1. Input contract

def build_charter_context(
    repo_root: Path,
    *,
    profile: str | None = None,         # NEW: now load-bearing
    action: str,
    mark_loaded: bool = True,
    depth: int | None = None,
    org_root: Path | None = None,
) -> CharterContextResult:
    ...
ParameterTypePre-conditionPost-condition
repo_rootPathDirectory containing .kittify/charter/charter.md (resolved via charter.resolution.resolve_canonical_repo_root).Unchanged.
profile`str \None`When provided, MUST be a profile ID resolvable via AgentProfileRepository.default().get(profile). Unknown profiles do NOT raise — resolver renders the prompt with profile sections omitted and logs a warning.
actionstrOne of BOOTSTRAP_ACTIONS (specify, plan, implement, review) for the bootstrap path; any string for the compact path. Case-insensitive.Normalised to lowercase in output.
mark_loadedboolWhen True, records first-load timestamp into .kittify/charter/context-state.json.Side effect identical to today.
depth`int \None`Override the auto-computed effective depth.
org_root`Path \None`Path to org doctrine snapshot.

2. Output contract

Returns CharterContextResult (frozen dataclass, shape unchanged). The text field MUST contain the following sections when profile=<known-id> and action is a bootstrap action:

#Section header (anchor)Required whenContent
1Charter Context (Bootstrap):alwaysSource path + first-load guidance.
2Policy Summary:charter has ## Policy Summary OR resolver falls back to bulletsUp to 8 bullets.
3Project authority paths:bootstrap actionAt minimum glossary/contexts/ and architecture/2.x/adr/ (when those directories exist on disk); additionally every path in governance.doctrine.authority_paths. Each entry carries a one-sentence "When you …, …" conditional.
4Action-Critical Charter Sections (<action>):bootstrap action AND charter has at least one action-critical sectionFor each section in the action-critical set, either the verbatim body OR the fetch + when-doing stanza. The action-critical set for software-dev defaults to Terminology Canon, Code Review Checklist, Regression Vigilance; missions MAY extend the set.
5Profile-Cited Directives (<profile-id>):bootstrap action AND profile=<id> resolvable AND profile has at least one directive_references entryFor each DIRECTIVE_NNN in profile.directive_references, an entry - DIRECTIVE_NNN: <title> — <rationale>. Verbatim body inlined when under budget; otherwise replaced with the fetch + when-doing stanza.
6Profile-Cited Tactics (<profile-id>):same conditions, for tactic_referencesSame shape as section 5.
7Action Doctrine (<action>):bootstrap actionExisting resolver-driven Directives: and Tactics: lists (today's behaviour).
8Reference Docs:alwaysUp to 10 reference entries, filtered by action.

Verbatim-OR-fetch stanza

Both halves are required. The stanza format is:

Run: spec-kitty charter context --include <selector>
When you <action-verb conditional>, run this command and apply the returned rule.

The selector is one of:

  • directive:DIRECTIVE_NNN
  • tactic:<tactic-id>
  • section:<kebab-cased-heading>

The conditional matches the regex pinned by the ATDD test helper _WHEN_DOING_RE:

r"when\s+you\s+(are\s+about\s+to|need\s+to|encounter|introduce|rename|review)"

Token budget

The aggregate text MUST stay under 32 000 characters. If a render exceeds the budget, the longest-body section is auto-substituted with its fetch stanza; substitution iterates until under budget. When all bodies have been substituted and the prompt is still over budget, the result carries a single line:

# Governance payload: <N> sections substituted with fetch commands (budget=32000).

3. Failure modes

FailureBehaviourSide effect
profile=<unknown-id>Sections 5 and 6 omitted; rest of the payload rendered normally.Single WARNING log line Profile '<id>' not found; profile-cited sections omitted. No exception.
Catalog citation unresolvable (DIRECTIVE_NNN referenced but not in catalog)The entry appears in section 5 with the ID and the placeholder body <not found in catalog>.Single WARNING log line per unresolvable ID.
Charter missing the section a _render_critical_section_bodies call expectedThe section is silently omitted from section 4.No log line (charter sections are optional).
charter.md absentReturns a CharterContextResult with mode="missing", exactly as today.No change to existing behaviour.
profile=<id> provided but action not in BOOTSTRAP_ACTIONSThe compact-mode path runs; sections 3-6 are not emitted.profile= is honoured by being recorded in compact view's metadata only; no profile rendering occurs in compact mode.

4. Backward compatibility guarantee

When profile=None (the default), result.text is byte-identical to today's output for the same (repo_root, action, depth, org_root) tuple, with two additive exceptions:

1. Section 3 (Project authority paths:) appears when at least one default authority directory (glossary/contexts/ or architecture/2.x/adr/) is present on disk. This is a new section that today's callers do not expect. 2. Section 4 (Action-Critical Charter Sections) appears when the charter body contains at least one section in the action-critical set. This is a new section that today's callers do not expect.

Both additions are pure prepends to existing content; existing string-matching callers that look for Charter Context (Bootstrap):, Policy Summary:, Action Doctrine (...):, or Reference Docs: continue to find their anchors.

When profile=<id> is provided, sections 5 and 6 additionally appear. No existing call sites pass profile=, so no caller is affected by this change without an explicit opt-in.


5. Layer-rule preservation (C-001 / NFR-004)

This contract is implementable without any new import from specify_cli in charter. The profile lookup uses from doctrine.agent_profiles.repository import AgentProfileRepository — an import from doctrine, which is below charter in the layer order (kernel ← doctrine ← charter ← specify_cli). The test suite tests/architectural/test_layer_rules.py (8 tests) MUST stay green.

charter-sync-cross-link.md

Mission: wp-prompt-governance-payload-01KRR8HS Surface: charter.sync.sync (via charter.extractor.Extractor._extract_directives) FRs covered: FR-006 ATDD anchors: TestCharterDirectiveNamespaceCrossLink::test_charter_sync_emits_cross_link_when_body_cites_catalog_id


1. Input

The input is the raw markdown body of a charter section that the extractor classifies as a directive section (any section whose heading lower-cased matches one of directive, constraint, rule via Extractor._classify_section).

Each numbered item inside such a section becomes a Directive. The extractor's job in this mission is to detect, inside that numbered-item body, citations to:

Citation kindDetection regexFilter
Doctrine-catalog directive ID\bDIRECTIVE_(\d{3})\bNone — every match counts.
Tactic ID (kebab-case slug)\b([a-z][a-z0-9]*(?:-[a-z0-9]+){1,4})\bMatch counts ONLY when DoctrineService.tactics.get(slug) returns a non-None record.

The tactic-id filter prevents false positives on incidental kebab-case words (e.g. pre-commit-hooks is not a tactic ID; language-driven-design is).


2. Output

The generated .kittify/charter/directives.yaml entry MUST gain a structured references: list when the body contained at least one catalog citation.

Example input charter body

### Code Review Checklist

- The WP diff respects the agent profile's directive-references.
- Terminology in code and docs aligns with the project glossary
  (DIRECTIVE_032 — Conceptual Alignment).
- Reviewers detect terminology drift early using the language-driven-design tactic.

Example output directives.yaml entry

directives:
  - id: DIR-005
    title: "Code Review Checklist (terminology alignment)"
    description: |
      Terminology in code and docs aligns with the project glossary
      (DIRECTIVE_032 — Conceptual Alignment).
    severity: warn
    references:
      - DIRECTIVE_032
      - language-driven-design

Field ordering

When references: is emitted, IDs MUST appear in the order they appeared in the body (first occurrence wins; duplicates are de-duplicated while preserving position). This makes diffs deterministic and reviewable.

When the body contains no citation, references: is omitted entirely (not emitted as an empty list) so existing serialized fixtures stay byte-identical.


3. Failure modes

FailureBehaviour
Body contains a malformed citation (e.g. DIRECTIVE_12 without three digits)No match; no entry added to references:.
Tactic-id-like slug appears but DoctrineService.tactics.get(slug) returns NoneSlug is not added to references:. Tactic-id filter does its job.
DoctrineService cannot be constructed (e.g. shipped catalog missing)Detection regex for directives still runs; the tactic-id detector silently emits no tactic references. charter sync does not error.
Multi-line citation across a numbered-item bodyDetection runs on the joined body string of each numbered item; line breaks are not significant to the regex.
Charter sync run on a charter that pre-dates this mission (no inline citations)references: field is never emitted; output is byte-identical to today (NFR-005).

4. Backward compatibility

field is optional with default=[]).

pre-mission output.

install) ignores the field — Pydantic's model_validate accepts unknown fields by default for Directive.

  • Pre-mission directives.yaml files load identically (the new references:
  • A charter body that contains no citation produces output byte-identical to
  • A consumer that does not know about references: (e.g. an older spec-kitty

5. Downstream consumption

build_charter_context consumes the references: field as follows: when a DIR-NNN entry is included in the resolved action context (by being in governance.doctrine.selected_directives or by matching the action's directive canon), the resolver iterates directive.references and additionally surfaces the catalog body — either inline or via the fetch + when-doing stanza — in section 4 ("Action-Critical Charter Sections") or section 5 ("Profile-Cited Directives") of the rendered text. The same applies to tactic-id references, which are surfaced via DoctrineService.tactics.

This downstream consumption is documented in charter-context-resolver.md.

runtime-template-governance-payload-contract.md

Contract: ## Governance Payload Contract section in runtime templates

Mission: wp-prompt-governance-payload-01KRR8HS Surface: src/specify_cli/missions/software-dev/command-templates/implement.md, src/specify_cli/missions/software-dev/command-templates/review.md FRs covered: FR-005 ATDD anchors: TestImplementTemplateForbidClauseIsHonest::test_template_either_drops_forbid_or_guarantees_governance_payload


1. Purpose

The runtime implement.md template at lines 68-71 today says:

> The output of spec-kitty agent action implement ... is the authoritative > work package prompt and execution context. Do not separately call > spec-kitty charter context or rummage through unrelated files looking > for a "newer" prompt unless the command output tells you to.

That clause is defensible only when the prompt actually carries the governance bodies it implicitly claims. The mission preserves the clause (per C-003 option b — "the forbid clause remains but is paired with a Governance Payload Contract section") and adds the contract section so the clause becomes honest. The review template (review.md) gains the parallel section for reviewer surfaces.


2. Section schema

The section MUST be added as a top-level ## Governance Payload Contract heading. It MUST appear after the forbid clause and before ## Execution Steps. The body has four fixed blocks in this order:

## Governance Payload Contract

The prompt above is guaranteed to carry the following surfaces. Trust the prompt;
do not consult external governance sources unless explicitly cited by a fetch
command + when-doing rule in the prompt.

**Guaranteed bodies** (verbatim in the prompt when under the token budget):

- Terminology Canon — from `.kittify/charter/charter.md`
- Code Review Checklist — from `.kittify/charter/charter.md`
- Regression Vigilance — from `.kittify/charter/charter.md`
- (any additional action-critical sections the mission declares)

**Guaranteed citations** (catalog IDs always present in the prompt):

- Every `DIRECTIVE_NNN` declared in the loaded agent profile's
  `directive-references` list.
- Every tactic-id declared in the loaded agent profile's `tactic-references`
  list.

**Guaranteed authority pointers** (path + when-doing conditional):

- `glossary/contexts/` — canonical terminology (consult when you encounter
  a domain term in the diff).
- `architecture/2.x/adr/` — architectural intent (consult when you change a
  structural boundary).
- (any additional paths declared in the charter's `authority_paths:` block).

**Fetch commands** (the prompt may substitute these for bodies that exceed the
token budget; when a fetch command appears, an accompanying
`When you <verb>, run this and apply` line specifies the trigger):

- `spec-kitty charter context --include directive:DIRECTIVE_NNN`
- `spec-kitty charter context --include tactic:<id>`
- `spec-kitty charter context --include section:<slug>`

3. Block-by-block requirements

BlockRequired keysSource of truth
Guaranteed bodiesBulleted list of action-critical section names. Minimum three for software-dev: Terminology Canon, Code Review Checklist, Regression Vigilance.Mission action-critical section configuration; defaults documented in data-model.md.
Guaranteed citationsTwo bullets describing the two profile-cited surfaces (directives, tactics).AgentProfile.directive_references / AgentProfile.tactic_references.
Guaranteed authority pointersBulleted list naming each authority path + a one-sentence "consult when …" conditional. Minimum two: glossary/contexts/, architecture/2.x/adr/.DoctrineSelectionConfig.authority_paths + filesystem defaults.
Fetch commandsBulleted list of the three canonical fetch-command forms.The resolver's fetch-stanza format documented in charter-context-resolver.md.

4. Detection regex (acceptance gate)

The architectural test tests/architectural/test_template_governance_payload_contract.py (new, added in WP06) MUST detect the section presence using this regex:

re.compile(
    r"##\s+Governance\s+Payload\s+Contract\b",
    re.IGNORECASE,
)

The ATDD test at tests/specify_cli/next/test_wp_prompt_governance_contract.py::TestImplementTemplateForbidClauseIsHonest::test_template_either_drops_forbid_or_guarantees_governance_payload already accepts any of three phrasings:

re.compile(
    r"governance\s+payload\s+contract|"
    r"the\s+prompt\s+is\s+guaranteed\s+to\s+include|"
    r"governance\s+sections?\s+below\s+contain\s+the\s+rule\s+bodies",
    re.IGNORECASE,
)

The chosen heading text Governance Payload Contract matches the primary phrasing.


5. Review template adaptation

The review template's section uses the same four-block structure with reviewer- oriented guaranteed-citations:

BlockReviewer-specific content
Guaranteed bodiesSame three sections (Terminology Canon, Code Review Checklist, Regression Vigilance).
Guaranteed citationsEvery DIRECTIVE_NNN and tactic-id declared by the loaded reviewer profile (e.g. reviewer-renata declares DIRECTIVE_032 — Conceptual Alignment, and language-driven-design).
Guaranteed authority pointersSame as implement.
Fetch commandsSame as implement.

The text MUST also include a sentence in the "Guaranteed citations" block such as:

> When you assess a WP that renames identifiers or terms, the prompt cites > DIRECTIVE_032 (Conceptual Alignment) by ID; consult its rule body inline or > via the paired fetch command and apply.

This satisfies the journey-2 user story and gives the reviewer-template's fetch-with-conditional check a deterministic anchor.


6. Failure modes

FailureBehaviour
Section is missing from the templateArchitectural test fails; ATDD test 5 fails. WP06 is the gate.
Section is present but a block is missing or emptyArchitectural test fails with the specific block name.
Section is present but the guaranteed-bodies list does not include the action-critical sections the resolver actually emitsArchitectural test fails (mismatch between template-promised and resolver-emitted surfaces).
Charter resolver emits a new section not listed in the templateArchitectural test fails; template must be updated in the same change.

7. Drift prevention

To prevent template drift from resolver behaviour, the architectural test parses both:

1. The template's ## Governance Payload Contract section (the promise). 2. The resolver's output for a fixture mission with a known profile (the reality).

It asserts every guaranteed surface listed in the template is present in the resolver output. The reverse is intentionally not enforced — the resolver may emit additional surfaces (e.g. action-doctrine list) without forcing a template update.