Quickstart — Operator smoke flow
Mission: charter-ux-and-org-pack-vocabulary-01KSAF14
This document describes the operator-facing smoke flow that proves the mission landed. Each step has a deterministic expected output.
Pre-requisites
- A fresh clone of a Spec Kitty project that ships only
.kittify/charter/charter.md(no synthesized doctrine yet). - A second fixture pack repo with a tactic declaring
enhances: <built-in-id>.
Step 1 — Fresh-checkout freshness reporting (FR-001..FR-005, FR-009)
cd <fresh-project-clone>
spec-kitty charter status --json | jq '.freshness'
Expected:
{
"charter_source": {"state": "fresh", "last_change": "...", "remediation": null},
"synced_bundle": {"state": "missing", "last_change": null, "remediation": "spec-kitty charter sync"},
"synthesized_drg": {"state": "missing", "last_change": null, "remediation": "spec-kitty charter synthesize"}
}
spec-kitty charter lint --json | jq '.graph_state'
Expected: "built_in_only" (lint falls back to built-in DRG; FR-002).
Step 2 — Preflight detects degradation (FR-006..FR-008)
spec-kitty charter preflight --json | jq '.passed,.blocked_reason'
Expected:
false
"synthesized_drg missing; run \`spec-kitty charter synthesize\`"
Run preflight with auto-refresh on:
spec-kitty charter preflight --auto-refresh --json | jq '.auto_refresh_applied,.auto_refresh_actions'
Expected:
true
["spec-kitty charter sync", "spec-kitty charter synthesize"]
Step 3 — Synthesize bootstrap contract (FR-009)
After Step 2's auto-refresh, the synthesizer either produced graph.yaml or set built_in_only: true in the manifest:
ls .kittify/doctrine/graph.yaml 2>&1
yq '.built_in_only' .kittify/charter/synthesis-manifest.yaml
Expected: either the graph file exists, OR the manifest reports true. Downstream charter status reports synthesized_drg.state = "fresh" or "built_in_only".
Step 4 — Pack-authoring vocabulary (FR-010..FR-014)
4a. Field-merge edge case lock (architect remediation, C-001 sharp edge)
Before the user-facing flow, the fixture pack MUST include this acceptance case to lock the field-merge behaviour:
A pack tactic with the same ID as a built-in tactic, declaring enhances: <built-in-id>, AND providing an empty steps: list (or omitting any optional field that the built-in has populated). Per the field-merge ADR 2026-05-16-1, the expected behaviour is:
- For fields the pack tactic provides with a non-empty value: pack value wins.
- For fields the pack tactic omits: built-in value survives.
- For fields the pack tactic provides as empty list / null: the empty value DOES NOT erase the built-in value (this is the sharp edge — empty is treated as "not provided" for merge purposes, per ratified
_merge()behaviour insrc/doctrine/base.py).
The fixture test tests/integration/test_pack_enhances_partial_fields.py MUST exercise this case and assert the merged DRG node retains the built-in's steps, failure_modes, and applies_to_languages when the pack tactic omits them.
4b. Validator behaviour (user-facing flow)
In the fixture pack:
spec-kitty doctrine pack validate <pack-dir> --json | jq '.issues[] | select(.category=="same_id_collision")'
Before the declaration is added: a same_id_collision ADVISORY with the new reworded message.
Add enhances: <built-in-id> to one of the pack tactics, then re-validate:
spec-kitty doctrine pack validate <pack-dir> --json | jq '.issues[] | select(.category=="same_id_collision")'
Expected: empty result (advisory suppressed; FR-013).
Inspect the auto-emitted DRG edge:
spec-kitty doctrine pack validate <pack-dir> --json | jq '.drg_fragment.edges[] | select(.relation=="enhances")'
Expected: a DRG edge with relation: "enhances", source: tactic:<pack-id>, target: tactic:<built-in-id> (FR-014).
Mistake test: change the field to reference a non-existent ID:
spec-kitty doctrine pack validate <pack-dir> --json | jq '.issues[] | select(.category=="unknown_target")'
Expected: a hard unknown_target error (FR-012).
Step 5 — Vocabulary cutover (FR-015, FR-016)
spec-kitty charter status --json | grep -c '"shipped"'
spec-kitty charter lint --json | grep -c '"shipped"'
spec-kitty agent profile list --json 2>/dev/null | grep -c '"shipped"' || true
spec-kitty doctrine pack validate <pack-dir> --json | grep -c '"shipped"' || true
Expected: every count is 0. The architectural regression test tests/architectural/test_no_shipped_layer_label.py enforces this on every CI run (FR-016).
Step 6 — End-to-end success criteria
After running Steps 1-5:
- ✅ Fresh-clone freshness reports the missing DRG with a single canonical remediation.
- ✅ Preflight blocks or auto-refreshes deterministically.
- ✅ Pack authors can declare
enhances/overrideswithout spurious advisories. - ✅
"shipped"no longer appears as a layer label in any public JSON.
If any step fails, the mission is not landed.