Data Model: State Architecture Cleanup Phase 2

Feature: 054-state-architecture-cleanup-phase-2 Date: 2026-03-20

State Contract Changes

This feature modifies the state contract (src/specify_cli/state_contract.py) to align classifications with enforced Git policy and removal of deprecated surfaces.

Constitution Surfaces (reclassified)

SurfaceBeforeAfterChange
constitution_interview_answersAUTHORITATIVE / INSIDE_REPO_NOT_IGNOREDAUTHORITATIVE / TRACKEDGit class → TRACKED (enforced by .gitignore absence)
constitution_referencesDERIVED / INSIDE_REPO_NOT_IGNOREDLOCAL_RUNTIME / IGNOREDAuthority → LOCAL_RUNTIME, Git → IGNORED (added to .gitignore)
constitution_libraryDERIVED / INSIDE_REPO_NOT_IGNOREDAUTHORITATIVE / TRACKEDAuthority → AUTHORITATIVE, Git → TRACKED

Active-Mission Marker (removed)

SurfaceBeforeAfterChange
active_mission_markerDEPRECATED / INSIDE_REPO_NOT_IGNOREDREMOVED from registryEntry deleted — no production code reads it

Atomic Write Flag (new metadata)

Surfaces that gain atomic-write discipline will have atomic_write=True set in their StateSurface entry:

SurfacePath PatternAtomic BeforeAtomic After
feature_metadatameta.jsonTrueTrue (unchanged)
runtime_feature_runs.kittify/runtime/feature-runs.jsonFalseTrue
workspace_context.kittify/workspaces/*.jsonFalseTrue
constitution_context_state.kittify/constitution/context-state.jsonFalseTrue
dashboard_metadata.kittify/.dashboardFalseTrue
lamport_clock~/.spec-kitty/clock.jsonTrue (inline)True (shared)
sync_credentials~/.spec-kitty/credentialsFalseTrue
sync_config~/.spec-kitty/config.tomlFalseTrue
tracker_config.kittify/config.yaml (tracker section)FalseTrue
project_metadata.kittify/metadata.yamlFalseTrue

Entity: AtomicWriter

New shared utility at src/specify_cli/core/atomic.py.

API

atomic_write(path, content, *, mkdir=False)
  path:    Target file path (Path)
  content: File content (str → UTF-8 encoded, or bytes → raw)
  mkdir:   If True, create parent directories before writing

  Guarantees:
  - Temp file created in same directory as target (same-filesystem rename)
  - os.replace() is atomic on all supported platforms
  - On failure: temp file cleaned up, original file untouched
  - On success: target file contains complete new content

Invariants

1. Temp file prefix: .atomic- (distinguishable from .meta- used by legacy feature_metadata.py) 2. Temp file suffix: .tmp 3. Exception handling: BaseException catch ensures cleanup even on KeyboardInterrupt 4. File descriptor lifecycle: os.fdopen() wraps fd, context manager closes it

.gitignore Changes

Added

.kittify/constitution/references.yaml

Unchanged (already present)

.kittify/constitution/context-state.json
.kittify/constitution/directives.yaml
.kittify/constitution/governance.yaml
.kittify/constitution/metadata.yaml

Unchanged (deliberately NOT ignored)

.kittify/constitution/constitution.md          # Source document
.kittify/constitution/interview/answers.yaml   # Team decisions
.kittify/constitution/library/*.md             # Shared knowledge

Acceptance Module Consolidation

Before

acceptance.py          → 657 lines, 10 public functions
acceptance_support.py  → 758 lines, 13 public functions (superset)

After

acceptance.py          → ~720 lines, 13 public functions (absorbed 3 unique standalone functions)
acceptance_support.py  → ~25 lines, pure re-export wrapper

Functions moved from standalone → canonical

FunctionPurpose
ArtifactEncodingErrorCustom exception for UTF-8 decode failures
normalize_feature_encoding()Windows-1252/Latin-1 → UTF-8 conversion
_read_text_strict()Encoding-strict file reader

AcceptanceSummary alignment

The path_violations field (present in acceptance.py, missing in acceptance_support.py) will be the single canonical definition after consolidation.