@gitdocket/core 0.0.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/search.ts ADDED
@@ -0,0 +1,147 @@
1
+ // Read-side ranked text search over a bundle's files. Lives in core so every
2
+ // surface (CLI, MCP, web) shares one definition of "search" — hits carry
3
+ // the owning concept's id/title when the file parses as one.
4
+ //
5
+ // Search is the entry-point finder: queries are tokenized, terms
6
+ // match anywhere in any order, and results come back best-first — more terms
7
+ // matched beats fewer, title/id matches outweigh body matches. One hit per
8
+ // file, snippeted at its best-matching line. No index build, no dependencies;
9
+ // same bundle + query always yields the same ordering.
10
+
11
+ import type { Bundle } from "./bundle";
12
+ import type { FileStore } from "./filestore";
13
+ import { resolveLink } from "./lint";
14
+
15
+ /** A neighboring concept: identity only, never contents — hits stay compact. */
16
+ export interface NeighborRef {
17
+ path: string;
18
+ id?: string;
19
+ title?: string;
20
+ }
21
+
22
+ export interface SearchHit {
23
+ path: string;
24
+ /** 1-based line of the best-matching snippet. */
25
+ line: number;
26
+ text: string;
27
+ id?: string;
28
+ title?: string;
29
+ /** Distinct query terms that matched this file. */
30
+ matched: string[];
31
+ score: number;
32
+ /** Outbound links of the owning concept (concept hits only). */
33
+ links?: NeighborRef[];
34
+ /** Concepts that link here (concept hits only). */
35
+ backlinks?: NeighborRef[];
36
+ }
37
+
38
+ const FIELD_WEIGHT = 3; // term found in frontmatter title/id
39
+ const BODY_WEIGHT = 1;
40
+
41
+ const tokenize = (s: string): string[] =>
42
+ s
43
+ .toLowerCase()
44
+ .split(/[^a-z0-9]+/)
45
+ .filter(Boolean);
46
+
47
+ /** A term hits a token by prefix — "navig" finds "navigation", "dkt" finds a Docket ID. */
48
+ const hasMatch = (tokens: string[], term: string): boolean =>
49
+ tokens.some((t) => t.startsWith(term));
50
+
51
+ export async function searchBundle(
52
+ store: FileStore,
53
+ bundle: Bundle,
54
+ query: string,
55
+ opts: { limit?: number } = {},
56
+ ): Promise<SearchHit[]> {
57
+ const terms = [...new Set(tokenize(query))];
58
+ if (terms.length === 0) return [];
59
+ const limit = Math.max(1, opts.limit ?? 20);
60
+
61
+ const byPath = new Map(bundle.concepts.map((c) => [c.path, c]));
62
+ const allPaths = await store.list();
63
+ const exists = new Set(allPaths);
64
+ const hits: SearchHit[] = [];
65
+
66
+ for (const path of allPaths) {
67
+ const lines = (await store.read(path)).split("\n");
68
+ const lineTokens = lines.map(tokenize);
69
+ const fm = byPath.get(path)?.fm;
70
+ const id = typeof fm?.id === "string" ? fm.id : undefined;
71
+ const fieldTokens = tokenize(`${id ?? ""} ${fm?.title ?? ""}`);
72
+
73
+ const matched = terms.filter(
74
+ (t) =>
75
+ hasMatch(fieldTokens, t) || lineTokens.some((lt) => hasMatch(lt, t)),
76
+ );
77
+ if (matched.length === 0) continue;
78
+
79
+ // Snippet: the line matching the most terms, earliest on ties. Frontmatter
80
+ // lines participate, so title/id-only matches still snippet somewhere real.
81
+ let best = 0;
82
+ let bestCount = 0;
83
+ for (const [i, tokens] of lineTokens.entries()) {
84
+ const count = terms.filter((t) => hasMatch(tokens, t)).length;
85
+ if (count > bestCount) {
86
+ bestCount = count;
87
+ best = i;
88
+ }
89
+ }
90
+
91
+ hits.push({
92
+ path,
93
+ line: best + 1,
94
+ text: (lines[best] ?? "").trim(),
95
+ ...(id ? { id } : {}),
96
+ ...(fm?.title ? { title: fm.title } : {}),
97
+ matched,
98
+ score: matched.reduce(
99
+ (s, t) => s + (hasMatch(fieldTokens, t) ? FIELD_WEIGHT : BODY_WEIGHT),
100
+ 0,
101
+ ),
102
+ });
103
+ }
104
+
105
+ // Best-first after the full scan: more terms, then weight, then path for
106
+ // a deterministic total order. Limit applies here, not during the scan.
107
+ hits.sort(
108
+ (a, z) =>
109
+ z.matched.length - a.matched.length ||
110
+ z.score - a.score ||
111
+ a.path.localeCompare(z.path),
112
+ );
113
+ const top = hits.slice(0, limit);
114
+
115
+ // Neighborhood: the hit is the foothold, its links are the map.
116
+ // Derived from the same parse lint/index use — no second source of truth —
117
+ // and only for the hits that survived ranking.
118
+ const inbound = new Map<string, Set<string>>();
119
+ for (const c of bundle.concepts)
120
+ for (const l of c.links) {
121
+ if (!l.internal) continue;
122
+ const to = resolveLink(c.path, l.target);
123
+ if (!to || to === c.path) continue;
124
+ inbound.get(to)?.add(c.path) ?? inbound.set(to, new Set([c.path]));
125
+ }
126
+ const ref = (path: string): NeighborRef => {
127
+ const fm = byPath.get(path)?.fm;
128
+ return {
129
+ path,
130
+ ...(typeof fm?.id === "string" ? { id: fm.id } : {}),
131
+ ...(fm?.title ? { title: fm.title } : {}),
132
+ };
133
+ };
134
+ for (const hit of top) {
135
+ const concept = byPath.get(hit.path);
136
+ if (!concept) continue; // non-concept file: no neighborhood, no error
137
+ const outbound = new Set<string>();
138
+ for (const l of concept.links) {
139
+ if (!l.internal) continue;
140
+ const to = resolveLink(concept.path, l.target);
141
+ if (to && to !== concept.path && exists.has(to)) outbound.add(to);
142
+ }
143
+ hit.links = [...outbound].map(ref);
144
+ hit.backlinks = [...(inbound.get(hit.path) ?? [])].sort().map(ref);
145
+ }
146
+ return top;
147
+ }
@@ -0,0 +1,53 @@
1
+ [
2
+ {
3
+ "version": "0.1.1",
4
+ "bodies": {
5
+ "docket-pickup": "Use this workflow only for authorized tracked Docket work. Pickup authority requires positive evidence: a Docket ID, an unambiguous reference to an existing tracked item, or an explicit request to select the next Docket or backlog item. Generic implementation language does not select pickup. A concrete direct request proceeds in the user's stated scope without creating, starting, or adopting Docket work; do not invoke this workflow for it.\n\n1. **Resolve the target and command**: a Docket ID authorizes `docket task start <ID> --json`. Resolve an unambiguous tracked-item reference to its ID, then use the same named command. Only explicit next-Docket-task or backlog-selection language authorizes bare `docket task start --json`. If an apparent tracked reference remains ambiguous, perform only focused resolution or ask for clarification; never omit the ID, substitute the top ready item, or mutate `.docket/active-task`.\n2. **Start through the engine**: run only the command authorized in step 1. If the command fails, stop; do not rename the session or begin tracked work.\n3. **Use the returned title intent**: read `suggestedSessionTitle` from the successful structured result. Do not rebuild it from prompt text or separately queried task fields.\n4. **Best-effort rename**: ask the current harness's native adapter to name the calling session with that exact value. If the host has no current-session naming capability, the capability is unavailable, or the rename fails, continue silently without retrying or treating pickup as failed.\n5. **Hand off context**: use the returned task, epic, dependency, linked-concept, and commit fields as the context packet, then begin the requested tracked work.\n\nStored status changes go through the engine's canonical transition table; invalid transitions are rejected, `done` and `closed` are terminal, and moving to `closed` requires a disposition note. Only `done` satisfies dependencies or counts as completion.\n\nThe engine owns task selection, the state-machine-checked status transition, active-task state, title derivation, and the context packet; the pickup workflow owns only their sequence, and a native adapter owns only its bounded rename binding.",
6
+ "docket-epic": "Supervise the named epic until its acceptance criteria support explicit closure or one concrete blocker prevents safe progress. Docket files and task-linked Git history are the durable source of truth. Native worker, wait, follow-up, notification, and isolated-checkout capabilities are optional accelerators; they never change readiness or completion semantics.\n\n## 1. Establish the authoritative graph\n\n1. Confirm the user named an epic and authorized running it, not merely reviewing it. Read the epic file, verify that it is an Epic with an ID and title, then run `docket task list --epic <EPIC-ID> --all --json` and `docket ready --json`.\n2. Derive one manager title from those authoritative epic fields, exactly `Epic <ID> — <title>`, and retain it for the entire supervision run. Ask the current harness's native adapter to apply it to the calling manager session. Unsupported, unavailable, or failed rename capability is a silent no-op; it never blocks supervision.\n3. Record the manager baseline: current Git commit and branch, working-tree state, epic status and acceptance criteria, every child status and dependency, already-linked task commits, and the stopping condition. Preserve unrelated user changes; do not hide, overwrite, or move them into a worker checkout.\n4. Use the engine's ready result as authoritative. Ready is derived, never written: a task is ready only when its stored status is `todo` and every dependency resolves to `done`; an unknown dependency blocks it. The ready queue puts ranked tasks first by ascending `rank`; rank ties and the unranked tail use priority (`p0` through `p3`), then ascending task ID as the stable fallback. Filter that result to the named epic; never dispatch from a remembered or hand-derived ready list.\n5. If no child is ready but unfinished children remain, inspect their dependency and blocked-state evidence. Continue only when Docket state identifies a resolvable in-scope next action; otherwise prepare the blocker receipt in section 6.\n\n## 2. Preflight isolation and likely write overlap\n\nBefore creating any worker, inspect each ready child's context, acceptance criteria, linked concepts, and likely implementation/test/generated-document surfaces. Parallel writing is allowed only when every selected child is dependency-independent, likely write sets are materially distinct, each worker has a separate checkout at the exact accepted manager ref, and the manager can integrate and verify results one at a time. Treat shared workflow templates, generated adapters, dependency manifests, schemas, migrations, indexes, and central registries as likely overlap unless evidence shows otherwise.\n\nIf any condition is unknown or false—or if the host lacks a verified worker, wait/follow-up, notification, or isolated-checkout binding—use the mandatory serial fallback: run exactly one child at a time in the calling session or one isolated worker, integrate it fully, refresh Docket state, and only then choose the next child. Never run concurrent writers in one checkout. A shared `.docket/active-task` is single-checkout state, not a coordination mechanism.\n\n## 3. Dispatch one bounded child contract\n\nFor each selected child, provide the exact task ID, accepted baseline commit, isolated checkout or serial location, permitted scope, acceptance criteria, relevant linked concepts, expected verification, and these constraints:\n\n- follow [the pickup workflow](/workflows/docket-pickup.md) before implementation and [the close workflow](/workflows/docket-close.md) only after the task is actually complete;\n- change only the named child and required reconciliation surfaces; do not start siblings, close the epic, or invent orchestration infrastructure;\n- preserve unrelated changes, use task-linked commits, clear the checkout's active-task marker after close, and return commit hashes, verification results, interventions, and exact blockers;\n- do not claim integration or readiness changes from the worker checkout—the manager re-establishes those facts after accepting the result.\n\nWhen no native worker binding is available, execute this same contract serially in the calling session. The contract, not process count, defines supervision.\n\nAn isolated child session follows pickup normally and keeps its own `<ID> — <title>` task name; never apply the manager title to that child. In the serial fallback, child pickup can temporarily rename the shared calling session, so immediately after every successful child pickup reapply the retained `Epic <ID> — <title>` manager title before implementation continues. A failed or unsupported restoration remains a silent no-op and does not change task state or the child contract.\n\n## 4. Inspect and integrate one result at a time\n\n1. Treat a worker report as a lead, not authority. Inspect its checkout or ref, diff, task file, checked or explicitly waived criteria, Outcome, Log, commit trailers, verification output, and clean active-task state.\n2. Reject or return incomplete, out-of-scope, unverified, or ambiguously based work. Keep the branch/worktree/ref recoverable and state the required correction. Never mark the child done merely because the worker said it finished.\n3. Integrate one accepted commit series into the manager checkout. Resolve only understood in-scope conflicts; otherwise stop integration, preserve both refs and the conflict evidence, and produce a blocker receipt. Do not integrate a second result against unresolved or unverified state.\n4. Run the verification proportionate to the accepted diff, regenerate derived state with `docket index`, then rerun `docket task list --epic <EPIC-ID> --all --json` and `docket ready --json`. Re-read the epic and Git history. Select further work only from this refreshed state.\n5. At every accepted boundary, durable task files plus integrated Git commits must be sufficient for a replacement manager to resume. Native task IDs and wait cursors are useful transient handles, never the recovery source of truth.\n\n## 5. Review and close the epic explicitly\n\nAll children being done is necessary evidence, not epic completion. When no unfinished child remains, review every epic acceptance criterion against integrated task Outcomes, diffs, tests, decisions, and reconciled docs. Run final repository verification. If any criterion lacks evidence, create or identify the smallest in-scope follow-on child and continue; do not check or waive a criterion silently.\n\nWhen every criterion is satisfied or explicitly waived with a reason, apply the close workflow to the epic itself: write its Outcome with commit evidence, reconcile affected concepts, close through the engine, regenerate the index, update the log, and commit with the epic's task trailer. Verify the integrated epic status rather than inferring it from the close command's prose.\n\n## 6. Return one consolidated receipt\n\nReturn only after verified epic closure or a concrete blocker. Before returning, ask the native adapter to reapply the retained manager title once so the calling session ends on the epic rather than incidental child work; unsupported or failed rename remains a silent no-op. A completion receipt names the epic, integrated child and epic commits, verification performed, serial-versus-parallel choice and why, interventions or conflicts, and any deliberately deferred follow-up. A blocker receipt names the exact failing child or epic criterion, dependency/decision/error, last accepted manager commit, preserved worker refs or worktrees, current Docket state, checks already attempted, and the single action needed to resume.\n\nDo not create an orchestration database, scheduler, permanent runner, or synthetic epic status. On interruption, restart this workflow from section 1: Docket and Git reveal completed children and the next authoritative ready set; absent native lifecycle state simply selects the serial fallback.",
7
+ "docket-task": "Create a work item conformant with the OKF task profile (bundled at `specs/okf-task-profile.md` when the repo carries it). The request describes the item (\"task: add X to Y, epic phase-1, depends on KEY-8\").\n\n**Prefer the engine**: `docket task create --title \"…\" --epic /work/epics/… --deps KEY-x,KEY-y --priority p1 --description \"…\"` handles ID assignment, file placement, and a conformant template. Then edit the created file to fill in real `# Context` links and `# Acceptance Criteria`, and run `docket index`. The manual steps below are the fallback when the engine is unavailable.\n\n1. **Assign the ID**: work items (tasks AND epics) take the next number in the project sequence under the key from `docket.yaml` — `grep -rh \"^id: KEY-\" <bundle>/`, max + 1. Decisions likewise on their own prefix (default `DEC-`). Verify the result is unused.\n2. **Write the file** at `work/tasks/<ID>-<short-slug>.md` (epics → `work/epics/`, decisions → `decisions/`) with frontmatter: `type`, `title`, `description` (one sentence), `id`, `status: todo`, `epic` (bundle-absolute link — ask or infer; a task without an epic is allowed but noted), `depends_on` (task IDs, omit if none), `priority` (default `p2`), `assignee`, `tags`, `timestamp` (current UTC ISO 8601).\n3. **Body**: `# Context` — link the relevant specs/docs/decisions (bundle-absolute paths); `# Acceptance Criteria` — checkboxes, verifiable, few. Omit `# Log` until there's something to log.\n4. **Regenerate the index** (`docket index`) and add a `log.md` entry when the item is notable.\n5. If work starts now, follow [the pickup workflow](/workflows/docket-pickup.md). It delegates task state and context-packet mechanics to `docket task start <ID> --json`; never set the active task without the status move or vice versa. Pausing later is `docket task stop` (clears the active task, status stays).\n\nNever skip or reuse numbers, never hand-maintain task lists inside epic files, never mark `status` beyond `todo` at creation.",
8
+ "docket-groom": "Run this full backlog-hygiene audit only when the user explicitly asks to groom or audit the backlog, find stale or inconsistent work, or review task hygiene. Ordinary status, orientation, what-is-next, and review requests use `docket overview --json` instead and stop when that structured response is sufficient.\n\nRead every file in `work/` and report, then apply agreed fixes. Start from the engine: `docket ready --json` and `docket task list --json`.\n\nThe engine owns ready/list derivation and task mutation mechanics; the groom workflow owns audit judgment, proposed changes, and the authorization boundary.\n\n1. **Derive ready**: `docket ready` (never compute by hand). Ready is derived, never written: a task is ready only when its stored status is `todo` and every dependency resolves to `done`; an unknown dependency blocks it. The ready queue puts ranked tasks first by ascending `rank`; rank ties and the unranked tail use priority (`p0` through `p3`), then ascending task ID as the stable fallback.\n2. **Flag inconsistencies**:\n - `in-progress` tasks with no commits trailer-matching their ID (`git log --grep \"Task: <ID>\"`) and no Log entry in 7+ days → probably stalled; propose `blocked` or `todo`.\n - `done` tasks with unchecked acceptance criteria or missing `# Outcome`.\n - `closed` tasks without a concrete `# Disposition` and replacement links when applicable.\n - `depends_on` pointing at nonexistent or done-and-superseded IDs; broken bundle links (`docket lint`).\n - Epics without a `spec` link; tasks without an `epic` link.\n - `index.md` out of sync (`docket index` fixes; report if it changes anything).\n3. **Propose, then apply**: present findings compactly; on confirmation (or when running autonomously, for mechanical fixes only) update files via `docket task move`/`docket task log`, regenerate the index, and add a `**YYYY-MM-DD**` line to affected `# Log` sections explaining status changes.\n4. Commit as `chore(docket): groom backlog` (no task trailer — `docket task stop` first).\n\nNever change priorities or close tasks without saying so; grooming narrates every mutation.",
9
+ "docket-close": "Conclude the given task (default: the ID in `.docket/active-task`). A terminal move is the moment the wiki gets paid — don't skip steps.\n\nStored status changes go through the engine's canonical transition table; invalid transitions are rejected, `done` and `closed` are terminal, and moving to `closed` requires a disposition note. Only `done` satisfies dependencies or counts as completion.\n\nThe engine owns the state-machine-checked terminal move and dated Log mutation; the close workflow owns the choice between completion (`done`) and non-completion (`closed`), Outcome or Disposition judgment, documentation review, and derived index/log reconciliation.\n\n1. **Choose the terminal meaning explicitly**. Completion is the backward-compatible default: every acceptance criterion is checked (or explicitly waived in the Outcome with a reason), and the target state is `done`. Use non-completion only when the user explicitly intends to abandon, decline, supersede, or otherwise discontinue the work; leave unmet criteria unchecked, target `closed`, and require a concrete disposition reason. If neither meaning is supported, say so and stop.\n2. **Write the terminal narrative**. For completion, write `# Outcome`: what actually shipped, citing commit hashes found via `git log --grep \"Task: <ID>\" --oneline` plus the task file's history, with anything descoped or discovered. For non-completion, write `# Disposition`: why the work ended, what remains unmet, and any replacement task or decision links; do not claim that work shipped.\n3. **Reconcile the docs** (the LLM-first step): from the task diff and terminal narrative, identify wiki concepts (`specs/`, `reference/`, `decisions/`, plan documents) the conclusion invalidates or extends. Update them now. If a choice foreclosed alternatives, record it as a `type: Decision` concept and link it from the Outcome or Disposition.\n4. **Update state**: for completion, run `docket task close <ID> --note \"…\"`; for non-completion, run `docket task close <ID> --without-completion --note \"<disposition>\"`. Then run `docket index`, add a `log.md` entry that says completed or closed, and check dependency and epic effects. Only `done` unblocks dependents or counts toward epic completion; a terminal epic may be `closed` without all children being done.\n5. **Commit everything together** — task file + reconciled docs + index/log — with the `Task: <ID>` trailer (keep the task active so the hook injects it, or add it manually), then `docket task stop` to clear the active task.\n\nThe commit that concludes a task must contain the doc reconciliation — that's the product's core promise.",
10
+ "docket-standup": "Report project status from files + git. **Mutate nothing.** Pull state from the engine (`docket task list --json`, `docket ready --json`); use git for the activity window.\n\n1. **Window**: since the last standup or the range given (default: 7 days).\n2. **Done**: tasks whose status flipped to `done` in the window — from `git log -p --since=<window> -- <bundle>/work/tasks/` (status line changes) — one line each: ID, title, outcome gist.\n3. **Closed without completion**: tasks whose status flipped to `closed` in the window — one line each: ID, title, and disposition; keep them separate from shipped work.\n4. **In flight**: `in-progress` tasks with their latest Log entry and commit count from `git log --grep \"Task: <ID>\" --since=<window>`. Call out any with zero commits and no Log movement.\n5. **Ready next**: derived ready list (`docket ready`), top 5. Ready is derived, never written: a task is ready only when its stored status is `todo` and every dependency resolves to `done`; an unknown dependency blocks it. The ready queue puts ranked tasks first by ascending `rank`; rank ties and the unranked tail use priority (`p0` through `p3`), then ascending task ID as the stable fallback.\n6. **Blocked**: `blocked` tasks with the blocking reason from their Log.\n7. **Epic pulse**: one line per active epic — fraction of its tasks done, with closed children called out separately (derive by grep, don't trust hand-maintained lists).\n\nOutput: compact markdown suitable for pasting into a chat. Flag (don't fix) any inconsistencies noticed along the way — fixing belongs to [docket-groom](/workflows/docket-groom.md).",
11
+ "docket-state-of-play": "Refresh the optional bundle-root `overview.md` re-entry note. The engine parses, ages, and renders this authored summary but never writes it; live task status, readiness, progress, and activity stay in the derived overview.\n\n1. **Read the evidence**: run `docket overview --json`; read the product spec, current epics and tasks, recent Outcomes, explicit Decision concepts, `log.md`, recent task-linked commits, and the existing `overview.md` when present. Treat the derived overview as execution truth and the product spec/decisions as direction truth.\n2. **Write only the re-entry through-line**: summarize a few recent outcomes rather than commits, then name the one or few current/next epics or frontiers—including work already underway—with enough context to understand the move. Put the canonical resume target first when one exists; multiple real frontiers remain multiple authored links rather than an engine-selected winner. Add Worth knowing only for a decision, constraint, discovery, risk, parked thread, or useful wiki destination that materially helps re-entry. Use concrete nouns and consequences, link claims to bundle evidence, and omit empty material instead of writing filler. The preserved project preamble owns the recognizable full name, concise purpose, and other durable product introduction; do not repeat it here, and do not infer missing identity. Repeat a derived fact only when it explains why something matters, never to copy an inventory.\n3. **Write the linked note**: use the full output of `git rev-parse HEAD` as `as_of` and the current UTC ISO-8601 time as `reviewed_at`. What we've done recently and What's up next are required and non-empty. Worth knowing is optional; omit the heading when it would be empty.\n\n ```markdown\n ---\n format: re-entry/v2\n as_of: <full commit sha>\n reviewed_at: <timestamp>\n ---\n\n # Project re-entry\n\n ## What we've done recently\n\n - <outcome and consequence with a link to evidence>\n\n ## What's up next\n\n - <current or next frontier and why it matters, linked to its epic or task>\n\n ## Worth knowing\n\n - <optional decision, constraint, discovery, risk, or parked thread with a useful link>\n ```\n\n4. **Apply freshness honestly**: five task-linked commits after `as_of` or fourteen days after `reviewed_at` makes the note need review. Renderers keep the visibly dated last-known context readable rather than hiding it or presenting it as fresh. Refresh when the re-entry through-line materially changes, not merely to reset a clock. After a task close that changes the note, stamp the close commit in a separate tracker-only refresh so it starts at zero task-linked commits behind.\n5. **Verify and commit**: run `docket overview` and `docket lint`; confirm the linked sections and freshness are accurate. Commit as `chore(docket): refresh product context` with no Task trailer (`docket task stop` first).\n\nA missing `overview.md` is valid and renders no placeholder. Earlier formats remain readable and unchanged, but renderers label legacy prose and `re-entry/v1` as needing review. Never migrate them automatically; the next meaningful refresh replaces the file with the linked form above.",
12
+ "docket-freshness": "Close-time reconciliation is prospective — it fires only when a task closes, and only for that task's diff. This workflow is the retrospective complement: periodically re-ask \"what does this invalidate?\" across everything that happened since the last sweep.\n\n1. **Find the anchor**: the most recent `**Freshness**` entry in `log.md` holds the watermark sha. If none exists (first run), sweep the full history.\n2. **Collect the range**: `git log <sha>..HEAD --name-only` (keep trailers). Partition the commits:\n - **Trailerless** — the high-risk bucket: nobody ever asked the reconciliation question. Give each the full treatment: from its changed paths, which concepts (`specs/`, `reference/`, `decisions/`, plan documents) does it invalidate or extend?\n - **Trailered** (`Task: KEY-n`) — reconciliation should have happened at close. Spot-check: did closes that plausibly invalidated docs actually touch them?\n3. **Rotate a deep read**: pick the 1–2 concepts in `specs/` and `reference/` with the oldest last-modified commit and verify their content against current reality (code, plan). This catches drift that has no local commit at all — don't skip it just because the commit range is clean.\n4. **Propose, then apply**: present findings compactly (per doc: what's stale, which commit made it so). On confirmation — or autonomously for unambiguous factual fixes only — update the docs.\n5. **Stamp the watermark**: append to today's section of `log.md`:\n\n ```\n - **Freshness** — reviewed through `<short-sha of HEAD>` (<n> commits, <k> trailerless): <one-line findings summary, or \"no drift found\">.\n ```\n\n A \"no drift found\" stamp is a real result — record it; the recorded null finding is what makes the next sweep cheap.\n6. Commit doc fixes and the watermark together as `chore(docket): freshness review` (`docket task stop` first — no task trailer).\n\nNever end a sweep without stamping the watermark, even when nothing changed."
13
+ }
14
+ },
15
+ {
16
+ "version": "0.1.0",
17
+ "bodies": {
18
+ "docket-pickup": "Use this workflow only for authorized tracked Docket work. Pickup authority requires positive evidence: a Docket ID, an unambiguous reference to an existing tracked item, or an explicit request to select the next Docket or backlog item. Generic implementation language does not select pickup. A concrete direct request proceeds in the user's stated scope without creating, starting, or adopting Docket work; do not invoke this workflow for it.\n\n1. **Resolve the target and command**: a Docket ID authorizes `docket task start <ID> --json`. Resolve an unambiguous tracked-item reference to its ID, then use the same named command. Only explicit next-Docket-task or backlog-selection language authorizes bare `docket task start --json`. If an apparent tracked reference remains ambiguous, perform only focused resolution or ask for clarification; never omit the ID, substitute the top ready item, or mutate `.docket/active-task`.\n2. **Start through the engine**: run only the command authorized in step 1. If the command fails, stop; do not rename the session or begin tracked work.\n3. **Use the returned title intent**: read `suggestedSessionTitle` from the successful structured result. Do not rebuild it from prompt text or separately queried task fields.\n4. **Best-effort rename**: ask the current harness's native adapter to name the calling session with that exact value. If the host has no current-session naming capability, the capability is unavailable, or the rename fails, continue silently without retrying or treating pickup as failed.\n5. **Hand off context**: use the returned task, epic, dependency, linked-concept, and commit fields as the context packet, then begin the requested tracked work.\n\nStored status changes go through the engine's canonical transition table; invalid transitions are rejected, `done` and `closed` are terminal, and moving to `closed` requires a disposition note. Only `done` satisfies dependencies or counts as completion.\n\nThe engine owns task selection, the state-machine-checked status transition, active-task state, title derivation, and the context packet; the pickup workflow owns only their sequence, and a native adapter owns only its bounded rename binding.",
19
+ "docket-epic": "Supervise the named epic until its acceptance criteria support explicit closure or one concrete blocker prevents safe progress. Docket files and task-linked Git history are the durable source of truth. Native worker, wait, follow-up, notification, and isolated-checkout capabilities are optional accelerators; they never change readiness or completion semantics.\n\n## 1. Establish the authoritative graph\n\n1. Confirm the user named an epic and authorized running it, not merely reviewing it. Read the epic file, verify that it is an Epic with an ID and title, then run `docket task list --epic <EPIC-ID> --all --json` and `docket ready --json`.\n2. Derive one manager title from those authoritative epic fields, exactly `Epic <ID> — <title>`, and retain it for the entire supervision run. Ask the current harness's native adapter to apply it to the calling manager session. Unsupported, unavailable, or failed rename capability is a silent no-op; it never blocks supervision.\n3. Record the manager baseline: current Git commit and branch, working-tree state, epic status and acceptance criteria, every child status and dependency, already-linked task commits, and the stopping condition. Preserve unrelated user changes; do not hide, overwrite, or move them into a worker checkout.\n4. Use the engine's ready result as authoritative. Ready is derived, never written: a task is ready only when its stored status is `todo` and every dependency resolves to `done`; an unknown dependency blocks it. The ready queue puts ranked tasks first by ascending `rank`; rank ties and the unranked tail use priority (`p0` through `p3`), then ascending task ID as the stable fallback. Filter that result to the named epic; never dispatch from a remembered or hand-derived ready list.\n5. If no child is ready but unfinished children remain, inspect their dependency and blocked-state evidence. Continue only when Docket state identifies a resolvable in-scope next action; otherwise prepare the blocker receipt in section 6.\n\n## 2. Preflight isolation and likely write overlap\n\nBefore creating any worker, inspect each ready child's context, acceptance criteria, linked concepts, and likely implementation/test/generated-document surfaces. Parallel writing is allowed only when every selected child is dependency-independent, likely write sets are materially distinct, each worker has a separate checkout at the exact accepted manager ref, and the manager can integrate and verify results one at a time. Treat shared workflow templates, generated adapters, dependency manifests, schemas, migrations, indexes, and central registries as likely overlap unless evidence shows otherwise.\n\nIf any condition is unknown or false—or if the host lacks a verified worker, wait/follow-up, notification, or isolated-checkout binding—use the mandatory serial fallback: run exactly one child at a time in the calling session or one isolated worker, integrate it fully, refresh Docket state, and only then choose the next child. Never run concurrent writers in one checkout. A shared `.docket/active-task` is single-checkout state, not a coordination mechanism.\n\n## 3. Dispatch one bounded child contract\n\nFor each selected child, provide the exact task ID, accepted baseline commit, isolated checkout or serial location, permitted scope, acceptance criteria, relevant linked concepts, expected verification, and these constraints:\n\n- follow [the pickup workflow](/workflows/docket-pickup.md) before implementation and [the close workflow](/workflows/docket-close.md) only after the task is actually complete;\n- change only the named child and required reconciliation surfaces; do not start siblings, close the epic, or invent orchestration infrastructure;\n- preserve unrelated changes, use task-linked commits, clear the checkout's active-task marker after close, and return commit hashes, verification results, interventions, and exact blockers;\n- do not claim integration or readiness changes from the worker checkout—the manager re-establishes those facts after accepting the result.\n\nWhen no native worker binding is available, execute this same contract serially in the calling session. The contract, not process count, defines supervision.\n\nAn isolated child session follows pickup normally and keeps its own `<ID> — <title>` task name; never apply the manager title to that child. In the serial fallback, child pickup can temporarily rename the shared calling session, so immediately after every successful child pickup reapply the retained `Epic <ID> — <title>` manager title before implementation continues. A failed or unsupported restoration remains a silent no-op and does not change task state or the child contract.\n\n## 4. Inspect and integrate one result at a time\n\n1. Treat a worker report as a lead, not authority. Inspect its checkout or ref, diff, task file, checked or explicitly waived criteria, Outcome, Log, commit trailers, verification output, and clean active-task state.\n2. Reject or return incomplete, out-of-scope, unverified, or ambiguously based work. Keep the branch/worktree/ref recoverable and state the required correction. Never mark the child done merely because the worker said it finished.\n3. Integrate one accepted commit series into the manager checkout. Resolve only understood in-scope conflicts; otherwise stop integration, preserve both refs and the conflict evidence, and produce a blocker receipt. Do not integrate a second result against unresolved or unverified state.\n4. Run the verification proportionate to the accepted diff, regenerate derived state with `docket index`, then rerun `docket task list --epic <EPIC-ID> --all --json` and `docket ready --json`. Re-read the epic and Git history. Select further work only from this refreshed state.\n5. At every accepted boundary, durable task files plus integrated Git commits must be sufficient for a replacement manager to resume. Native task IDs and wait cursors are useful transient handles, never the recovery source of truth.\n\n## 5. Review and close the epic explicitly\n\nAll children being done is necessary evidence, not epic completion. When no unfinished child remains, review every epic acceptance criterion against integrated task Outcomes, diffs, tests, decisions, and reconciled docs. Run final repository verification. If any criterion lacks evidence, create or identify the smallest in-scope follow-on child and continue; do not check or waive a criterion silently.\n\nWhen every criterion is satisfied or explicitly waived with a reason, apply the close workflow to the epic itself: write its Outcome with commit evidence, reconcile affected concepts, close through the engine, regenerate the index, update the log, and commit with the epic's task trailer. Verify the integrated epic status rather than inferring it from the close command's prose.\n\n## 6. Return one consolidated receipt\n\nReturn only after verified epic closure or a concrete blocker. Before returning, ask the native adapter to reapply the retained manager title once so the calling session ends on the epic rather than incidental child work; unsupported or failed rename remains a silent no-op. A completion receipt names the epic, integrated child and epic commits, verification performed, serial-versus-parallel choice and why, interventions or conflicts, and any deliberately deferred follow-up. A blocker receipt names the exact failing child or epic criterion, dependency/decision/error, last accepted manager commit, preserved worker refs or worktrees, current Docket state, checks already attempted, and the single action needed to resume.\n\nDo not create an orchestration database, scheduler, permanent runner, or synthetic epic status. On interruption, restart this workflow from section 1: Docket and Git reveal completed children and the next authoritative ready set; absent native lifecycle state simply selects the serial fallback.",
20
+ "docket-task": "Create a work item conformant with the OKF task profile (bundled at `specs/okf-task-profile.md` when the repo carries it). The request describes the item (\"task: add X to Y, epic phase-1, depends on KEY-8\").\n\n**Prefer the engine**: `docket task create --title \"…\" --epic /work/epics/… --deps KEY-x,KEY-y --priority p1 --description \"…\"` handles ID assignment, file placement, and a conformant template. Then edit the created file to fill in real `# Context` links and `# Acceptance Criteria`, and run `docket index`. The manual steps below are the fallback when the engine is unavailable.\n\n1. **Assign the ID**: work items (tasks AND epics) take the next number in the project sequence under the key from `docket.yaml` — `grep -rh \"^id: KEY-\" <bundle>/`, max + 1. Decisions likewise on their own prefix (default `DEC-`). Verify the result is unused.\n2. **Write the file** at `work/tasks/<ID>-<short-slug>.md` (epics → `work/epics/`, decisions → `decisions/`) with frontmatter: `type`, `title`, `description` (one sentence), `id`, `status: todo`, `epic` (bundle-absolute link — ask or infer; a task without an epic is allowed but noted), `depends_on` (task IDs, omit if none), `priority` (default `p2`), `assignee`, `tags`, `timestamp` (current UTC ISO 8601).\n3. **Body**: `# Context` — link the relevant specs/docs/decisions (bundle-absolute paths); `# Acceptance Criteria` — checkboxes, verifiable, few. Omit `# Log` until there's something to log.\n4. **Regenerate the index** (`docket index`) and add a `log.md` entry when the item is notable.\n5. If work starts now, follow [the pickup workflow](/workflows/docket-pickup.md). It delegates task state and context-packet mechanics to `docket task start <ID> --json`; never set the active task without the status move or vice versa. Pausing later is `docket task stop` (clears the active task, status stays).\n\nNever skip or reuse numbers, never hand-maintain task lists inside epic files, never mark `status` beyond `todo` at creation.",
21
+ "docket-groom": "Run this full backlog-hygiene audit only when the user explicitly asks to groom or audit the backlog, find stale or inconsistent work, or review task hygiene. Ordinary status, orientation, what-is-next, and review requests use `docket overview --json` instead and stop when that structured response is sufficient.\n\nRead every file in `work/` and report, then apply agreed fixes. Start from the engine: `docket ready --json` and `docket task list --json`.\n\nThe engine owns ready/list derivation and task mutation mechanics; the groom workflow owns audit judgment, proposed changes, and the authorization boundary.\n\n1. **Derive ready**: `docket ready` (never compute by hand). Ready is derived, never written: a task is ready only when its stored status is `todo` and every dependency resolves to `done`; an unknown dependency blocks it. The ready queue puts ranked tasks first by ascending `rank`; rank ties and the unranked tail use priority (`p0` through `p3`), then ascending task ID as the stable fallback.\n2. **Flag inconsistencies**:\n - `in-progress` tasks with no commits trailer-matching their ID (`git log --grep \"Task: <ID>\"`) and no Log entry in 7+ days → probably stalled; propose `blocked` or `todo`.\n - `done` tasks with unchecked acceptance criteria or missing `# Outcome`.\n - `closed` tasks without a concrete `# Disposition` and replacement links when applicable.\n - `depends_on` pointing at nonexistent or done-and-superseded IDs; broken bundle links (`docket lint`).\n - Epics without a `spec` link; tasks without an `epic` link.\n - `index.md` out of sync (`docket index` fixes; report if it changes anything).\n3. **Propose, then apply**: present findings compactly; on confirmation (or when running autonomously, for mechanical fixes only) update files via `docket task move`/`docket task log`, regenerate the index, and add a `**YYYY-MM-DD**` line to affected `# Log` sections explaining status changes.\n4. Commit as `chore(docket): groom backlog` (no task trailer — `docket task stop` first).\n\nNever change priorities or close tasks without saying so; grooming narrates every mutation.",
22
+ "docket-close": "Conclude the given task (default: the ID in `.docket/active-task`). A terminal move is the moment the wiki gets paid — don't skip steps.\n\nStored status changes go through the engine's canonical transition table; invalid transitions are rejected, `done` and `closed` are terminal, and moving to `closed` requires a disposition note. Only `done` satisfies dependencies or counts as completion.\n\nThe engine owns the state-machine-checked terminal move and dated Log mutation; the close workflow owns the choice between completion (`done`) and non-completion (`closed`), Outcome or Disposition judgment, documentation review, and derived index/log reconciliation.\n\n1. **Choose the terminal meaning explicitly**. Completion is the backward-compatible default: every acceptance criterion is checked (or explicitly waived in the Outcome with a reason), and the target state is `done`. Use non-completion only when the user explicitly intends to abandon, decline, supersede, or otherwise discontinue the work; leave unmet criteria unchecked, target `closed`, and require a concrete disposition reason. If neither meaning is supported, say so and stop.\n2. **Write the terminal narrative**. For completion, write `# Outcome`: what actually shipped, citing commit hashes found via `git log --grep \"Task: <ID>\" --oneline` plus the task file's history, with anything descoped or discovered. For non-completion, write `# Disposition`: why the work ended, what remains unmet, and any replacement task or decision links; do not claim that work shipped.\n3. **Reconcile the docs** (the LLM-first step): from the task diff and terminal narrative, identify wiki concepts (`specs/`, `reference/`, `decisions/`, plan documents) the conclusion invalidates or extends. Update them now. If a choice foreclosed alternatives, record it as a `type: Decision` concept and link it from the Outcome or Disposition.\n4. **Update state**: for completion, run `docket task close <ID> --note \"…\"`; for non-completion, run `docket task close <ID> --without-completion --note \"<disposition>\"`. Then run `docket index`, add a `log.md` entry that says completed or closed, and check dependency and epic effects. Only `done` unblocks dependents or counts toward epic completion; a terminal epic may be `closed` without all children being done.\n5. **Commit everything together** — task file + reconciled docs + index/log — with the `Task: <ID>` trailer (keep the task active so the hook injects it, or add it manually), then `docket task stop` to clear the active task.\n\nThe commit that concludes a task must contain the doc reconciliation — that's the product's core promise.",
23
+ "docket-standup": "Report project status from files + git. **Mutate nothing.** Pull state from the engine (`docket task list --json`, `docket ready --json`); use git for the activity window.\n\n1. **Window**: since the last standup or the range given (default: 7 days).\n2. **Done**: tasks whose status flipped to `done` in the window — from `git log -p --since=<window> -- <bundle>/work/tasks/` (status line changes) — one line each: ID, title, outcome gist.\n3. **Closed without completion**: tasks whose status flipped to `closed` in the window — one line each: ID, title, and disposition; keep them separate from shipped work.\n4. **In flight**: `in-progress` tasks with their latest Log entry and commit count from `git log --grep \"Task: <ID>\" --since=<window>`. Call out any with zero commits and no Log movement.\n5. **Ready next**: derived ready list (`docket ready`), top 5. Ready is derived, never written: a task is ready only when its stored status is `todo` and every dependency resolves to `done`; an unknown dependency blocks it. The ready queue puts ranked tasks first by ascending `rank`; rank ties and the unranked tail use priority (`p0` through `p3`), then ascending task ID as the stable fallback.\n6. **Blocked**: `blocked` tasks with the blocking reason from their Log.\n7. **Epic pulse**: one line per active epic — fraction of its tasks done, with closed children called out separately (derive by grep, don't trust hand-maintained lists).\n\nOutput: compact markdown suitable for pasting into a chat. Flag (don't fix) any inconsistencies noticed along the way — fixing belongs to [docket-groom](/workflows/docket-groom.md).",
24
+ "docket-state-of-play": "Refresh the optional bundle-root `overview.md` re-entry note. The engine parses, ages, and renders this authored summary but never writes it; live task status, readiness, progress, and activity stay in the derived overview.\n\n1. **Read the evidence**: run `docket overview --json`; read the product spec, current epics and tasks, recent Outcomes, explicit Decision concepts, `log.md`, recent task-linked commits, and the existing `overview.md` when present. Treat the derived overview as execution truth and the product spec/decisions as direction truth.\n2. **Write only the re-entry through-line**: summarize a few recent outcomes rather than commits, then name the one or few current/next epics or frontiers—including work already underway—with enough context to understand the move. Put the canonical resume target first when one exists; multiple real frontiers remain multiple authored links rather than an engine-selected winner. Add Worth knowing only for a decision, constraint, discovery, risk, parked thread, or useful wiki destination that materially helps re-entry. Use concrete nouns and consequences, link claims to bundle evidence, and omit empty material instead of writing filler. The preserved project preamble owns the recognizable full name, concise purpose, and other durable product introduction; do not repeat it here, and do not infer missing identity. Repeat a derived fact only when it explains why something matters, never to copy an inventory.\n3. **Write the linked note**: use the full output of `git rev-parse HEAD` as `as_of` and the current UTC ISO-8601 time as `reviewed_at`. What we've done recently and What's up next are required and non-empty. Worth knowing is optional; omit the heading when it would be empty.\n\n ```markdown\n ---\n format: re-entry/v2\n as_of: <full commit sha>\n reviewed_at: <timestamp>\n ---\n\n # Project re-entry\n\n ## What we've done recently\n\n - <outcome and consequence with a link to evidence>\n\n ## What's up next\n\n - <current or next frontier and why it matters, linked to its epic or task>\n\n ## Worth knowing\n\n - <optional decision, constraint, discovery, risk, or parked thread with a useful link>\n ```\n\n4. **Apply freshness honestly**: five task-linked commits after `as_of` or fourteen days after `reviewed_at` makes the note need review. Renderers keep the visibly dated last-known context readable rather than hiding it or presenting it as fresh. Refresh when the re-entry through-line materially changes, not merely to reset a clock. After a task close that changes the note, stamp the close commit in a separate tracker-only refresh so it starts at zero task-linked commits behind.\n5. **Verify and commit**: run `docket overview` and `docket lint`; confirm the linked sections and freshness are accurate. Commit as `chore(docket): refresh product context` with no Task trailer (`docket task stop` first).\n\nA missing `overview.md` is valid and renders no placeholder. Earlier formats remain readable and unchanged, but renderers label legacy prose and `re-entry/v1` as needing review. Never migrate them automatically; the next meaningful refresh replaces the file with the linked form above.",
25
+ "docket-freshness": "Close-time reconciliation is prospective — it fires only when a task closes, and only for that task's diff. This workflow is the retrospective complement: periodically re-ask \"what does this invalidate?\" across everything that happened since the last sweep.\n\n1. **Find the anchor**: the most recent `**Freshness**` entry in `log.md` holds the watermark sha. If none exists (first run), sweep the full history.\n2. **Collect the range**: `git log <sha>..HEAD --name-only` (keep trailers). Partition the commits:\n - **Trailerless** — the high-risk bucket: nobody ever asked the reconciliation question. Give each the full treatment: from its changed paths, which concepts (`specs/`, `reference/`, `decisions/`, plan documents) does it invalidate or extend?\n - **Trailered** (`Task: KEY-n`) — reconciliation should have happened at close. Spot-check: did closes that plausibly invalidated docs actually touch them?\n3. **Rotate a deep read**: pick the 1–2 concepts in `specs/` and `reference/` with the oldest last-modified commit and verify their content against current reality (code, plan). This catches drift that has no local commit at all — don't skip it just because the commit range is clean.\n4. **Propose, then apply**: present findings compactly (per doc: what's stale, which commit made it so). On confirmation — or autonomously for unambiguous factual fixes only — update the docs.\n5. **Stamp the watermark**: append to today's section of `log.md`:\n\n ```\n - **Freshness** — reviewed through `<short-sha of HEAD>` (<n> commits, <k> trailerless): <one-line findings summary, or \"no drift found\">.\n ```\n\n A \"no drift found\" stamp is a real result — record it; the recorded null finding is what makes the next sweep cheap.\n6. Commit doc fixes and the watermark together as `chore(docket): freshness review` (`docket task stop` first — no task trailer).\n\nNever end a sweep without stamping the watermark, even when nothing changed."
26
+ }
27
+ },
28
+ {
29
+ "version": "0.0.2",
30
+ "bodies": {
31
+ "docket-pickup": "Use this workflow only for authorized tracked Docket work. Pickup authority requires positive evidence: a Docket ID, an unambiguous reference to an existing tracked item, or an explicit request to select the next Docket or backlog item. Generic implementation language does not select pickup. A concrete direct request proceeds in the user's stated scope without creating, starting, or adopting Docket work; do not invoke this workflow for it.\n\n1. **Resolve the target and command**: a Docket ID authorizes `docket task start <ID> --json`. Resolve an unambiguous tracked-item reference to its ID, then use the same named command. Only explicit next-Docket-task or backlog-selection language authorizes bare `docket task start --json`. If an apparent tracked reference remains ambiguous, perform only focused resolution or ask for clarification; never omit the ID, substitute the top ready item, or mutate `.docket/active-task`.\n2. **Start through the engine**: run only the command authorized in step 1. If the command fails, stop; do not rename the session or begin tracked work.\n3. **Use the returned title intent**: read `suggestedSessionTitle` from the successful structured result. Do not rebuild it from prompt text or separately queried task fields.\n4. **Best-effort rename**: ask the current harness's native adapter to name the calling session with that exact value. If the host has no current-session naming capability, the capability is unavailable, or the rename fails, continue silently without retrying or treating pickup as failed.\n5. **Hand off context**: use the returned task, epic, dependency, linked-concept, and commit fields as the context packet, then begin the requested tracked work.\n\nStored status changes go through the engine's canonical transition table; invalid transitions are rejected, `done` and `closed` are terminal, and moving to `closed` requires a disposition note. Only `done` satisfies dependencies or counts as completion.\n\nThe engine owns task selection, the state-machine-checked status transition, active-task state, title derivation, and the context packet; the pickup workflow owns only their sequence, and a native adapter owns only its bounded rename binding.",
32
+ "docket-epic": "Supervise the named epic until its acceptance criteria support explicit closure or one concrete blocker prevents safe progress. Docket files and task-linked Git history are the durable source of truth. Native worker, wait, follow-up, notification, and isolated-checkout capabilities are optional accelerators; they never change readiness or completion semantics.\n\n## 1. Establish the authoritative graph\n\n1. Confirm the user named an epic and authorized running it, not merely reviewing it. Read the epic file, verify that it is an Epic with an ID and title, then run `docket task list --epic <EPIC-ID> --all --json` and `docket ready --json`.\n2. Derive one manager title from those authoritative epic fields, exactly `Epic <ID> — <title>`, and retain it for the entire supervision run. Ask the current harness's native adapter to apply it to the calling manager session. Unsupported, unavailable, or failed rename capability is a silent no-op; it never blocks supervision.\n3. Record the manager baseline: current Git commit and branch, working-tree state, epic status and acceptance criteria, every child status and dependency, already-linked task commits, and the stopping condition. Preserve unrelated user changes; do not hide, overwrite, or move them into a worker checkout.\n4. Use the engine's ready result as authoritative. Ready is derived, never written: a task is ready only when its stored status is `todo` and every dependency resolves to `done`; an unknown dependency blocks it. The ready queue puts ranked tasks first by ascending `rank`; rank ties and the unranked tail use priority (`p0` through `p3`), then ascending task ID as the stable fallback. Filter that result to the named epic; never dispatch from a remembered or hand-derived ready list.\n5. If no child is ready but unfinished children remain, inspect their dependency and blocked-state evidence. Continue only when Docket state identifies a resolvable in-scope next action; otherwise prepare the blocker receipt in section 6.\n\n## 2. Preflight isolation and likely write overlap\n\nBefore creating any worker, inspect each ready child's context, acceptance criteria, linked concepts, and likely implementation/test/generated-document surfaces. Parallel writing is allowed only when every selected child is dependency-independent, likely write sets are materially distinct, each worker has a separate checkout at the exact accepted manager ref, and the manager can integrate and verify results one at a time. Treat shared workflow templates, generated adapters, dependency manifests, schemas, migrations, indexes, and central registries as likely overlap unless evidence shows otherwise.\n\nIf any condition is unknown or false—or if the host lacks a verified worker, wait/follow-up, notification, or isolated-checkout binding—use the mandatory serial fallback: run exactly one child at a time in the calling session or one isolated worker, integrate it fully, refresh Docket state, and only then choose the next child. Never run concurrent writers in one checkout. A shared `.docket/active-task` is single-checkout state, not a coordination mechanism.\n\n## 3. Dispatch one bounded child contract\n\nFor each selected child, provide the exact task ID, accepted baseline commit, isolated checkout or serial location, permitted scope, acceptance criteria, relevant linked concepts, expected verification, and these constraints:\n\n- follow [the pickup workflow](/workflows/docket-pickup.md) before implementation and [the close workflow](/workflows/docket-close.md) only after the task is actually complete;\n- change only the named child and required reconciliation surfaces; do not start siblings, close the epic, or invent orchestration infrastructure;\n- preserve unrelated changes, use task-linked commits, clear the checkout's active-task marker after close, and return commit hashes, verification results, interventions, and exact blockers;\n- do not claim integration or readiness changes from the worker checkout—the manager re-establishes those facts after accepting the result.\n\nWhen no native worker binding is available, execute this same contract serially in the calling session. The contract, not process count, defines supervision.\n\nAn isolated child session follows pickup normally and keeps its own `<ID> — <title>` task name; never apply the manager title to that child. In the serial fallback, child pickup can temporarily rename the shared calling session, so immediately after every successful child pickup reapply the retained `Epic <ID> — <title>` manager title before implementation continues. A failed or unsupported restoration remains a silent no-op and does not change task state or the child contract.\n\n## 4. Inspect and integrate one result at a time\n\n1. Treat a worker report as a lead, not authority. Inspect its checkout or ref, diff, task file, checked or explicitly waived criteria, Outcome, Log, commit trailers, verification output, and clean active-task state.\n2. Reject or return incomplete, out-of-scope, unverified, or ambiguously based work. Keep the branch/worktree/ref recoverable and state the required correction. Never mark the child done merely because the worker said it finished.\n3. Integrate one accepted commit series into the manager checkout. Resolve only understood in-scope conflicts; otherwise stop integration, preserve both refs and the conflict evidence, and produce a blocker receipt. Do not integrate a second result against unresolved or unverified state.\n4. Run the verification proportionate to the accepted diff, regenerate derived state with `docket index`, then rerun `docket task list --epic <EPIC-ID> --all --json` and `docket ready --json`. Re-read the epic and Git history. Select further work only from this refreshed state.\n5. At every accepted boundary, durable task files plus integrated Git commits must be sufficient for a replacement manager to resume. Native task IDs and wait cursors are useful transient handles, never the recovery source of truth.\n\n## 5. Review and close the epic explicitly\n\nAll children being done is necessary evidence, not epic completion. When no unfinished child remains, review every epic acceptance criterion against integrated task Outcomes, diffs, tests, decisions, and reconciled docs. Run final repository verification. If any criterion lacks evidence, create or identify the smallest in-scope follow-on child and continue; do not check or waive a criterion silently.\n\nWhen every criterion is satisfied or explicitly waived with a reason, apply the close workflow to the epic itself: write its Outcome with commit evidence, reconcile affected concepts, close through the engine, regenerate the index, update the log, and commit with the epic's task trailer. Verify the integrated epic status rather than inferring it from the close command's prose.\n\n## 6. Return one consolidated receipt\n\nReturn only after verified epic closure or a concrete blocker. Before returning, ask the native adapter to reapply the retained manager title once so the calling session ends on the epic rather than incidental child work; unsupported or failed rename remains a silent no-op. A completion receipt names the epic, integrated child and epic commits, verification performed, serial-versus-parallel choice and why, interventions or conflicts, and any deliberately deferred follow-up. A blocker receipt names the exact failing child or epic criterion, dependency/decision/error, last accepted manager commit, preserved worker refs or worktrees, current Docket state, checks already attempted, and the single action needed to resume.\n\nDo not create an orchestration database, scheduler, permanent runner, or synthetic epic status. On interruption, restart this workflow from section 1: Docket and Git reveal completed children and the next authoritative ready set; absent native lifecycle state simply selects the serial fallback.",
33
+ "docket-task": "Create a work item conformant with the OKF task profile (bundled at `specs/okf-task-profile.md` when the repo carries it). The request describes the item (\"task: add X to Y, epic phase-1, depends on KEY-8\").\n\n**Prefer the engine**: `docket task create --title \"…\" --epic /work/epics/… --deps KEY-x,KEY-y --priority p1 --description \"…\"` handles ID assignment, file placement, and a conformant template. Then edit the created file to fill in real `# Context` links and `# Acceptance Criteria`, and run `docket index`. The manual steps below are the fallback when the engine is unavailable.\n\n1. **Assign the ID**: work items (tasks AND epics) take the next number in the project sequence under the key from `docket.yaml` — `grep -rh \"^id: KEY-\" <bundle>/`, max + 1. Decisions likewise on their own prefix (default `DEC-`). Verify the result is unused.\n2. **Write the file** at `work/tasks/<ID>-<short-slug>.md` (epics → `work/epics/`, decisions → `decisions/`) with frontmatter: `type`, `title`, `description` (one sentence), `id`, `status: todo`, `epic` (bundle-absolute link — ask or infer; a task without an epic is allowed but noted), `depends_on` (task IDs, omit if none), `priority` (default `p2`), `assignee`, `tags`, `timestamp` (current UTC ISO 8601).\n3. **Body**: `# Context` — link the relevant specs/docs/decisions (bundle-absolute paths); `# Acceptance Criteria` — checkboxes, verifiable, few. Omit `# Log` until there's something to log.\n4. **Regenerate the index** (`docket index`) and add a `log.md` entry when the item is notable.\n5. If work starts now, follow [the pickup workflow](/workflows/docket-pickup.md). It delegates task state and context-packet mechanics to `docket task start <ID> --json`; never set the active task without the status move or vice versa. Pausing later is `docket task stop` (clears the active task, status stays).\n\nNever skip or reuse numbers, never hand-maintain task lists inside epic files, never mark `status` beyond `todo` at creation.",
34
+ "docket-groom": "Run this full backlog-hygiene audit only when the user explicitly asks to groom or audit the backlog, find stale or inconsistent work, or review task hygiene. Ordinary status, orientation, what-is-next, and review requests use `docket overview --json` instead and stop when that structured response is sufficient.\n\nRead every file in `work/` and report, then apply agreed fixes. Start from the engine: `docket ready --json` and `docket task list --json`.\n\nThe engine owns ready/list derivation and task mutation mechanics; the groom workflow owns audit judgment, proposed changes, and the authorization boundary.\n\n1. **Derive ready**: `docket ready` (never compute by hand). Ready is derived, never written: a task is ready only when its stored status is `todo` and every dependency resolves to `done`; an unknown dependency blocks it. The ready queue puts ranked tasks first by ascending `rank`; rank ties and the unranked tail use priority (`p0` through `p3`), then ascending task ID as the stable fallback.\n2. **Flag inconsistencies**:\n - `in-progress` tasks with no commits trailer-matching their ID (`git log --grep \"Task: <ID>\"`) and no Log entry in 7+ days → probably stalled; propose `blocked` or `todo`.\n - `done` tasks with unchecked acceptance criteria or missing `# Outcome`.\n - `closed` tasks without a concrete `# Disposition` and replacement links when applicable.\n - `depends_on` pointing at nonexistent or done-and-superseded IDs; broken bundle links (`docket lint`).\n - Epics without a `spec` link; tasks without an `epic` link.\n - `index.md` out of sync (`docket index` fixes; report if it changes anything).\n3. **Propose, then apply**: present findings compactly; on confirmation (or when running autonomously, for mechanical fixes only) update files via `docket task move`/`docket task log`, regenerate the index, and add a `**YYYY-MM-DD**` line to affected `# Log` sections explaining status changes.\n4. Commit as `chore(docket): groom backlog` (no task trailer — `docket task stop` first).\n\nNever change priorities or close tasks without saying so; grooming narrates every mutation.",
35
+ "docket-close": "Conclude the given task (default: the ID in `.docket/active-task`). A terminal move is the moment the wiki gets paid — don't skip steps.\n\nStored status changes go through the engine's canonical transition table; invalid transitions are rejected, `done` and `closed` are terminal, and moving to `closed` requires a disposition note. Only `done` satisfies dependencies or counts as completion.\n\nThe engine owns the state-machine-checked terminal move and dated Log mutation; the close workflow owns the choice between completion (`done`) and non-completion (`closed`), Outcome or Disposition judgment, documentation review, and derived index/log reconciliation.\n\n1. **Choose the terminal meaning explicitly**. Completion is the backward-compatible default: every acceptance criterion is checked (or explicitly waived in the Outcome with a reason), and the target state is `done`. Use non-completion only when the user explicitly intends to abandon, decline, supersede, or otherwise discontinue the work; leave unmet criteria unchecked, target `closed`, and require a concrete disposition reason. If neither meaning is supported, say so and stop.\n2. **Write the terminal narrative**. For completion, write `# Outcome`: what actually shipped, citing commit hashes found via `git log --grep \"Task: <ID>\" --oneline` plus the task file's history, with anything descoped or discovered. For non-completion, write `# Disposition`: why the work ended, what remains unmet, and any replacement task or decision links; do not claim that work shipped.\n3. **Reconcile the docs** (the LLM-first step): from the task diff and terminal narrative, identify wiki concepts (`specs/`, `reference/`, `decisions/`, plan documents) the conclusion invalidates or extends. Update them now. If a choice foreclosed alternatives, record it as a `type: Decision` concept and link it from the Outcome or Disposition.\n4. **Update state**: for completion, run `docket task close <ID> --note \"…\"`; for non-completion, run `docket task close <ID> --without-completion --note \"<disposition>\"`. Then run `docket index`, add a `log.md` entry that says completed or closed, and check dependency and epic effects. Only `done` unblocks dependents or counts toward epic completion; a terminal epic may be `closed` without all children being done.\n5. **Commit everything together** — task file + reconciled docs + index/log — with the `Task: <ID>` trailer (keep the task active so the hook injects it, or add it manually), then `docket task stop` to clear the active task.\n\nThe commit that concludes a task must contain the doc reconciliation — that's the product's core promise.",
36
+ "docket-standup": "Report project status from files + git. **Mutate nothing.** Pull state from the engine (`docket task list --json`, `docket ready --json`); use git for the activity window.\n\n1. **Window**: since the last standup or the range given (default: 7 days).\n2. **Done**: tasks whose status flipped to `done` in the window — from `git log -p --since=<window> -- <bundle>/work/tasks/` (status line changes) — one line each: ID, title, outcome gist.\n3. **Closed without completion**: tasks whose status flipped to `closed` in the window — one line each: ID, title, and disposition; keep them separate from shipped work.\n4. **In flight**: `in-progress` tasks with their latest Log entry and commit count from `git log --grep \"Task: <ID>\" --since=<window>`. Call out any with zero commits and no Log movement.\n5. **Ready next**: derived ready list (`docket ready`), top 5. Ready is derived, never written: a task is ready only when its stored status is `todo` and every dependency resolves to `done`; an unknown dependency blocks it. The ready queue puts ranked tasks first by ascending `rank`; rank ties and the unranked tail use priority (`p0` through `p3`), then ascending task ID as the stable fallback.\n6. **Blocked**: `blocked` tasks with the blocking reason from their Log.\n7. **Epic pulse**: one line per active epic — fraction of its tasks done, with closed children called out separately (derive by grep, don't trust hand-maintained lists).\n\nOutput: compact markdown suitable for pasting into a chat. Flag (don't fix) any inconsistencies noticed along the way — fixing belongs to [docket-groom](/workflows/docket-groom.md).",
37
+ "docket-state-of-play": "Refresh the optional bundle-root `overview.md` re-entry note. The engine parses, ages, and renders this authored summary but never writes it; live task status, readiness, progress, and activity stay in the derived overview.\n\n1. **Read the evidence**: run `docket overview --json`; read the product spec, current epics and tasks, recent Outcomes, explicit Decision concepts, `log.md`, recent task-linked commits, and the existing `overview.md` when present. Treat the derived overview as execution truth and the product spec/decisions as direction truth.\n2. **Write only the re-entry through-line**: summarize a few recent outcomes rather than commits, then name the one or few current/next epics or frontiers—including work already underway—with enough context to understand the move. Put the canonical resume target first when one exists; multiple real frontiers remain multiple authored links rather than an engine-selected winner. Add Worth knowing only for a decision, constraint, discovery, risk, parked thread, or useful wiki destination that materially helps re-entry. Use concrete nouns and consequences, link claims to bundle evidence, and omit empty material instead of writing filler. The preserved project preamble owns the recognizable full name, concise purpose, and other durable product introduction; do not repeat it here, and do not infer missing identity. Repeat a derived fact only when it explains why something matters, never to copy an inventory.\n3. **Write the linked note**: use the full output of `git rev-parse HEAD` as `as_of` and the current UTC ISO-8601 time as `reviewed_at`. What we've done recently and What's up next are required and non-empty. Worth knowing is optional; omit the heading when it would be empty.\n\n ```markdown\n ---\n format: re-entry/v2\n as_of: <full commit sha>\n reviewed_at: <timestamp>\n ---\n\n # Project re-entry\n\n ## What we've done recently\n\n - <outcome and consequence with a link to evidence>\n\n ## What's up next\n\n - <current or next frontier and why it matters, linked to its epic or task>\n\n ## Worth knowing\n\n - <optional decision, constraint, discovery, risk, or parked thread with a useful link>\n ```\n\n4. **Apply freshness honestly**: five task-linked commits after `as_of` or fourteen days after `reviewed_at` makes the note need review. Renderers keep the visibly dated last-known context readable rather than hiding it or presenting it as fresh. Refresh when the re-entry through-line materially changes, not merely to reset a clock. After a task close that changes the note, stamp the close commit in a separate tracker-only refresh so it starts at zero task-linked commits behind.\n5. **Verify and commit**: run `docket overview` and `docket lint`; confirm the linked sections and freshness are accurate. Commit as `chore(docket): refresh product context` with no Task trailer (`docket task stop` first).\n\nA missing `overview.md` is valid and renders no placeholder. Earlier formats remain readable and unchanged, but renderers label legacy prose and `re-entry/v1` as needing review. Never migrate them automatically; the next meaningful refresh replaces the file with the linked form above.",
38
+ "docket-freshness": "Close-time reconciliation is prospective — it fires only when a task closes, and only for that task's diff. This workflow is the retrospective complement: periodically re-ask \"what does this invalidate?\" across everything that happened since the last sweep.\n\n1. **Find the anchor**: the most recent `**Freshness**` entry in `log.md` holds the watermark sha. If none exists (first run), sweep the full history.\n2. **Collect the range**: `git log <sha>..HEAD --name-only` (keep trailers). Partition the commits:\n - **Trailerless** — the high-risk bucket: nobody ever asked the reconciliation question. Give each the full treatment: from its changed paths, which concepts (`specs/`, `reference/`, `decisions/`, plan documents) does it invalidate or extend?\n - **Trailered** (`Task: KEY-n`) — reconciliation should have happened at close. Spot-check: did closes that plausibly invalidated docs actually touch them?\n3. **Rotate a deep read**: pick the 1–2 concepts in `specs/` and `reference/` with the oldest last-modified commit and verify their content against current reality (code, plan). This catches drift that has no local commit at all — don't skip it just because the commit range is clean.\n4. **Propose, then apply**: present findings compactly (per doc: what's stale, which commit made it so). On confirmation — or autonomously for unambiguous factual fixes only — update the docs.\n5. **Stamp the watermark**: append to today's section of `log.md`:\n\n ```\n - **Freshness** — reviewed through `<short-sha of HEAD>` (<n> commits, <k> trailerless): <one-line findings summary, or \"no drift found\">.\n ```\n\n A \"no drift found\" stamp is a real result — record it; the recorded null finding is what makes the next sweep cheap.\n6. Commit doc fixes and the watermark together as `chore(docket): freshness review` (`docket task stop` first — no task trailer).\n\nNever end a sweep without stamping the watermark, even when nothing changed."
39
+ }
40
+ },
41
+ {
42
+ "version": "0.0.1",
43
+ "bodies": {
44
+ "docket-pickup": "Pick up the requested task (or the top ready task when none is named) and establish its context before doing implementation work.\n\n1. **Start through the engine**: run `docket task start [ID] --json`. If the command fails, stop; do not rename the session or begin work.\n2. **Use the returned title intent**: read `suggestedSessionTitle` from the successful structured result. Do not rebuild it from prompt text or separately queried task fields.\n3. **Best-effort rename**: ask the current harness's native adapter to name the calling session with that exact value. If the host has no current-session naming capability, the capability is unavailable, or the rename fails, continue silently without retrying or treating pickup as failed.\n4. **Hand off context**: use the returned task, epic, dependency, linked-concept, and commit fields as the context packet, then begin the requested work.\n\nTask selection, status transition, active-task state, title derivation, and packet contents belong to the engine. The workflow owns only this sequence; a native adapter owns only its bounded rename binding.",
45
+ "docket-task": "Create a work item conformant with the OKF task profile (bundled at `specs/okf-task-profile.md` when the repo carries it). The request describes the item (\"task: add X to Y, epic phase-1, depends on KEY-8\").\n\n**Prefer the engine**: `docket task create --title \"…\" --epic /work/epics/… --deps KEY-x,KEY-y --priority p1 --description \"…\"` handles ID assignment, file placement, and a conformant template. Then edit the created file to fill in real `# Context` links and `# Acceptance Criteria`, and run `docket index`. The manual steps below are the fallback when the engine is unavailable.\n\n1. **Assign the ID**: work items (tasks AND epics) take the next number in the project sequence under the key from `docket.yaml` — `grep -rh \"^id: KEY-\" <bundle>/`, max + 1. Decisions likewise on their own prefix (default `DEC-`). Verify the result is unused.\n2. **Write the file** at `work/tasks/<ID>-<short-slug>.md` (epics → `work/epics/`, decisions → `decisions/`) with frontmatter: `type`, `title`, `description` (one sentence), `id`, `status: todo`, `epic` (bundle-absolute link — ask or infer; a task without an epic is allowed but noted), `depends_on` (task IDs, omit if none), `priority` (default `p2`), `assignee`, `tags`, `timestamp` (current UTC ISO 8601).\n3. **Body**: `# Context` — link the relevant specs/docs/decisions (bundle-absolute paths); `# Acceptance Criteria` — checkboxes, verifiable, few. Omit `# Log` until there's something to log.\n4. **Regenerate the index** (`docket index`) and add a `log.md` entry when the item is notable.\n5. If work starts now, follow [the pickup workflow](/workflows/docket-pickup.md). It delegates task state and context-packet mechanics to `docket task start <ID> --json`; never set the active task without the status move or vice versa. Pausing later is `docket task stop` (clears the active task, status stays).\n\nNever skip or reuse numbers, never hand-maintain task lists inside epic files, never mark `status` beyond `todo` at creation.",
46
+ "docket-groom": "Run this full backlog-hygiene audit only when the user explicitly asks to groom or audit the backlog, find stale or inconsistent work, or review task hygiene. Ordinary status, orientation, what-is-next, and review requests use `docket overview --json` instead and stop when that structured response is sufficient.\n\nRead every file in `work/` and report, then apply agreed fixes. Start from the engine: `docket ready --json` and `docket task list --json`.\n\n1. **Derive ready**: `docket ready` (never compute by hand). Present as the ordered next-up list (priority, then dependency depth).\n2. **Flag inconsistencies**:\n - `in-progress` tasks with no commits trailer-matching their ID (`git log --grep \"Task: <ID>\"`) and no Log entry in 7+ days → probably stalled; propose `blocked` or `todo`.\n - `done` tasks with unchecked acceptance criteria or missing `# Outcome`.\n - `depends_on` pointing at nonexistent or done-and-superseded IDs; broken bundle links (`docket lint`).\n - Epics without a `spec` link; tasks without an `epic` link.\n - `index.md` out of sync (`docket index` fixes; report if it changes anything).\n3. **Propose, then apply**: present findings compactly; on confirmation (or when running autonomously, for mechanical fixes only) update files via `docket task move`/`docket task log`, regenerate the index, and add a `**YYYY-MM-DD**` line to affected `# Log` sections explaining status changes.\n4. Commit as `chore(docket): groom backlog` (no task trailer — `docket task stop` first).\n\nNever change priorities or close tasks without saying so; grooming narrates every mutation.",
47
+ "docket-close": "Close the given task (default: the ID in `.docket/active-task`). Closing is the moment the wiki gets paid — don't skip steps.\n\n**Prefer the engine for the mechanical part**: `docket task close <ID> --note \"…\"` (state-machine-checked status flip + dated Log entry). Everything below that is judgment and stays manual: the Outcome, the doc reconciliation, index/log updates.\n\n1. **Verify done-ness**: every acceptance criterion checked (or explicitly waived in the Outcome with a reason). If not done, say so and stop.\n2. **Write `# Outcome`**: what actually shipped, citing commit hashes — find them via `git log --grep \"Task: <ID>\" --oneline` plus the task file's own history. Note anything descoped or discovered.\n3. **Reconcile the docs** (the LLM-first step): from the diff of those commits, identify wiki concepts (`specs/`, `reference/`, `decisions/`, plan documents) the change invalidates or extends. Update them now. If a choice foreclosed alternatives during the work, record it as a `type: Decision` concept in `decisions/` and link it from the Outcome.\n4. **Update state**: final `# Log` entry; `docket index`; add a `log.md` entry; check whether this unblocks tasks (their `depends_on` now all done) and whether the epic itself is complete — if so, note it in the epic's Log and propose closing it.\n5. **Commit everything together** — task file + reconciled docs + index/log — with the `Task: <ID>` trailer (keep the task active so the hook injects it, or add it manually), then `docket task stop` to clear the active task.\n\nThe commit that closes a task must contain the doc reconciliation — that's the product's core promise.",
48
+ "docket-standup": "Report project status from files + git. **Mutate nothing.** Pull state from the engine (`docket task list --json`, `docket ready --json`); use git for the activity window.\n\n1. **Window**: since the last standup or the range given (default: 7 days).\n2. **Done**: tasks whose status flipped to `done` in the window — from `git log -p --since=<window> -- <bundle>/work/tasks/` (status line changes) — one line each: ID, title, outcome gist.\n3. **In flight**: `in-progress` tasks with their latest Log entry and commit count from `git log --grep \"Task: <ID>\" --since=<window>`. Call out any with zero commits and no Log movement.\n4. **Ready next**: derived ready list (`docket ready`), priority-ordered, top 5.\n5. **Blocked**: `blocked` tasks with the blocking reason from their Log.\n6. **Epic pulse**: one line per active epic — fraction of its tasks done (derive by grep, don't trust hand-maintained lists).\n\nOutput: compact markdown suitable for pasting into a chat. Flag (don't fix) any inconsistencies noticed along the way — fixing belongs to [docket-groom](/workflows/docket-groom.md).",
49
+ "docket-state-of-play": "Refresh the optional bundle-root `overview.md` product checkpoint. The engine parses, ages, and renders this authored judgment but never writes it; live execution stays in the derived overview.\n\n1. **Read the evidence**: run `docket overview --json`; read the product spec, current epics and tasks, recent Outcomes, explicit Decision concepts, `log.md`, recent task-linked commits, and the existing `overview.md` when present. Treat the derived overview as execution truth and the product spec/decisions as direction truth.\n2. **Separate durable orientation from current assessment**: product orientation says what this specific product is and the durable outcome it serves. The current assessment states the outcome now being pursued, the bet or hypothesis, evidence or learning, principal risk or open question, next decision or validation event, and links to material decisions. Use concrete product nouns and consequences; reject prose that could describe an unrelated project. Repeat a derived fact only when it is necessary to explain why something matters, never to copy the task inventory.\n3. **Write the structured checkpoint**: use the full output of `git rev-parse HEAD` as `as_of` and the current UTC ISO-8601 time as `reviewed_at`. Keep every heading and give every section concrete content; `Material decisions` may explicitly say none are in force.\n\n ```markdown\n ---\n format: re-entry/v1\n as_of: <full commit sha>\n reviewed_at: <timestamp>\n ---\n\n # Product context\n\n ## Product orientation\n\n <durable product identity and purpose>\n\n ## Current outcome\n\n <outcome now being pursued>\n\n ## Current bet\n\n <hypothesis or chosen approach>\n\n ## Evidence and learning\n\n <what has been learned and what supports or weakens the bet>\n\n ## Principal risk\n\n <most important uncertainty or open question>\n\n ## Next decision\n\n <decision or validation event that changes the plan>\n\n ## Material decisions\n\n - [<decision and consequence>](/decisions/<file>.md)\n ```\n\n4. **Apply freshness honestly**: stable orientation does not expire with the assessment. Five task-linked commits after `as_of` or fourteen days after `reviewed_at` makes the assessment need review; renderers retain it for inspection but do not present it as current. Refresh at standup, grooming, a material decision, or a notable close when judgment changed. After a close, stamp the close commit in a separate tracker-only refresh so the checkpoint starts at zero task-linked commits behind.\n5. **Verify and commit**: run `docket overview` and `docket lint`; confirm the structured fields appear above the derived execution and freshness is accurate. Commit as `chore(docket): refresh product context` with no Task trailer (`docket task stop` first).\n\nA missing `overview.md` is valid and renders no placeholder. The earlier prose-only format remains readable and unchanged, but renderers label it as needing review; the next meaningful refresh converts it by replacing the file with the structured form above.",
50
+ "docket-freshness": "Close-time reconciliation is prospective — it fires only when a task closes, and only for that task's diff. This workflow is the retrospective complement: periodically re-ask \"what does this invalidate?\" across everything that happened since the last sweep.\n\n1. **Find the anchor**: the most recent `**Freshness**` entry in `log.md` holds the watermark sha. If none exists (first run), sweep the full history.\n2. **Collect the range**: `git log <sha>..HEAD --name-only` (keep trailers). Partition the commits:\n - **Trailerless** — the high-risk bucket: nobody ever asked the reconciliation question. Give each the full treatment: from its changed paths, which concepts (`specs/`, `reference/`, `decisions/`, plan documents) does it invalidate or extend?\n - **Trailered** (`Task: KEY-n`) — reconciliation should have happened at close. Spot-check: did closes that plausibly invalidated docs actually touch them?\n3. **Rotate a deep read**: pick the 1–2 concepts in `specs/` and `reference/` with the oldest last-modified commit and verify their content against current reality (code, plan). This catches drift that has no local commit at all — don't skip it just because the commit range is clean.\n4. **Propose, then apply**: present findings compactly (per doc: what's stale, which commit made it so). On confirmation — or autonomously for unambiguous factual fixes only — update the docs.\n5. **Stamp the watermark**: append to today's section of `log.md`:\n\n ```\n - **Freshness** — reviewed through `<short-sha of HEAD>` (<n> commits, <k> trailerless): <one-line findings summary, or \"no drift found\">.\n ```\n\n A \"no drift found\" stamp is a real result — record it; the recorded null finding is what makes the next sweep cheap.\n6. Commit doc fixes and the watermark together as `chore(docket): freshness review` (`docket task stop` first — no task trailer).\n\nNever end a sweep without stamping the watermark, even when nothing changed."
51
+ }
52
+ }
53
+ ]
package/src/shipped.ts ADDED
@@ -0,0 +1,96 @@
1
+ // Shipped-text history for vendored workflows. Scaffolded workflows
2
+ // are repo-owned and may diverge deliberately, so upgrading them is a
3
+ // 3-way merge — which needs the base text: what docket shipped at the version
4
+ // the repo scaffolded from. That base lives in shipped-history.json, keyed by
5
+ // version, kept forever (a few KB of markdown per release).
6
+ //
7
+ // The current version's texts derive live from DOCKET_WORKFLOWS; the ledger's
8
+ // head entry must mirror them at all times (guard-tested — editing a template
9
+ // without running `bun run sync-shipped` fails the suite, because it would
10
+ // silently rewrite the merge base under every copy vendored at this version,
11
+ // Bumping DOCKET_VERSION needs no freeze step: the old head is
12
+ // already the frozen record of the outgoing release. See
13
+ // docket/reference/releasing.md.
14
+
15
+ import LEDGER from "./shipped-history.json";
16
+ import { DOCKET_VERSION } from "./version";
17
+ import { DOCKET_WORKFLOWS } from "./workflows";
18
+
19
+ /** Provenance of a vendored workflow: which shipped text it descends from. */
20
+ export interface Origin {
21
+ /** Workflow slug at ship time, e.g. `docket-close`. */
22
+ slug: string;
23
+ /** Engine version whose shipped text is the merge base, e.g. `0.0.1`. */
24
+ version: string;
25
+ }
26
+
27
+ /** Render an origin as the frontmatter value: `<slug>@<version>`. */
28
+ export const formatOrigin = (o: Origin): string => `${o.slug}@${o.version}`;
29
+
30
+ /** Parse an `origin:` frontmatter value; undefined when malformed. */
31
+ export function parseOrigin(value: string): Origin | undefined {
32
+ const match = value.trim().match(/^([a-z0-9-]+)@(\d+\.\d+\.\d+)$/);
33
+ return match?.[1] && match[2]
34
+ ? { slug: match[1], version: match[2] }
35
+ : undefined;
36
+ }
37
+
38
+ /** Shipped workflow bodies per version, newest first. */
39
+ export type ShippedHistory = readonly {
40
+ version: string;
41
+ bodies: Record<string, string>;
42
+ }[];
43
+
44
+ /** Frozen bodies of past versions, newest first — the ledger minus the live head. */
45
+ const FROZEN: ShippedHistory = LEDGER.filter(
46
+ (h) => h.version !== DOCKET_VERSION,
47
+ ).map((entry) => ({
48
+ version: entry.version,
49
+ // Different releases legitimately carry different workflow slugs. JSON
50
+ // inference models an absent historical slug as `undefined`; the shipped
51
+ // contract models only the string entries that actually existed.
52
+ bodies: Object.fromEntries(
53
+ Object.entries(entry.bodies).filter(
54
+ (pair): pair is [string, string] => typeof pair[1] === "string",
55
+ ),
56
+ ),
57
+ }));
58
+
59
+ /** All shipped versions, newest first — current release derives live. */
60
+ export function shippedHistory(): ShippedHistory {
61
+ const current = {
62
+ version: DOCKET_VERSION,
63
+ bodies: Object.fromEntries(DOCKET_WORKFLOWS.map((w) => [w.slug, w.body])),
64
+ };
65
+ return [current, ...FROZEN];
66
+ }
67
+
68
+ /** The workflow body docket shipped at `version`; undefined if never shipped. */
69
+ export function shippedWorkflow(
70
+ slug: string,
71
+ version: string,
72
+ history: ShippedHistory = shippedHistory(),
73
+ ): string | undefined {
74
+ return history.find((h) => h.version === version)?.bodies[slug];
75
+ }
76
+
77
+ /**
78
+ * Recover provenance for an un-stamped copy by matching its text against
79
+ * every shipped body. Takes the full file source (frontmatter is stripped —
80
+ * timestamps vary per repo, the body is what's vendored). Only exact
81
+ * (whitespace-trimmed) matches recover; a modified copy returns undefined —
82
+ * its true origin is unknowable after the fact, which is why new scaffolds
83
+ * are stamped at birth. Newest matching version wins.
84
+ */
85
+ export function recoverOrigin(
86
+ source: string,
87
+ history: ShippedHistory = shippedHistory(),
88
+ ): Origin | undefined {
89
+ const body = source.replace(/^---\n[\s\S]*?\n---\n/, "").trim();
90
+ for (const { version, bodies } of history) {
91
+ for (const [slug, shipped] of Object.entries(bodies)) {
92
+ if (shipped.trim() === body) return { slug, version };
93
+ }
94
+ }
95
+ return undefined;
96
+ }