@unbrained/pm-cli 2026.8.31 → 2026.9.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.
Files changed (103) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/AGENTS.md +15 -13
  3. package/CHANGELOG.md +23 -6
  4. package/README.md +3 -3
  5. package/dist/cli/commands/merge.d.ts +1 -1
  6. package/dist/cli/commands/merge.js +3 -3
  7. package/dist/cli/register-mutation.js +8 -5
  8. package/dist/cli-bundle/bundle-manifest.json +125 -125
  9. package/dist/cli-bundle/chunks/{chunk-SXMHPK4V.js → chunk-2MC2LCXZ.js} +4 -4
  10. package/dist/cli-bundle/chunks/{chunk-52JXYSDW.js → chunk-3WICXLVT.js} +2 -2
  11. package/dist/cli-bundle/chunks/{chunk-OUGDA7NR.js → chunk-6XIIGE7U.js} +2 -2
  12. package/dist/cli-bundle/chunks/chunk-ALKKAUFN.js +35 -0
  13. package/dist/cli-bundle/chunks/{chunk-JIFYOJF7.js → chunk-DTGWEL23.js} +2 -2
  14. package/dist/cli-bundle/chunks/chunk-GOACULJW.js +13 -0
  15. package/dist/cli-bundle/chunks/{chunk-NW6LV3TU.js → chunk-NOPL37X4.js} +64 -64
  16. package/dist/cli-bundle/chunks/{chunk-IEFJ3ZEB.js → chunk-TAG4VC22.js} +4 -4
  17. package/dist/cli-bundle/chunks/{chunk-MWIN6OH2.js → chunk-U45JXJCC.js} +2 -2
  18. package/dist/cli-bundle/chunks/{chunk-5DH5XCJL.js → chunk-X4RCGLTX.js} +2 -2
  19. package/dist/cli-bundle/chunks/{chunk-QZCIBZR3.js → chunk-Y7VOMSZT.js} +2 -2
  20. package/dist/cli-bundle/chunks/{register-list-query-7C7DKZ2U.js → register-list-query-XYNX4ASQ.js} +2 -2
  21. package/dist/cli-bundle/chunks/register-mutation-2MVLRS3J.js +20 -0
  22. package/dist/cli-bundle/chunks/{register-operations-IIMA5OKO.js → register-operations-WCIDXBU3.js} +2 -2
  23. package/dist/cli-bundle/chunks/{register-setup-52H36QCB.js → register-setup-KVNC5KPL.js} +2 -2
  24. package/dist/cli-bundle/focused-chunks/chunk-7VATS5QY.js +16 -0
  25. package/dist/cli-bundle/focused-chunks/{chunk-VJ7EFMFL.js → chunk-FWPLDEXF.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/{chunk-FOMZ6NG5.js → chunk-MKHG3T76.js} +2 -2
  27. package/dist/cli-bundle/focused-chunks/{chunk-UN34D63M.js → chunk-PF3E6Q36.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/{chunk-APN4QN6R.js → chunk-RMRCQPWU.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/{chunk-BPWJ4QM5.js → chunk-S4U5CN5Q.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/chunk-T2LXXSOD.js +158 -0
  31. package/dist/cli-bundle/focused-chunks/{chunk-JCOOAUHK.js → chunk-Y6JJIX6I.js} +30 -30
  32. package/dist/cli-bundle/main.js +2 -2
  33. package/dist/cli-bundle/sdk-contracts.js +1 -1
  34. package/dist/cli-bundle/sdk-core.js +25 -25
  35. package/dist/cli-bundle/sdk-governance.js +1 -1
  36. package/dist/cli-bundle/sdk-graph.js +1 -1
  37. package/dist/cli-bundle/sdk-merge.js +31 -31
  38. package/dist/cli-bundle/sdk-query.js +1 -1
  39. package/dist/cli-bundle/sdk-runtime.js +1 -1
  40. package/dist/cli-bundle/sdk-testing.js +1 -1
  41. package/dist/cli-bundle/sdk.js +10 -10
  42. package/dist/core/history/drift-scan.js +6 -6
  43. package/dist/core/history/history.d.ts +6 -0
  44. package/dist/core/history/history.js +47 -5
  45. package/dist/core/history/replay.d.ts +12 -1
  46. package/dist/core/history/replay.js +60 -39
  47. package/dist/mcp/server.d.ts +2 -2
  48. package/dist/mcp/server.js +78 -15
  49. package/dist/mcp/tool-definitions.js +56 -2
  50. package/dist/sdk/agent-capability-contracts.js +4 -2
  51. package/dist/sdk/history-read.js +14 -6
  52. package/dist/sdk/history-repair.js +17 -10
  53. package/dist/sdk/index.d.ts +1 -0
  54. package/dist/sdk/index.js +3 -2
  55. package/dist/sdk/init-agent-guidance.js +4 -4
  56. package/dist/sdk/lifecycle/update.js +23 -10
  57. package/dist/sdk/mcp/discovery.d.ts +152 -0
  58. package/dist/sdk/mcp/discovery.js +495 -0
  59. package/dist/sdk/merge/index.d.ts +1 -1
  60. package/dist/sdk/merge/index.js +3 -3
  61. package/dist/sdk/merge/receipts.d.ts +27 -1
  62. package/dist/sdk/merge/receipts.js +27 -8
  63. package/dist/sdk/merge/three-way.d.ts +3 -1
  64. package/dist/sdk/merge/three-way.js +9 -7
  65. package/dist/sdk/runtime-extended-actions.js +4 -4
  66. package/docs/AGENT_GUIDE.md +11 -3
  67. package/docs/ARCHITECTURE.md +5 -0
  68. package/docs/CLAUDE_CODE_PLUGIN.md +2 -2
  69. package/docs/CONTEXT_RECOVERY_PRIMITIVES.md +14 -5
  70. package/docs/MCP_2026_07_28.md +9 -5
  71. package/docs/MCP_CAPABILITY_SURFACES.md +7 -1
  72. package/docs/MCP_REMOTE_TRANSPORT_SECURITY.md +11 -1
  73. package/docs/MERGE_SAFETY.md +30 -2
  74. package/docs/OUTPUT_TOKEN_ACCOUNTING.md +5 -3
  75. package/docs/PROGRESSIVE_TOOL_DISCOVERY.md +42 -0
  76. package/docs/README.md +1 -0
  77. package/docs/agent-task-token-baseline.json +157 -3
  78. package/docs/agent-task-transcripts.json +143 -0
  79. package/docs/performance/cli-transport-overhead.md +8 -6
  80. package/marketplace.json +2 -2
  81. package/package.json +2 -2
  82. package/packages/pm-beads/package.json +1 -1
  83. package/packages/pm-calendar/package.json +1 -1
  84. package/packages/pm-command-kit/package.json +1 -1
  85. package/packages/pm-digital-twin/package.json +1 -1
  86. package/packages/pm-governance-audit/package.json +1 -1
  87. package/packages/pm-guide-shell/package.json +1 -1
  88. package/packages/pm-kanban/package.json +1 -1
  89. package/packages/pm-lifecycle-hooks/package.json +1 -1
  90. package/packages/pm-linked-test-adapters/package.json +1 -1
  91. package/packages/pm-search-advanced/package.json +1 -1
  92. package/packages/pm-templates/package.json +1 -1
  93. package/packages/pm-todos/package.json +1 -1
  94. package/packages/pm-vcs/package.json +1 -1
  95. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  96. package/plugins/pm-claude/README.md +4 -3
  97. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  98. package/sdk/public-surface.json +145 -2
  99. package/dist/cli-bundle/chunks/chunk-2XVV45NJ.js +0 -35
  100. package/dist/cli-bundle/chunks/chunk-3FCSFWZT.js +0 -13
  101. package/dist/cli-bundle/chunks/register-mutation-OI4BJLUX.js +0 -20
  102. package/dist/cli-bundle/focused-chunks/chunk-BMD42PUG.js +0 -158
  103. package/dist/cli-bundle/focused-chunks/chunk-EUTABA4M.js +0 -16
@@ -9,7 +9,7 @@
9
9
  * definition for multi-branch agent workflows.
10
10
  */
11
11
 
12
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="393f6460-bf8b-5479-8e3e-cac6f2680823")}catch(e){}}();
12
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7e8fcddf-09fe-5979-b23a-78915bf7f896")}catch(e){}}();
13
13
  import { historyEntriesToRaw, reanchorHistoryEntries, } from "../../core/history/replay.js";
14
14
  import { canonicalDocument, parseItemDocument, serializeItemDocument, } from "../../core/item/item-format.js";
15
15
  import { EXIT_CODE } from "../../core/shared/constants.js";
@@ -69,7 +69,9 @@ function commonHistoryPrefixLength(ours, theirs) {
69
69
  * hash re-anchored so the resulting chain verifies again. No side's events are
70
70
  * ever discarded: content divergence is preserved for the post-merge
71
71
  * reconciliation pass (`pm validate` + `pm history-repair`) instead of being
72
- * silently resolved by last-writer-wins.
72
+ * silently resolved by last-writer-wins. If either suffix cannot apply after
73
+ * deterministic ordering, the merge fails closed instead of publishing a
74
+ * hash-valid stream that omitted a branch effect.
73
75
  */
74
76
  export function mergeHistoryStreams(baseRaw, oursRaw, theirsRaw) {
75
77
  parseHistoryJsonl(baseRaw, "base");
@@ -126,10 +128,10 @@ export function mergeHistoryStreams(baseRaw, oursRaw, theirsRaw) {
126
128
  }
127
129
  return historyEntryIdentity(left).localeCompare(historyEntryIdentity(right));
128
130
  });
129
- const reanchored = reanchorHistoryEntries([
130
- ...ours.slice(0, shared),
131
- ...mergedSuffix,
132
- ]);
131
+ const reanchored = reanchorHistoryEntries([...ours.slice(0, shared), ...mergedSuffix], undefined, { continuousHashSurface: true });
132
+ if (reanchored.skippedOps > 0) {
133
+ throw new PmCliError(`History union cannot preserve ${reanchored.skippedOps} patch operation(s) after deterministic suffix ordering; leave the file unresolved for manual recovery.`, EXIT_CODE.GENERIC_FAILURE);
134
+ }
133
135
  return {
134
136
  merged: historyEntriesToRaw(reanchored.entries),
135
137
  strategy: "union_reanchor",
@@ -704,4 +706,4 @@ export function mergeJsonDocuments(baseRaw, oursRaw, theirsRaw, options = {}) {
704
706
  };
705
707
  }
706
708
  //# sourceMappingURL=three-way.js.map
707
- //# debugId=393f6460-bf8b-5479-8e3e-cac6f2680823
709
+ //# debugId=7e8fcddf-09fe-5979-b23a-78915bf7f896
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="95e9def9-3f48-5108-8a17-0747fcf64f2d")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="41227e35-6354-5208-997b-e8a1cf696bf9")}catch(e){}}();
3
3
  import { EXIT_CODE } from "../core/shared/constants.js";
4
4
  import { PmCliError } from "../core/shared/errors.js";
5
5
  import { transferMutationStdinTokenPolicy } from "./runtime-primitives.js";
@@ -9,7 +9,7 @@ import { runEval } from "./eval.js";
9
9
  import { runMergeDriver } from "./merge/driver.js";
10
10
  import { runMergeInstall } from "./merge/install.js";
11
11
  import { runMergeReconcile } from "./merge/reconcile.js";
12
- import { runMergeReceiptReport } from "./merge/receipts.js";
12
+ import { runMergeReceiptEvidenceReport } from "./merge/receipts.js";
13
13
  import { listMutationEvents } from "./mutation-events.js";
14
14
  import { parseRuntimeInteger, readRuntimeString, readRuntimeStringArray, } from "./runtime-input.js";
15
15
  import { runEvent, runMeet, runRemind, } from "./scheduling-shortcuts.js";
@@ -87,7 +87,7 @@ export function runRuntimeMergeAction(context) {
87
87
  }, context.global);
88
88
  }
89
89
  if (subcommand === "report") {
90
- return runMergeReceiptReport({
90
+ return runMergeReceiptEvidenceReport({
91
91
  includeReconciled: input.includeReconciled === true,
92
92
  cwd: readRuntimeString(input, "cwd"),
93
93
  });
@@ -175,4 +175,4 @@ export function runRuntimeSchedulingAction(context) {
175
175
  return runRemind(title, input, context.global);
176
176
  }
177
177
  //# sourceMappingURL=runtime-extended-actions.js.map
178
- //# debugId=95e9def9-3f48-5108-8a17-0747fcf64f2d
178
+ //# debugId=41227e35-6354-5208-997b-e8a1cf696bf9
@@ -4,18 +4,25 @@ This guide is optimized for coding agents that need to understand and mutate rep
4
4
 
5
5
  ## Agent Quick Context
6
6
 
7
- Run this before heavy work:
7
+ Run this before heavy work. The first call is the canonical cold start. Then run the request-specific duplicate and ownership checks before item mutation:
8
8
 
9
9
  ```bash
10
- pm context --limit 10
10
+ pm context --limit 10 --for orient
11
11
  pm search "<request keywords>" --limit 10
12
12
  pm list --status open --limit 20
13
13
  pm list --status in_progress --limit 20
14
+ ```
15
+
16
+ Run the optional guidance setup commands only when needed:
17
+
18
+ ```bash
14
19
  pm init --agent-guidance status
15
20
  pm package install guide-shell --project
16
21
  pm guide workflows
17
22
  ```
18
23
 
24
+ The completed-task replay gate selects that one-call protocol from equivalent fixed-corpus candidates: 1,055 estimated tokens versus 2,293 for the historical four-read sequence and 4,798 for `contracts --summary` plus `next`. The reviewed numbers and per-step receipts live in [the published token baseline](agent-task-token-baseline.json).
25
+
19
26
  If a relevant item exists, reuse it. If not, create a parent lineage, then create and claim the child implementation item.
20
27
  When AGENTS/CLAUDE guidance is missing, use `pm init --agent-guidance add` to inject compact workflow guardrails, or `pm init --agent-guidance skip` to persist an explicit decline.
21
28
 
@@ -26,7 +33,7 @@ Tracked documentation work: [pm-u9d0](../.agents/pm/epics/pm-u9d0.toon).
26
33
  1. **Orient**
27
34
 
28
35
  ```bash
29
- pm context --limit 10
36
+ pm context --limit 10 --for orient
30
37
  pm search "<keywords>" --limit 10
31
38
  pm list --status open --limit 20
32
39
  pm list --status in_progress --limit 20
@@ -108,6 +115,7 @@ pm release <item-id>
108
115
  | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
109
116
  | The single next action + why | `pm next` (concrete ready leaves first; completed-container closeout rows surface only when no leaf work is ready; `--ready-only` for the tightest output) |
110
117
  | Next ready work in one epic | `pm next --parent <id>` |
118
+ | Canonical cold-start orientation | `pm context --limit 10 --for orient` (1,055 estimated tokens on the fixed 101-item replay corpus) |
111
119
  | Next work and agenda | `pm context --limit 10` |
112
120
  | Comprehensive whole-tracker snapshot | `pm context --depth full` (every section, no per-section row cap) |
113
121
  | Status of one epic/subtree | `pm context --parent <id> --depth deep` |
@@ -56,9 +56,12 @@ src/
56
56
  shared/
57
57
  mcp/
58
58
  server.ts
59
+ tool-definitions.ts
59
60
  sdk/
60
61
  cli-contracts.ts
61
62
  index.ts
63
+ mcp/
64
+ discovery.ts
62
65
  types/
63
66
  tests/
64
67
  unit/
@@ -287,6 +290,8 @@ allowance file:
287
290
  Do not add new CLI/MCP behavior by deep-importing `src/core`. Add or extend an
288
291
  SDK primitive first, then consume that SDK surface from the presentation layer.
289
292
 
293
+ Progressive tool discovery follows this boundary directly: `src/sdk/mcp/discovery.ts` owns ranking, authorization filtering, budgets, cursors, omissions, cache identity, and the canonical result contract. `src/mcp/server.ts` only negotiates the extension, selects the entry catalog, and adapts compatibility text. See [Progressive Tool Discovery](PROGRESSIVE_TOOL_DISCOVERY.md).
294
+
290
295
  ## Telemetry Schema Negotiation
291
296
 
292
297
  Telemetry preserves wire compatibility through an explicit client/server negotiation split:
@@ -55,7 +55,7 @@ This clones the repo, reads `.claude-plugin/marketplace.json` at the root, insta
55
55
  claude mcp add --transport stdio pm-mcp -- npx -y --package=@unbrained/pm-cli@latest pm-mcp
56
56
  ```
57
57
 
58
- Gives you the 31 MCP tools without skills or slash commands.
58
+ Gives you the 32 MCP tools without skills or slash commands.
59
59
 
60
60
  ### 3. Direct project `.mcp.json` (project-scoped MCP only)
61
61
 
@@ -117,7 +117,7 @@ pnpm smoke:claude-plugin
117
117
  ```
118
118
 
119
119
  Verifies: plugin file structure, manifest name consistency, stateless MCP
120
- discovery for `2026-07-28`, 31 tools present, full workflow (init → create →
120
+ discovery for `2026-07-28`, 32 tools present, full workflow (init → create →
121
121
  claim → update → link files/docs/tests → get → context → search → validate →
122
122
  health), and session-start hook.
123
123
 
@@ -46,11 +46,20 @@ When multiple entries exist, the SDK selects the requested package by key or `na
46
46
 
47
47
  ## History and merge recovery
48
48
 
49
- History hash capability 3 distinguishes the current canonical writer surface from older epoch-2 writers while retaining readers for epochs 1 and 2. An unsupported epoch is version skew, not permission to reinterpret or silently normalize history.
50
-
51
- Health keeps the metadata-only cache path for clean history streams. A cached hash mismatch, chain mismatch, or writer-version skew is only a candidate: health rereads canonical item sources and verifies stream content hashes before reporting corruption. The `history_drift` details expose `cache_confirmation` candidate, confirmed, and resolved-false-positive item sets, while `cache_hit_verification: metadata_then_content_hash` identifies the authoritative fallback. Drift-cache envelopes also carry the current item-hash capability, so a runtime with incompatible hash semantics rebuilds the cache instead of trusting it.
52
-
53
- Merge reconciliation may consume a durable hash-only receipt without `--force` only when its canonical item path, complete declared-field set, and every merged-value hash exactly match the current item snapshot. Raw discarded values remain clone-local. Any incomplete or mismatched proof fails closed.
49
+ History hash capability 3 distinguishes the current canonical writer surface
50
+ from older writers while retaining frozen readers for epochs 1 and 2. Two
51
+ writer surfaces were historically emitted under epoch 2: its earlier form
52
+ excluded linked-test workspace/provenance fields and test-run execution
53
+ receipts while normalizing dependency ids; its later form included those
54
+ fields. Verification recognizes both immutable forms and requires each entry's
55
+ before/after hashes to use one consistent form. Current-document verification
56
+ selects the candidate that matches the verified chain head. An unsupported
57
+ epoch is version skew, not permission to reinterpret or silently normalize
58
+ history.
59
+
60
+ Health keeps the metadata-only cache path for clean history streams. A cached hash mismatch, chain mismatch, or writer-version skew is only a candidate: health rereads canonical item sources and verifies stream content hashes before reporting corruption. The `history_drift` details expose `cache_confirmation` candidate, confirmed, and resolved-false-positive item sets, while `cache_hit_verification: metadata_then_content_hash` identifies the authoritative fallback. Drift-cache envelopes carry both their schema version and the current item-hash capability, so changing legacy canonicalization invalidates prior verdicts and a runtime with incompatible hash semantics rebuilds the cache instead of trusting it.
61
+
62
+ Merge reconciliation may consume a durable hash-only receipt without `--force` only when its canonical item path, complete declared-field set, and every merged-value hash exactly match the current item snapshot. Raw discarded values remain clone-local. Any incomplete or mismatched proof fails closed. SDK gates use `inspectMergeReceiptEvidence` or `runMergeReceiptEvidenceReport` to retain the distinction between no evidence, rejected evidence, and clone-local evidence whose Git directory could not be resolved; `clone_local_evidence_resolved=false` makes the loss-aware report incomplete. The list-only and legacy report compatibility projections intentionally return valid receipts only. Diverged history unions also fail closed when deterministic suffix ordering would make any patch operation inapplicable, instead of publishing a rehashed stream with a skipped branch effect.
54
63
 
55
64
  ## Strict-create recovery
56
65
 
@@ -12,7 +12,9 @@ by [pm-v7e337](../.agents/pm/features/pm-v7e337.toon),
12
12
  [pm-3zh9s4](../.agents/pm/features/pm-3zh9s4.toon), and
13
13
  [pm-vzcisw](../.agents/pm/chores/pm-vzcisw.toon). Skills and Apps are tracked
14
14
  by [pm-8nzivt](../.agents/pm/features/pm-8nzivt.toon) and
15
- [pm-pznhee](../.agents/pm/features/pm-pznhee.toon).
15
+ [pm-pznhee](../.agents/pm/features/pm-pznhee.toon). Progressive tool discovery
16
+ and canonical model-facing results are tracked by
17
+ [pm-3g3f8z](../.agents/pm/features/pm-3g3f8z.toon).
16
18
 
17
19
  Status: accepted. MCP `2026-07-28` is pm's canonical protocol revision.
18
20
 
@@ -39,16 +41,18 @@ capabilities, public cache policy, server identity, and bounded instructions.
39
41
  No modern request reads identity, capabilities, or version from a previous
40
42
  request.
41
43
 
44
+ The optional `dev.unbrained.pm/progressive-tool-discovery` extension is also request-local. Negotiated clients receive a stable five-tool entry catalog and expand it with `pm_discover`; unnegotiated clients retain the complete profile-selected list. Negotiated tool results designate `structuredContent.result` as canonical and use text content only as a pointer, while the isolated compatibility adapter retains duplicated JSON text for existing clients. The public SDK owns ranking, cursor binding, token and omission receipts, and cache invalidation. See [Progressive Tool Discovery](PROGRESSIVE_TOOL_DISCOVERY.md).
45
+
42
46
  ## Legacy boundary
43
47
 
44
48
  Legacy means every revision that establishes a session with an `initialize`
45
49
  handshake, which the canonical revision defines as `2025-11-25` and earlier.
46
50
  The stdio adapter therefore accepts the full initialize-era set, newest first:
47
51
 
48
- | Revision | Accepted at `initialize` | Notes |
49
- |----------|--------------------------|-------|
50
- | `2025-11-25` | yes | Final legacy revision; offered when a client omits `protocolVersion` |
51
- | `2025-06-18` | yes | Earlier legacy revision retained for existing consumers |
52
+ | Revision | Accepted at `initialize` | Notes |
53
+ | ------------ | ------------------------ | -------------------------------------------------------------------- |
54
+ | `2025-11-25` | yes | Final legacy revision; offered when a client omits `protocolVersion` |
55
+ | `2025-06-18` | yes | Earlier legacy revision retained for existing consumers |
52
56
 
53
57
  `initialize` answers with the revision the client requested, because a legacy
54
58
  client has no fall-forward mechanism and treats the answered version as the
@@ -1,6 +1,6 @@
1
1
  # Runtime capability surfaces
2
2
 
3
- Tracker: [pm-xwah](../.agents/pm/chores/pm-xwah.toon), [pm-kxci8x](../.agents/pm/tasks/pm-kxci8x.toon), [pm-mu8m](../.agents/pm/tasks/pm-mu8m.toon), [pm-9k90](../.agents/pm/features/pm-9k90.toon), [pm-m4ikkz](../.agents/pm/features/pm-m4ikkz.toon), [pm-yf07b7](../.agents/pm/features/pm-yf07b7.toon)
3
+ Tracker: [pm-xwah](../.agents/pm/chores/pm-xwah.toon), [pm-kxci8x](../.agents/pm/tasks/pm-kxci8x.toon), [pm-mu8m](../.agents/pm/tasks/pm-mu8m.toon), [pm-9k90](../.agents/pm/features/pm-9k90.toon), [pm-m4ikkz](../.agents/pm/features/pm-m4ikkz.toon), [pm-yf07b7](../.agents/pm/features/pm-yf07b7.toon), and [pm-3g3f8z](../.agents/pm/features/pm-3g3f8z.toon).
4
4
 
5
5
  The SDK exports one agent capability contract for command visibility, MCP tool profiles, resources, and workflow prompts. CLI-facing generators and MCP hosts should project from these contracts instead of maintaining independent allowlists.
6
6
 
@@ -15,6 +15,12 @@ Set `PM_MCP_PROFILE` for the `pm-mcp` process:
15
15
 
16
16
  An activated extension command makes `pm_run` discoverable outside a custom profile. Its action is added to the live `pm_run` action enumeration. Custom profiles remain exact allowlists.
17
17
 
18
+ ## Progressive discovery
19
+
20
+ Modern clients may negotiate `dev.unbrained.pm/progressive-tool-discovery`. Their initial `tools/list` response is the stable five-tool entry catalog, and `pm_discover` expands authorized tools by intent, family, and tier under explicit row and token ceilings. Unnegotiated and initialize-era clients retain the full selected profile.
21
+
22
+ The SDK ranks with visible lexical, semantic, graph, permission, freshness, and usage signals, binds cursors to all ranking inputs, and emits omission recovery plus cache invalidation metadata. Negotiated tool calls make `structuredContent.result` canonical and replace duplicated JSON text with a pointer; compatibility clients retain the old representation. See [Progressive Tool Discovery](PROGRESSIVE_TOOL_DISCOVERY.md).
23
+
18
24
  Package authors can declare `tier: "core" | "standard" | "full" | "internal"` on `registerCommand()` definitions. The default is `standard`; `internal` commands remain callable by native dispatch but are not advertised by normal MCP profiles.
19
25
 
20
26
  ## Workspace schema projection
@@ -1,6 +1,7 @@
1
1
  # MCP Remote Transport, Authorization, and Migration
2
2
 
3
- Tracker references: [pm-v7e337](../.agents/pm/features/pm-v7e337.toon),
3
+ Tracker references: [pm-3g3f8z](../.agents/pm/features/pm-3g3f8z.toon),
4
+ [pm-v7e337](../.agents/pm/features/pm-v7e337.toon),
4
5
  [pm-3zh9s4](../.agents/pm/features/pm-3zh9s4.toon), and
5
6
  [pm-vzcisw](../.agents/pm/chores/pm-vzcisw.toon).
6
7
 
@@ -36,6 +37,7 @@ Configuration is explicit and environment-only:
36
37
  | `PM_MCP_HTTP_AUTH_ISSUER` | Exact HTTPS authorization-server issuer | none |
37
38
  | `PM_MCP_HTTP_RESOURCE` | Canonical MCP resource/audience URI | none |
38
39
  | `PM_MCP_HTTP_SCOPES` | Space-separated consent scopes | `pm:read pm:write` |
40
+ | `PM_MCP_DISCOVERY_CURSOR_KEY` | Shared 32-byte-or-longer secret for discovery pages | process-local key |
39
41
 
40
42
  A non-loopback bind fails closed unless token, issuer, and resource are all
41
43
  present. Production deployments should normally call
@@ -50,9 +52,17 @@ placing a real credential in documentation:
50
52
  PM_MCP_HTTP_BEARER_TOKEN='<deployment-secret>' \
51
53
  PM_MCP_HTTP_AUTH_ISSUER='https://auth.example.test' \
52
54
  PM_MCP_HTTP_RESOURCE='http://127.0.0.1:3000/mcp' \
55
+ PM_MCP_DISCOVERY_CURSOR_KEY='<32-byte-or-longer-random-secret>' \
53
56
  pm-mcp-http
54
57
  ```
55
58
 
59
+ Single-process deployments may omit the discovery cursor key. That selects a
60
+ random process-local HMAC key and deliberately makes pagination process-affine:
61
+ a restart or another worker rejects the cursor as stale. Multi-worker or
62
+ restart-continuous deployments must provide the same high-entropy value to
63
+ every worker. Rotating it invalidates outstanding discovery cursors without
64
+ exposing the key in MCP input, output, errors, traces, or cache identities.
65
+
56
66
  The adapter serves RFC 9728 protected-resource metadata at both
57
67
  `/.well-known/oauth-protected-resource` and the path-qualified
58
68
  `/.well-known/oauth-protected-resource/mcp` location.
@@ -97,7 +97,20 @@ pm merge report
97
97
  pm merge report --include-reconciled
98
98
  ```
99
99
 
100
- The underlying public SDK exports are `mergeItemDocuments`, `mergeHistoryStreams`, `mergeRelationshipEventStreams`, `mergeJsonDocuments`, `runMergeDriver`, `runMergeInstall`, `installMergeFence`, `findGitWorkspaceRoot`, `runMergeReconcile`, `runMergeReceiptReport`, `listMergeReceipts`, `auditMergeDriverConfiguration`, `refreshMergeAttributeFenceIfInstalled`, `buildMergeAttributePatterns`, and `auditMergeAttributeFence` from `@unbrained/pm-cli/sdk`. `installMergeFence` accepts explicit tracker and workspace roots, so custom init hosts do not depend on process cwd or CLI globals.
100
+ The underlying public SDK exports are `mergeItemDocuments`, `mergeHistoryStreams`, `mergeRelationshipEventStreams`, `mergeJsonDocuments`, `runMergeDriver`, `runMergeInstall`, `installMergeFence`, `findGitWorkspaceRoot`, `runMergeReconcile`, `runMergeReceiptReport`, `runMergeReceiptEvidenceReport`, `inspectMergeReceiptEvidence`, `listMergeReceipts`, `auditMergeDriverConfiguration`, `refreshMergeAttributeFenceIfInstalled`, `buildMergeAttributePatterns`, and `auditMergeAttributeFence` from `@unbrained/pm-cli/sdk`. `installMergeFence` accepts explicit tracker and workspace roots, so custom init hosts do not depend on process cwd or CLI globals.
101
+
102
+ `listMergeReceipts` is the compatibility projection for callers that only need
103
+ validated receipts. It cannot distinguish an empty evidence store from a store
104
+ whose candidates were all rejected. Gates and diagnostic integrations should
105
+ use `inspectMergeReceiptEvidence`, whose `invalid_evidence_count` preserves
106
+ that distinction without returning malformed contents.
107
+ `runMergeReceiptEvidenceReport` and `pm merge report --json` expose the same
108
+ loss-aware contract through `complete`, `invalid_evidence_count`, and
109
+ `clone_local_evidence_resolved`; the CLI
110
+ exits nonzero when evidence is incomplete, even when the valid-receipt count is
111
+ zero. Current SDK implementations always emit the new field, while its optional
112
+ type preserves structural compatibility for existing typed adapters and test
113
+ fixtures. `runMergeReceiptReport` remains the compatible valid-only report.
101
114
 
102
115
  ## Cross-branch id collision safety
103
116
 
@@ -189,7 +202,22 @@ pm merge reconcile --dry-run --json
189
202
 
190
203
  `history-repair` records the reconciliation patch and classifies its changed fields against the final item. Append-only collection unions and deterministic reordering are reported as preserved context without a data-loss warning. Fields whose replayed values are actually removed or replaced remain loud with discarded event authors/operations and recovery guidance. Re-apply any intended losing mutation as a normal `pm update` so it remains explicit and auditable.
191
204
 
192
- History events now declare an item-hash epoch. The current epoch preserves linked-test insertion order; unversioned streams are verified against both the legacy sorted-test canonicalization and the order-preserving canonicalization. An unknown explicit epoch is reported as `unsupported_item_hash_version` and repair refuses to guess. This keeps version incompatibility distinct from item corruption and is tracked by [pm-2htk4p](../.agents/pm/issues/pm-2htk4p.toon).
205
+ History events now declare an item-hash epoch. Epochs are immutable writer
206
+ contracts, not aliases for the current item serializer: epoch 1 sorts linked
207
+ tests; epoch 2 preserves their insertion order and has both an earlier
208
+ field-frozen form and a later expanded form because the writer surface grew
209
+ before the marker advanced; epoch 3 is the current form. Verification accepts
210
+ both recorded epoch-2 forms without allowing one entry to mix them, and repair
211
+ preserves the form evidenced by each event. Unversioned streams are verified
212
+ against the supported legacy canonicalizations. Current-document comparison
213
+ selects the resolved epoch candidate that matches the chain head. A union merge
214
+ may consume suffixes written by both epoch-2 forms, but its synthesized output
215
+ uses one form for the complete re-anchored stream so every stored `after_hash`
216
+ is exactly the next stored `before_hash`. An unknown explicit epoch is
217
+ reported as `unsupported_item_hash_version` and repair refuses to guess. This
218
+ keeps version incompatibility distinct from item corruption and is tracked by
219
+ [pm-2htk4p](../.agents/pm/issues/pm-2htk4p.toon) and
220
+ [pm-2qahia](../.agents/pm/issues/pm-2qahia.toon).
193
221
 
194
222
  ## Delete versus modify policy
195
223
 
@@ -1,6 +1,6 @@
1
1
  # Output Token Accounting
2
2
 
3
- Tracker references: [pm-t5dt4z](../.agents/pm/tasks/pm-t5dt4z.toon), [pm-g3n00m](../.agents/pm/stories/pm-g3n00m.toon), [pm-8pnj](../.agents/pm/features/pm-8pnj.toon), [pm-f05lsg](../.agents/pm/features/pm-f05lsg.toon), and [pm-srns](../.agents/pm/issues/pm-srns.toon).
3
+ Tracker references: [pm-t5dt4z](../.agents/pm/tasks/pm-t5dt4z.toon), [pm-g3n00m](../.agents/pm/stories/pm-g3n00m.toon), [pm-8pnj](../.agents/pm/features/pm-8pnj.toon), [pm-f05lsg](../.agents/pm/features/pm-f05lsg.toon), [pm-srns](../.agents/pm/issues/pm-srns.toon), and [pm-jjqyzd](../.agents/pm/issues/pm-jjqyzd.toon).
4
4
 
5
5
  ## Agent Quick Context
6
6
 
@@ -39,7 +39,9 @@ The command still exits with its normal non-zero status; the receipt is additive
39
39
 
40
40
  ## Release-Level Task Entitlement
41
41
 
42
- [`agent-task-transcripts.json`](agent-task-transcripts.json) is the SDK-validated, versioned golden corpus. [`agent-task-token-baseline.json`](agent-task-token-baseline.json) is its externally shipped release ratchet. The gate executes the built CLI against independent, identically seeded accounting-on and accounting-off workspaces. Its five complete workflows cover:
42
+ [`agent-task-transcripts.json`](agent-task-transcripts.json) is the SDK-validated, versioned golden corpus. [`agent-task-token-baseline.json`](agent-task-token-baseline.json) is its externally shipped release ratchet. The gate executes the built CLI against independent, identically seeded accounting-on and accounting-off workspaces. Its eight complete workflows cover:
43
+
44
+ - three equivalent cold-start protocols and a fail-closed rule that the documented canonical protocol must remain the measured winner: `context --for orient` at 1,055 estimated tokens, the historical four-read sequence at 2,293, and `contracts --summary` plus `next` at 4,798 on the fixed 101-item corpus;
43
45
 
44
46
  - bounded triage, scaled-workspace orientation, and returning-agent inspection;
45
47
  - a closed-domain refusal followed by the exact advertised shell-free retry;
@@ -49,7 +51,7 @@ The command still exits with its normal non-zero status; the receipt is additive
49
51
 
50
52
  Every step verifies its public SDK output family, canonical successful or refusal exit status, required own-property paths, declared `expected_field_values`, and refusal identity where applicable. Recovery steps must declare a successful output family instead of chaining one refusal to another, every refusal in a completed task must have a later successful `recovery_for` step, and every completed task must terminate with successful output. Successful steps cannot carry refusal-only metadata. Dot-separated `required_fields` and `expected_field_values` paths are traversed structurally from the output root, so incidental prose or nested key names cannot satisfy completeness or terminal-state assertions. The report publishes bytes and estimated tokens for each step and completed task, retry counts, corpus digest, and composite cost. Accounting-on application payloads must be byte-equivalent to their independently captured accounting-off payloads after removing only the receipt. Receipt byte and token fields are independently measured rather than trusted. Runtime refusals verify that their self-reported `total_bytes` matches the independent transport and that `total_estimated_tokens` equals `ceil(total_bytes / 4)`; Commander usage refusals that happen before accounting attachment are measured directly from the captured transport and labeled `independent_transport`.
51
53
 
52
- The baseline fails closed on corpus digest, task identity, step identity, missing or non-finite per-step and per-task ceilings, and missing or non-finite composite cost ceilings. A seeded million-token completed-task regression proves the ratchet fails. Run it with:
54
+ The baseline fails closed on corpus digest, task identity, step identity, the canonical orientation winner and its ceiling, missing or non-finite per-step and per-task ceilings, and missing or non-finite composite cost ceilings. A seeded million-token completed-task regression proves the ratchet fails. Run it with:
53
55
 
54
56
  ```bash
55
57
  pnpm quality:agent-task-token
@@ -0,0 +1,42 @@
1
+ # Progressive Tool Discovery
2
+
3
+ Tracker: [pm-3g3f8z](../.agents/pm/features/pm-3g3f8z.toon).
4
+
5
+ `pm` exposes a public SDK discovery engine and an opt-in MCP extension for large tool catalogs. The design keeps the full legacy-compatible catalog available while allowing capable clients to attach only a small entry surface and expand it by intent.
6
+
7
+ ## Negotiation and entry catalog
8
+
9
+ `server/discover` advertises the namespaced `dev.unbrained.pm/progressive-tool-discovery` extension. A client opts in by returning that extension in the request-local MCP capabilities on every modern request. Negotiated `tools/list` responses contain the stable entry catalog:
10
+
11
+ - `pm_discover` for bounded capability expansion;
12
+ - `pm_next` and `pm_context` for action and workspace orientation;
13
+ - `pm_search` and `pm_get` for targeted retrieval.
14
+
15
+ Clients that do not negotiate the extension receive the complete profile-selected tool list. Legacy initialize-era clients are unchanged.
16
+
17
+ ## Public SDK contract
18
+
19
+ `discoverPmTools()` accepts an authorization-filtered candidate catalog plus query, family, tier, limit, cursor, schema projection, profile, output-budget, and optional host-owned cursor-integrity-key options. It returns:
20
+
21
+ - deterministic score-then-name ordering;
22
+ - lexical, semantic, graph, permission, freshness, and usage scores with public weights and source provenance;
23
+ - a tamper-evident cursor bound to query, filters, schemas, authorization-filtered catalog, ranking inputs, and either the private discovery process or an explicit shared host key;
24
+ - exact estimated token cost and a fail-closed `within_budget` verdict;
25
+ - explicit schema, row-limit, and token-budget omission receipts with recovery;
26
+ - a private cache key, 30-second TTL, and named invalidation events.
27
+
28
+ Hosts may supply normalized semantic, graph, freshness, and usage signals. Missing host values use documented deterministic fallbacks, and the result identifies every signal source; the formula never changes implicitly.
29
+
30
+ SDK hosts that route pages across processes or restarts must pass the same random, secret `cursorIntegrityKey` of at least 32 bytes on every call. Omitting it deliberately selects a random process-local key: that securely enforces process affinity, and a cursor reaching another process fails closed as stale. The bundled MCP adapter reads the shared key from `PM_MCP_DISCOVERY_CURSOR_KEY`; local stdio needs no override, while multi-worker or restart-continuous HTTP deployments must configure one shared high-entropy value.
31
+
32
+ ## Canonical tool results
33
+
34
+ Negotiated clients treat `structuredContent.result` as the single canonical model-facing application result. The text content becomes a stable pointer instead of a second JSON serialization. Errors use the same rule through `structuredContent`.
35
+
36
+ Unnegotiated and legacy clients retain the prior duplicated JSON text plus structured result. That compatibility behavior is isolated at the MCP adapter boundary; domain operations and SDK results do not branch on transport generation.
37
+
38
+ ## Scale and change safety
39
+
40
+ The discovery quality gate exercises selection, deterministic pagination, stale-cursor refusal, permission filtering, schema recovery, and token ceilings at 100, 1,000, and 10,000 candidate tools. Contract snapshots cover the tool schema, and modern MCP integration tests prove both negotiated and compatibility modes.
41
+
42
+ Any tool-definition, workspace-extension, profile, authorization, ranking-signal, or cursor-key change invalidates prior cursors and cache entries. Process-local cursors also expire when that process exits. Clients restart discovery without a cursor after an explicit stale-cursor refusal.
package/docs/README.md CHANGED
@@ -45,6 +45,7 @@ pm guide release --json
45
45
  - [Package SDK Contract Conformance](PACKAGE_SDK_CONTRACT_CONFORMANCE.md) - authoritative public types, `typeof` module derivation, and the first-party parity gate.
46
46
  - [SDK Action and Boundary Conformance](SDK_ACTION_CONFORMANCE.md) - derived CLI/SDK/MCP action vocabulary, public-import ratchets, intent budget diagnostics, and package-runner proof.
47
47
  - [MCP 2026-07-28 Protocol Decision](MCP_2026_07_28.md) - stateless request metadata, discovery, result envelopes, explicit legacy boundary, and migration policy.
48
+ - [Progressive Tool Discovery](PROGRESSIVE_TOOL_DISCOVERY.md) - opt-in bounded MCP catalogs, public SDK ranking and pagination, canonical results, and compatibility isolation.
48
49
  - [MCP 2026-07-28 Conformance Matrix](MCP_2026_07_28_CONFORMANCE.md) - official revision changes mapped to canonical owners and executable evidence.
49
50
  - [MCP Interaction and Task SDK](SDK_MCP_INTERACTIONS.md) - public MRTR continuation, cache/schema validation, and durable task-store contracts.
50
51
  - [MCP Skills and Apps](MCP_SKILLS_AND_APPS.md) - negotiated draft workflow discovery, stable interactive views, digests, provenance, accessibility, and trust boundaries.
@@ -1,11 +1,165 @@
1
1
  {
2
- "version": 3,
2
+ "version": 4,
3
3
  "transcript_version": 2,
4
- "transcript_digest": "sha256:2bf91406426d2a9e5de1da10ba67f7b8e9d4a4baa09eb488fe89ae3925c78f3f",
4
+ "transcript_digest": "sha256:4f9e96bb6ce12bbbeb2514e4bd5958d201c4e62e95083bcbebd749d1e48cc82d",
5
5
  "estimator": "ceil(utf8_bytes / 4)",
6
6
  "measurement_scope": "output_before_token_accounting",
7
7
  "published_with_release": true,
8
+ "orientation": {
9
+ "canonical_task_id": "orientation-context-intent",
10
+ "required_capabilities": [
11
+ "actionable_work",
12
+ "active_ownership",
13
+ "workspace_state"
14
+ ],
15
+ "measured_winner_tokens": 1055,
16
+ "protocols": [
17
+ {
18
+ "task_id": "orientation-context-intent",
19
+ "command_count": 1,
20
+ "estimated_tokens": 1055,
21
+ "capabilities": [
22
+ "actionable_work",
23
+ "active_ownership",
24
+ "workspace_state"
25
+ ],
26
+ "capability_evidence": {
27
+ "actionable_work": [
28
+ {
29
+ "step_id": "context",
30
+ "field_path": "hierarchy"
31
+ }
32
+ ],
33
+ "active_ownership": [
34
+ {
35
+ "step_id": "context",
36
+ "field_path": "activity"
37
+ }
38
+ ],
39
+ "workspace_state": [
40
+ {
41
+ "step_id": "context",
42
+ "field_path": "summary.active_items"
43
+ }
44
+ ]
45
+ }
46
+ },
47
+ {
48
+ "task_id": "orientation-operating-rules",
49
+ "command_count": 4,
50
+ "estimated_tokens": 2293,
51
+ "capabilities": [
52
+ "actionable_work",
53
+ "active_ownership",
54
+ "workspace_state"
55
+ ],
56
+ "capability_evidence": {
57
+ "actionable_work": [
58
+ {
59
+ "step_id": "open",
60
+ "field_path": "items"
61
+ }
62
+ ],
63
+ "active_ownership": [
64
+ {
65
+ "step_id": "in-progress",
66
+ "field_path": "items"
67
+ }
68
+ ],
69
+ "workspace_state": [
70
+ {
71
+ "step_id": "context",
72
+ "field_path": "summary.active_items"
73
+ }
74
+ ]
75
+ }
76
+ },
77
+ {
78
+ "task_id": "orientation-contracts-next",
79
+ "command_count": 2,
80
+ "estimated_tokens": 4798,
81
+ "capabilities": [
82
+ "actionable_work",
83
+ "active_ownership",
84
+ "workspace_state"
85
+ ],
86
+ "capability_evidence": {
87
+ "actionable_work": [
88
+ {
89
+ "step_id": "next",
90
+ "field_path": "recommended.id"
91
+ }
92
+ ],
93
+ "active_ownership": [
94
+ {
95
+ "step_id": "next",
96
+ "field_path": "held_by_others"
97
+ }
98
+ ],
99
+ "workspace_state": [
100
+ {
101
+ "step_id": "next",
102
+ "field_path": "summary.in_progress"
103
+ }
104
+ ]
105
+ }
106
+ }
107
+ ]
108
+ },
8
109
  "tasks": [
110
+ {
111
+ "id": "orientation-context-intent",
112
+ "max_estimated_tokens": 1055,
113
+ "steps": [
114
+ {
115
+ "id": "context",
116
+ "max_estimated_tokens": 1055,
117
+ "accounting_mode": "self_reported"
118
+ }
119
+ ]
120
+ },
121
+ {
122
+ "id": "orientation-contracts-next",
123
+ "max_estimated_tokens": 4798,
124
+ "steps": [
125
+ {
126
+ "id": "contracts",
127
+ "max_estimated_tokens": 4111,
128
+ "accounting_mode": "self_reported"
129
+ },
130
+ {
131
+ "id": "next",
132
+ "max_estimated_tokens": 687,
133
+ "accounting_mode": "self_reported"
134
+ }
135
+ ]
136
+ },
137
+ {
138
+ "id": "orientation-operating-rules",
139
+ "max_estimated_tokens": 2293,
140
+ "steps": [
141
+ {
142
+ "id": "context",
143
+ "max_estimated_tokens": 1055,
144
+ "accounting_mode": "self_reported"
145
+ },
146
+ {
147
+ "id": "search",
148
+ "max_estimated_tokens": 164,
149
+ "accounting_mode": "self_reported"
150
+ },
151
+ {
152
+ "id": "open",
153
+ "max_estimated_tokens": 878,
154
+ "accounting_mode": "self_reported"
155
+ },
156
+ {
157
+ "id": "in-progress",
158
+ "max_estimated_tokens": 196,
159
+ "accounting_mode": "self_reported"
160
+ }
161
+ ]
162
+ },
9
163
  {
10
164
  "id": "context-bootstrap",
11
165
  "max_estimated_tokens": 1978,
@@ -107,5 +261,5 @@
107
261
  ]
108
262
  }
109
263
  ],
110
- "composite_max_estimated_tokens": 4331
264
+ "composite_max_estimated_tokens": 12477
111
265
  }