Files
msd-core/docs/features/task-content-resolution-seam.md
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
2026-10-06 01:47:40 +02:00

43 lines
2.8 KiB
Markdown

---
id: 3970
title: Per-Task External-Tracker Content-Resolution Seam
group: v1.7.0 Features
---
**Purpose:** Let a capability declare that an external issue tracker — beads, Linear, Jira,
GitHub Issues — owns a task's *content* (`<action>`/`<verify>`/`<acceptance_criteria>`/
`<read_first>`/`<done>`), not just its status, so `execute-plan.md` can resolve that content
from the tracker at execution time instead of reading it inline out of `PLAN.md`.
**What changed (ADR-3646, #3970):**
- A new optional feature-body manifest field, `taskContentResolver`, declares a `trackerPrefix`
(matched against a task's `<task tracker-id="beads:MSD-42">` attribute — everything before the
first `:`) and a bounded `invoke` (`binary`, `args` carrying the `{{id}}` placeholder,
`timeoutMs`).
- `execute-plan.md`'s per-task loop gains one new, unconditional call before that task's
`read_first` gate: `msd_run task resolve-content --plan <path> --task-id <tracker-id> --raw`.
A task with no `tracker-id` attribute is unaffected — the call is only made when the attribute
is present, and resolves instantly to a no-op for every project that declares none.
- **The safety property is a real process exit code, not a prose dispatch.** No capability
registered for the tracker, or resolution succeeds with empty content, exits `0` with
`resolved: false` and falls back to inline `PLAN.md` — the one legitimate pre-migration
boundary case. Resolution succeeding with non-empty content exits `0` with `resolved: true` and
its `content` supersedes the task's inline fields for every downstream gate in the execute step.
A resolver that is declared but fails — tracker unreachable, id not found, timeout, malformed
JSON — makes `task resolve-content` itself **exit non-zero**, which `execute-plan.md` treats as
a **hard halt**: stop, surface the tracker-id/prefix/stderr, never fall back to stale
`PLAN.md` content.
- `execute:task` is a new dispatch shape below wave granularity, deliberately **not** one of the
12 existing loop extension points (`discuss:pre` … `ship:post`) and not routed through
`msd_run loop render-hooks <point>` / `activeHooks`. It exists because the existing
`step`/`gate` prose-dispatch mechanism cannot deliver a hard-halt guarantee while dispatch
reliability at that layer is an open concern (#3647) — see ADR-3646's Context and Rejected
Alternatives for the full reasoning.
See [Develop a task-content resolver capability](../how-to/develop-a-task-content-resolver-capability.md)
for the authoring walkthrough, [Capability manifest → `taskContentResolver`](../reference/capability-manifest.md#taskcontentresolver)
for the field reference, and
[`loop-hook-dispatch.md`](../../msd-core/references/loop-hook-dispatch.md#the-executetask-point-a-different-shape)
for how `execute:task` differs from the twelve prose-dispatched points.