ADR-0008: Task lifecycle sidecar and immutable task spec
- Status: Accepted
- Date: 2026-06-11
- Deciders: Michel Gillet
Context
Milestone 9 introduced historical delivery metrics derived from planning task front matter
(created_at, updated_at, status, etc.). That approach assumes lifecycle timestamps
already exist on planning/tasks/<id>.md, but does not define how status transitions are
recorded while work is in progress.
We need lifecycle data that is:
- Portable — visible on any machine after
git pull(not local-only cache) - Fast to aggregate — metrics collection should not walk full git history per task
- Separated from the task spec — implementation work should not repeatedly rewrite the human-readable task definition (summary, acceptance criteria, scope) during delivery
Embedding a full status_history in task front matter on every transition satisfies
portability and read speed, but mixes spec (what the task is) with telemetry (what
happened). Reviewers then see planning markdown churn alongside code changes.
Decision
Each task may have a lifecycle sidecar alongside its planning artifact:
| Artifact | Path | Role |
|---|---|---|
| Task spec | planning/tasks/<id>.md | Immutable specification — summary, acceptance criteria, scope, links. Status and timestamp fields on this file are not updated during active implementation unless the spec itself changes. |
| Lifecycle sidecar | planning/tasks/<id>.events.ndjson | Append-only event log — status transitions and other delivery events, one JSON object per line (NDJSON). |
Sidecar format
Each line is a self-contained JSON object. Minimum event shape:
{
"task_id": "TASK-42",
"event": "status_changed",
"from": "todo",
"to": "in_progress",
"at": "2026-06-11T14:30:00Z"
}
Additional fields (e.g. actor, source, note) may be added in later schema versions.
The sidecar file is machine-oriented by design.
Repository and portability
- The sidecar is a committed file under
planning/tasks/, versioned like any other planning artifact. - Events are not stored in local-only
.a2c/cache/for lifecycle purposes. - Git merge strategy (merge commit, squash, rebase, etc.) is out of scope for this ADR. The sidecar model does not depend on a particular trunk-integration workflow.
Metrics collection
Future a2c_core metrics collection should read lifecycle data from the sidecar when
present, and derive work_started_at, completed_at, lead time, and cycle time from
recorded events rather than coarse proxies on the task spec file.
Front-matter-only collection remains valid as a fallback when no sidecar exists.
Tooling expectations (future)
- Record events via core API / CLI (append to
<id>.events.ndjson). - Validate sidecar schema and task id consistency.
- Do not require rewriting
<id>.mdon each status transition.
Rationale
- Spec vs telemetry — keeps
planning/tasks/<id>.mdstable for humans and AI during implementation; lifecycle noise lives in NDJSON reviewers can skim. - Fast reads —
a2c metrics collectscans task files and sidecars in O(tasks), not git history. - Portable — committed sidecar syncs across machines without a separate event store.
- NDJSON — append-friendly, supports structured fields (tags, actors) without CSV pain.
Alternatives considered
| Alternative | Verdict | Reason |
|---|---|---|
Lifecycle only in task front matter (status_history, repeated updated_at edits) | Rejected as primary | Fast to read but pollutes the spec file and review diffs |
Lifecycle only in git commits (parse git log per task file) | Rejected as primary | Portable but slow for repo-wide metrics; better as optional augmentation |
Local-only .a2c/events/ | Rejected | Not portable across machines |
| Flush sidecar into task.md at merge | Optional | Valid variant but not required; sidecar may remain the canonical lifecycle store on trunk |
Consequences
Positive
- Clear separation between task definition and task delivery history
- Metrics can be accurate without mutating the spec artifact during implementation
- Sidecar diffs are clearly machine-oriented in review
- Aligns with repo-first planning under
planning/tasks/
Negative
- Two files per task when lifecycle tracking is enabled
- Loaders and validators must recognize
*.events.ndjsonnaming convention - Sidecar-aware metrics derivation is follow-on work (not part of this ADR)
- Immutability of the spec is a convention — tooling should discourage spec edits for status-only updates, but cannot enforce without hooks
Migration
- Document sidecar convention in planning artifact docs.
- Implement event record API and sidecar-aware metrics derivation (follow-on milestone).
- Optionally add schema
task-events.schema.jsonfor sidecar lines. - Existing tasks without sidecars may continue using front-matter timestamp proxies.
Relationship to other ADRs
| ADR / doc | Relationship |
|---|---|
| planning-artifact-format.md | Complements — adds sidecar naming next to task Markdown |
References
- docs/task-body-profiles.md — task spec sections (sidecar is not a profile variant)
- Milestone 9 task fixture:
tests/fixtures/workflows/task_m9_historical_delivery_metrics.body.md
Supersedes
- (none)
Superseded by
- (none)