Skip to content

Unify artifact frontmatter under a canonical metadata schema #537

Description

@williamthorsen

Problem

Artifacts written by skills (reviews, devlogs, plans, change summaries, response files, run summaries, deferred-findings, and orchestrated subagent outputs) record metadata in inconsistent shapes:

  • Inline pseudo-header (review-branch, summarize-change, respond-to-review): markdown lines below the H1 — e.g., Commit: abc123. Not machine-readable without bespoke parsing.
  • Partial YAML frontmatter (create-devlog, save-plan, wrap-up deferred-findings): a provenance: block plus a varying subset of identity fields.
  • No frontmatter at all (plan, orchestrated subagent outputs from reviewers/planners/architects/savings-analyzer): nothing.

Readers must hunt for the metadata they need, and when a ticket spans multiple branches and PRs, determining which branch/PR an artifact applies to requires git archaeology. The data is locally derivable at write time but isn't captured.

Concrete prior incident: ticket COMPPLAN-800 had three sequential branches and three sequential PRs (#279, #280, #283). Answering "which branch does this saved review apply to?" required git cat-file archaeology because the original commits had since been squash-and-force-pushed.

Context

Three structural facts shape this work:

  1. get-session-context already returns branch_name and platform in its manifest — branch is free at write time.
  2. The codebase already uses YAML frontmatter in three artifact types (create-devlog, save-plan, wrap-up). This is the established pattern; the inline-header artifacts are outliers.
  3. PR is resolvable at write time via gh pr list --head <branch> --state all (GitHub) or the existing Bitbucket query pattern.

Affected artifact-writing surfaces:

Bucket A — inline-header artifacts (migrate to YAML frontmatter):

  • review-branch, summarize-change, respond-to-review

Bucket B — partial-frontmatter artifacts (extend with missing fields):

  • create-devlog, save-plan, wrap-up (deferred-findings)

Bucket C — no-frontmatter artifacts (introduce frontmatter):

  • plan (interactive)
  • orchestrated-reviewer, aspect-code-reviewer, aspect-test-reviewer, aspect-silent-failure-reviewer, code-simplification-reviewer
  • orchestrated-planner (orchestration-plan.md; sibling .json unaffected)
  • orchestrated-architect, orchestrated-coder (the latter inherits via summarize-change)
  • planner, plan-reviewer, plan-reviser, savings-analyzer, refine-plan
  • Orchestrator Phase 5 run-summary

Solution

1. Define a universal artifact frontmatter schema

Add a new "Universal artifact frontmatter" section to _data/artifact-conventions.md defining the canonical schema once:

---
provenance:
  skill: <skill-name>                # required — the skill that wrote this artifact
  timestamp: <ISO 8601 UTC>          # required — write time
  baseSha: <short SHA>                # optional — omit if origin/main unresolvable
  isInteractive: true|false          # required
  refinedBy: <skill-name>             # optional — existing convention
  model: <model id>                   # optional — present when an AI model authored the body
ticket_id: <id>                       # optional — omit when null
ticket_ref: <display ref>             # optional — omit when ticket_id is null
branch: <branch name>                 # required
commit: <short SHA of HEAD>           # required — HEAD at write time
pr: <full URL>                        # optional — omit when no PR or lookup fails
author: <name(s)>                     # optional — used by review artifacts
commits: [<sha>, ...]                 # optional — used by devlogs
run_id: <run id>                      # optional — present in orchestrated runs
---

Field naming conventions: keys inside provenance: use camelCase (preserves existing convention; 544+ historical artifacts use this spelling and refine-plan reads them). All other top-level keys use snake_case. This split is documented explicitly in the new section.

Existing per-artifact sections (## Plan provenance, ## Devlog frontmatter, ## Deferred-findings frontmatter) refactor to inherit from the universal section and document only artifact-specific extensions.

2. Specify shared PR resolution

Add _data/pr-resolution.md:

  • GitHub: gh pr list --head "$BRANCH" --state all --json url --jq '.[0].url // empty'
  • Bitbucket: existing bitbucketPullRequest list query, extract links.html.href
  • URL format: GitHub PR is https://github.com/{owner}/{repo}/pull/{n} (not /issues/); Bitbucket is https://bitbucket.org/{workspace}/{repo}/pull-requests/{n}. The CLI returns the correct URL — no interpolation.
  • --state all (GitHub) so closed/merged PRs are still resolvable for post-merge artifacts.
  • Failure mode: on CLI unavailable, auth error, network error, or >5s timeout — omit pr: line, emit one brief warning in the agent's text output, proceed with the write. Never block.

3. Migrate all affected artifact-writing skills

For each skill in Buckets A, B, and C:

  • Update ## Output format to reference the universal schema and declare artifact-specific extensions.
  • Bucket A: remove inline pseudo-header from the artifact template; H1 remains.
  • Bucket C: introduce YAML frontmatter at the top.
  • All buckets: saving step writes branch + commit from session context + git rev-parse, and invokes shared PR resolution.

4. No backfill

Historical artifacts are not modified. The 544 existing artifacts with camelCase provenance.* keys remain valid under the preserved convention.

5. Consumer compatibility

refine-plan continues to read existing provenance blocks as-is (camelCase preserved). Orchestrator trust evaluation (CHANGELOG #317, "refinement-elevated" classification) reads provenance.refinedBy — unchanged.

Acceptance criteria

  • _data/artifact-conventions.md has a new "Universal artifact frontmatter" section defining the canonical schema, with the camelCase/snake_case rule documented explicitly.
  • Existing per-artifact sections in _data/artifact-conventions.md (## Plan provenance, ## Devlog frontmatter, ## Deferred-findings frontmatter) inherit from the universal section and document only extensions.
  • _data/pr-resolution.md exists and documents GitHub and Bitbucket dispatch, URL formats, --state all rationale, and the failure-handling rule.
  • Every skill in Buckets A, B, and C writes artifacts whose frontmatter conforms to the universal schema, with artifact-specific extensions where applicable.
  • Bucket A artifacts no longer contain the inline Commit: / Timestamp: / Author: / Generated by: lines below the H1; equivalent data is in YAML frontmatter.
  • When the current branch has an open or merged PR, pr: contains the full URL (GitHub: /pull/{n}, Bitbucket: /pull-requests/{n}).
  • When the branch has no PR, the pr: line is omitted entirely (not written as null or none).
  • When PR resolution fails (CLI unavailable, auth error, network error, timeout > 5s), the artifact is still written, pr: is omitted, and one brief warning is surfaced in the agent's text output.
  • branch: is always present in every new artifact, with the raw branch_name from session context (no sanitization).
  • commit: is always present in every new artifact, with the short HEAD SHA at write time.
  • No existing artifacts are modified (no backfill).
  • refine-plan and orchestrator trust evaluation continue to work unmodified — provenance.* camelCase convention is preserved.
  • End-to-end verification: invoke a representative skill from each bucket (a) on a branch with a PR, (b) on a branch without a PR, (c) with PR lookup forced to fail; verify the expected frontmatter shape in each case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureAdded or improved external functionalityscope:agents

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions