Data Model: Canonical State Authority & Single Metadata Writer

Feature: 051-canonical-state-authority-single-metadata-writer Date: 2026-03-18

Entities

FeatureMetadata (meta.json)

The canonical metadata document for a feature. Owned exclusively by feature_metadata.py.

Required fields (always present, validated on every write):

FieldTypeDescription
feature_numberstrZero-padded feature number (e.g., "051")
slugstrFull feature slug (e.g., "051-canonical-state-authority-single-metadata-writer")
feature_slugstrAlias for slug (backward compatibility)
friendly_namestrHuman-readable title
missionstrMission key (e.g., "software-dev", "research", "documentation")
target_branchstrBranch to merge into (e.g., "2.x", "main")
created_atstrISO 8601 timestamp

Optional fields (present only when relevant operation has occurred):

FieldTypeSet byDescription
vcsstrset_vcs_lock()VCS type ("git")
vcs_locked_atstrset_vcs_lock()ISO 8601 timestamp of VCS lock
accepted_atstrrecord_acceptance()ISO 8601 timestamp of acceptance
accepted_bystrrecord_acceptance()Actor who accepted
acceptance_modestrrecord_acceptance()How accepted (e.g., "standard", "orchestrator")
accepted_from_commitstrrecord_acceptance()Git commit at acceptance time
accept_commitstrrecord_acceptance()Git commit created by acceptance
acceptance_historylist[dict]record_acceptance()Bounded list (max 20) of past acceptances
merged_atstrrecord_merge()ISO 8601 timestamp of merge
merged_bystrrecord_merge()Actor who merged
merged_intostrrecord_merge()Target branch merged into
merged_strategystrrecord_merge()Merge strategy used
merged_pushboolrecord_merge()Whether push was performed
merged_commitstrfinalize_merge()Final merge commit hash
merge_historylist[dict]record_merge()Bounded list (max 20) of past merges
documentation_statedictset_documentation_state()Documentation mission state (see DocumentationState)
source_descriptionstrFeature creationOriginal feature description text

Forward compatibility: Unknown fields are preserved during read-modify-write. The API does not strip unrecognized keys.

AcceptanceHistoryEntry

A single entry in the acceptance_history array.

FieldTypeDescription
accepted_atstrISO 8601 timestamp
accepted_bystrActor
acceptance_modestrMode (standard/orchestrator)
accepted_from_commitstrSource commit
accept_commitstrCreated commit

MergeHistoryEntry

A single entry in the merge_history array.

FieldTypeDescription
merged_atstrISO 8601 timestamp
merged_bystrActor
merged_intostrTarget branch
merged_strategystrStrategy
merged_pushboolPush performed
merged_commit`str \None`

StatusSnapshot (status.json) — Existing, No Changes

Materialized view of all WP lane states, derived from status.events.jsonl. Already canonical. This sprint does not modify the status model — it only changes who reads it (acceptance now reads this instead of Activity Log).

Compatibility Views — Existing, Downgraded

These remain generated but are no longer read for workflow decisions:

ViewLocationGenerated by
WP frontmatter lanetasks/WP*.md YAML frontmatterlegacy_bridge.update_frontmatter_views()
Activity Logtasks/WP*.md body sectionlegacy_bridge.update_all_views()
Status blocktasks.md generated sectionlegacy_bridge.update_tasks_md_views()

State Authority Hierarchy

CANONICAL (source of truth):
  status.events.jsonl  →  Lane transitions (append-only)
  status.json          →  Materialized snapshot (derived from events)
  meta.json            →  Feature metadata (single writer: feature_metadata.py)

DERIVED (compatibility views, non-authoritative):
  WP frontmatter lane  →  Generated after each transition
  Activity Log body    →  Human narrative, generated after each transition
  tasks.md status      →  Dashboard view, generated after each transition

Mutation API Surface

feature_metadata.py
├── load_meta(feature_dir) → dict[str, Any] | None
├── write_meta(feature_dir, meta) → None  [atomic, validated]
├── record_acceptance(feature_dir, accepted_by, mode, from_commit, accept_commit) → dict
├── record_merge(feature_dir, merged_by, merged_into, strategy, push) → dict
├── finalize_merge(feature_dir, merged_commit) → dict
├── set_vcs_lock(feature_dir, vcs_type, locked_at) → dict
├── set_documentation_state(feature_dir, state) → dict
├── set_target_branch(feature_dir, branch) → dict
├── validate_meta(meta) → list[str]  [returns error messages, empty = valid]
└── _atomic_write(path, content) → None  [internal]

Each mutation helper: 1. Calls load_meta() (read current) 2. Applies the specific mutation 3. Calls validate_meta() (check required fields) 4. Calls write_meta() (atomic write with standard formatting) 5. Returns the updated dict