Skip to content

feat(framework): a task identity file linking delivery to its backlog item #649

Closed

Description

@blafourcade

Outcome

A delivery folder declares where it comes from and which step produced which file, without repeating anything the backlog item already holds.

Scope

  • Includes: metadata.json in the task folder, carrying a unit_id — the identity of the unit of work, distinct from the run_id of a session in feat(framework): an aidd-telemetry plugin whose hooks journal every session #620, one upward link, and a journal of the steps that ran with the files each produced.
  • Includes: written by the skills that create and advance the folder, at step boundaries — a handful of writes over a task's life, human-readable and correctable.
  • Excludes: status of any kind. The artefact's frontmatter owns it, and a step with an end time is finished by deduction.
  • Excludes: the folder's file listing. produced is provenance, not an inventory.
  • Excludes: tokens, cost, model, duration. Those come from telemetry.
  • Excludes: any downward link. Readers index the folders and group by the upward one.

The file

{
  "schema_version": 1,
  "unit_id": "01J9X4M2K7QRVB",
  "task_id": "2026_08_14_telemetry-v1",
  "backlog": "ai-driven-dev/framework#617",
  "branch": "feat/telemetry-v1",
  "pull_request": "ai-driven-dev/framework#651",
  "opened_at": "2026-08-14T08:40:12Z",
  "closed_at": null,
  "steps": [
    { "skill": "aidd-pm:04-spec",   "from": "...", "to": "...", "produced": ["spec.md"] },
    { "skill": "aidd-dev:01-plan",  "from": "...", "to": "...", "produced": ["plan.md", "phase-1.md"] },
    { "skill": "aidd-dev:08-debug", "from": "...", "to": "...", "produced": [] }
  ]
}

steps is a journal, not a checklist: entries repeat, and arrive out of order. Three debug runs mid-implementation give three entries. The flow is read afterwards, never constrained beforehand.

One field, one support

backlog points at the artefact on whatever support it lives: an issue reference where the backlog lives with the ticket provider, a project-relative path where it lives in Markdown. This is what persistence.md already prescribes — "Use native fields when supported; otherwise use explicit ids or project-relative paths" — and its companion rule is why nothing is copied: "Never mirror one Story across supports."

Neither the type of work nor the originating ticket is repeated here. The backlog artefact carries them in type, work_kind and source.

A folder with no backlog artefact is a normal state: backlog is null, and only then does the file carry its own source and type.

Done When

  • From a run file, the chain to the epic is walkable with no missing edge.
  • No field in the file duplicates one that exists on the backlog artefact, asserted field by field.
  • A story delivered in two folders produces two files pointing at it, and the story is unchanged.
  • A folder created without a backlog artefact is valid and readable.
  • Three invocations of the same skill produce three step entries, not one.
  • Nothing writes a downward link.

Completion Evidence

A real feature whose cost, sessions, steps, produced files and originating ticket are reachable from one another in both directions of reading.

Relations

Field Value
parent #648
depends_on #620

Activity

  1. added this to the milestone on Aug 14, 2026
  2. blafourcade commented on Aug 18, 2026

    @blafourcade
    ContributorAuthor

    Each step must carry the run_id. Time is not enough.

    The join from a delivery folder to a session is task_id, and it is exact. The join from a step to the session that ran it was left to timestamps, and that fails on the case this project hits every day.

    Two sessions can work the same task at once — a background agent while you work, two worktrees on one feature. A step recorded from 10:04 to 10:12 overlaps both. Time cannot say which session ran it, so any cost split across steps is a guess wearing a decimal point.

    Two smaller reasons point the same way: nowIso() drops milliseconds, so two steps in the same second are indistinguishable; and a reader would have to trust that two files written by two different programs agree on an instant.

    The rule: time is never used to choose a session, only to split inside one.

    Join Key Exactness
    run → delivery folder task_id exact, already built
    step → session run_id exact, missing today
    step → tokens run_id + time window exact, because one session has one writer

    Timezones are not a concern here and never were: toISOString() emits UTC with a Z, so every timestamp already shares one clock.

    This may not be a file the skills write at all

    The steps in this issue are declared: a skill is expected to record its own boundary. The run journal started that way too, with a pointer the planning skills wrote, and it was replaced by observation for two reasons that apply here unchanged — it put a measurement concern inside skills whose concern is something else, and a declaration can silently stop happening while everything still looks fine.

    A skill start is observable: a recorded PostToolUse payload carries tool_name: "Skill" and tool_input: { skill }. The hook already listens on that event for attachment.

    So the question this issue should answer before it is built: are steps observed by the hook, or declared by the skills? #663 is measuring exactly that. If steps are observable, metadata.json keeps its upward link and its identity, and stops carrying a journal that something else records more reliably.

  3. blafourcade commented on Aug 18, 2026

    @blafourcade
    ContributorAuthor

    Confirmed placement: metadata.json lives in each task folder, aidd_docs/tasks/<yyyy_mm>/<task_id>/metadata.json, beside plan.md. That is what this issue already says; recording it here because the end-to-end diagram omitted both the location and the run_id on each step, and the omission was mine.

    Each step entry carries its run_id — the reason is on the comment above: two sessions can work the same task in the same time window, so a timestamp cannot say which one ran a step.

    And a scope reduction that follows from #663: since PreToolUse and PostToolUse both fire for the Skill tool with the real name, step boundaries are observed rather than declared. If the hook records them, this file keeps its identity and its upward link to the backlog, and stops carrying a journal that something else records more reliably — the same correction already made twice in this layer.

  4. blafourcade commented on Aug 28, 2026

    @blafourcade
    ContributorAuthor

    Moved to the front, second after #661

    A product decision taken 2026-08-28 puts this immediately behind #661.

    Once a person can be resolved across tools and machines, the next thing a team view needs is the link outward: which backlog item a session's work belonged to. That is what turns "tokens per skill" — which the local layer already reports for one person — into pull requests, features and bugs per epic. Without it, an aggregated view shows a bigger version of what one machine already shows, which is not a reason for anyone to look at it.

    It stays distinct from #720, deliberately. #720 groups the tasks the journal already records against each other; this one links a task to the item it delivers. Neither implies the other, and #720 is the cheaper half.

    Note for whoever picks this up: the journal already records a task declaration from a tool's own arguments (task_declared, with the path the tool named), on claude-code, cursor, codex and copilot. The path is there; what is missing is the identity file that says which backlog item that path is delivering.

  5. moved this from Ideation to Todo in AIDD Roadmapon Aug 28, 2026
  6. blafourcade commented on Sep 2, 2026

    @blafourcade
    ContributorAuthor

    Delivered by #706, squash-merged into next as 627408f.

  7. blafourcade commented on Sep 12, 2026

    @blafourcade
    ContributorAuthor

    Closed as a rescope.

    The delivered contract is backlog-link.json: one upward backlog reference plus writing provenance. The task folder itself is the identity.

    The omitted metadata.json fields are deliberate: step and file provenance already belong to the journal; branch and pull request are derivable from Git and the forge. Duplicating them would create a second source of truth.

    aidd telemetry report reads the link as by_backlog.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Fields

    Priority

    High

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions