@open-agent-toolkit/cli 0.3.10 → 0.3.12

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 (163) hide show
  1. package/README.md +2 -1
  2. package/assets/bundle-metadata.json +1 -1
  3. package/assets/docs/cli-utilities/meta.json +13 -0
  4. package/assets/docs/cli-utilities/tool-packs.md +8 -2
  5. package/assets/docs/cli-utilities/workflow-gates.md +13 -0
  6. package/assets/docs/contributing/code.md +8 -2
  7. package/assets/docs/contributing/documentation.md +7 -1
  8. package/assets/docs/contributing/meta.json +15 -0
  9. package/assets/docs/docs-tooling/add-docs-to-a-repo.md +14 -5
  10. package/assets/docs/docs-tooling/commands.md +26 -8
  11. package/assets/docs/docs-tooling/meta.json +4 -0
  12. package/assets/docs/docs-tooling/workflows.md +2 -2
  13. package/assets/docs/guide/meta.json +4 -0
  14. package/assets/docs/meta.json +14 -0
  15. package/assets/docs/provider-sync/instruction-sync.md +8 -0
  16. package/assets/docs/provider-sync/meta.json +11 -0
  17. package/assets/docs/provider-sync/scope-and-surface.md +3 -4
  18. package/assets/docs/reference/cli-reference.md +5 -2
  19. package/assets/docs/reference/docs-index-contract.md +23 -6
  20. package/assets/docs/reference/file-locations.md +7 -2
  21. package/assets/docs/reference/index.md +1 -1
  22. package/assets/docs/reference/meta.json +10 -0
  23. package/assets/docs/reference/troubleshooting.md +41 -6
  24. package/assets/docs/workflows/ideas/meta.json +4 -0
  25. package/assets/docs/workflows/meta.json +4 -0
  26. package/assets/docs/workflows/projects/cursor-cloud.md +8 -3
  27. package/assets/docs/workflows/projects/evidence-layers.md +6 -8
  28. package/assets/docs/workflows/projects/implementation-execution.md +30 -4
  29. package/assets/docs/workflows/projects/lifecycle.md +7 -1
  30. package/assets/docs/workflows/projects/meta.json +25 -0
  31. package/assets/docs/workflows/projects/orchestration-model.md +5 -6
  32. package/assets/docs/workflows/skills/meta.json +10 -0
  33. package/assets/docs/workflows/skills/recon.md +31 -3
  34. package/assets/public-package-versions.json +4 -4
  35. package/assets/skills/oat-cursor-cloud-projects/SKILL.md +30 -16
  36. package/assets/skills/oat-dispatch-subagents/SKILL.md +4 -12
  37. package/assets/skills/oat-dispatch-subagents/references/managed-claude-example.json +1 -1
  38. package/assets/skills/oat-dispatch-subagents/references/provider-codex.md +51 -0
  39. package/assets/skills/oat-dispatch-subagents/references/record-schema.md +8 -7
  40. package/assets/skills/oat-docs-analyze/SKILL.md +28 -3
  41. package/assets/skills/oat-docs-apply/SKILL.md +3 -3
  42. package/assets/skills/oat-docs-authoring/SKILL.md +1 -1
  43. package/assets/skills/oat-docs-authoring/references/oat-fumadocs-contract.md +5 -2
  44. package/assets/skills/oat-docs-bootstrap/SKILL.md +4 -3
  45. package/assets/skills/oat-docs-bootstrap/assets/AGENTS.md.template +3 -3
  46. package/assets/skills/oat-pjm-decision/SKILL.md +3 -2
  47. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +4 -4
  48. package/assets/skills/oat-project-complete/SKILL.md +47 -2
  49. package/assets/skills/oat-project-design/SKILL.md +10 -6
  50. package/assets/skills/oat-project-discover/SKILL.md +3 -3
  51. package/assets/skills/oat-project-dispatch-subagents/SKILL.md +3 -10
  52. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +4 -4
  53. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  54. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +135 -3
  55. package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +7 -9
  56. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +4 -4
  57. package/assets/skills/oat-project-implement/references/plan-and-resume.md +1 -1
  58. package/assets/skills/oat-project-import-plan/SKILL.md +3 -3
  59. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +4 -4
  60. package/assets/skills/oat-project-new/SKILL.md +3 -3
  61. package/assets/skills/oat-project-next/SKILL.md +50 -7
  62. package/assets/skills/oat-project-plan/SKILL.md +4 -4
  63. package/assets/skills/oat-project-plan-writing/SKILL.md +2 -2
  64. package/assets/skills/oat-project-pr-final/SKILL.md +11 -1
  65. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +4 -4
  66. package/assets/skills/oat-project-promote-spec-driven/SKILL.md +2 -2
  67. package/assets/skills/oat-project-quick-start/SKILL.md +12 -8
  68. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +4 -4
  69. package/assets/skills/oat-project-retro/SKILL.md +6 -3
  70. package/assets/skills/oat-project-review-provide/SKILL.md +2 -2
  71. package/assets/skills/oat-project-review-provide-remote/SKILL.md +2 -2
  72. package/assets/skills/oat-project-spec/SKILL.md +3 -3
  73. package/assets/skills/oat-project-summary/SKILL.md +22 -3
  74. package/assets/skills/oat-repo-improve/SKILL.md +2 -2
  75. package/assets/skills/recon/SKILL.md +24 -3
  76. package/assets/skills/recon/references/packet-contract.md +36 -6
  77. package/assets/skills/recon/references/profiles.md +3 -0
  78. package/assets/skills/recon/references/worker-contract.md +16 -3
  79. package/assets/skills/recon/scripts/create-review-brief.mjs +17 -86
  80. package/assets/skills/recon/scripts/lib/contracts.mjs +158 -41
  81. package/assets/skills/recon/scripts/lib/review-binding.mjs +123 -0
  82. package/assets/skills/recon/scripts/lib/routing.mjs +5 -2
  83. package/assets/skills/recon/scripts/reconcile-ledger.mjs +18 -7
  84. package/assets/skills/recon/scripts/render-packet.mjs +120 -1
  85. package/assets/skills/recon/scripts/validate-packet.mjs +119 -88
  86. package/assets/templates/state.md +1 -0
  87. package/dist/commands/backlog/new.d.ts +2 -2
  88. package/dist/commands/backlog/new.d.ts.map +1 -1
  89. package/dist/commands/backlog/new.js +2 -2
  90. package/dist/commands/decision/new.d.ts +2 -2
  91. package/dist/commands/decision/new.d.ts.map +1 -1
  92. package/dist/commands/decision/new.js +2 -2
  93. package/dist/commands/docs/nav/contents.d.ts +9 -0
  94. package/dist/commands/docs/nav/contents.d.ts.map +1 -1
  95. package/dist/commands/docs/nav/contents.js +7 -2
  96. package/dist/commands/docs/nav/fumadocs.d.ts +58 -0
  97. package/dist/commands/docs/nav/fumadocs.d.ts.map +1 -0
  98. package/dist/commands/docs/nav/fumadocs.js +326 -0
  99. package/dist/commands/docs/nav/sync.d.ts +31 -1
  100. package/dist/commands/docs/nav/sync.d.ts.map +1 -1
  101. package/dist/commands/docs/nav/sync.js +163 -6
  102. package/dist/commands/index.d.ts.map +1 -1
  103. package/dist/commands/index.js +2 -0
  104. package/dist/commands/instructions/instructions.utils.d.ts +19 -0
  105. package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
  106. package/dist/commands/instructions/instructions.utils.js +89 -17
  107. package/dist/commands/instructions/sync/sync.d.ts.map +1 -1
  108. package/dist/commands/instructions/sync/sync.js +49 -2
  109. package/dist/commands/pjm/init.d.ts.map +1 -1
  110. package/dist/commands/pjm/init.js +2 -2
  111. package/dist/commands/project/closeout-check/closeout-invariant.d.ts +97 -0
  112. package/dist/commands/project/closeout-check/closeout-invariant.d.ts.map +1 -0
  113. package/dist/commands/project/closeout-check/closeout-invariant.js +301 -0
  114. package/dist/commands/project/closeout-check/index.d.ts +38 -0
  115. package/dist/commands/project/closeout-check/index.d.ts.map +1 -0
  116. package/dist/commands/project/closeout-check/index.js +117 -0
  117. package/dist/commands/project/complete-state/index.d.ts +4 -0
  118. package/dist/commands/project/complete-state/index.d.ts.map +1 -1
  119. package/dist/commands/project/complete-state/index.js +11 -13
  120. package/dist/commands/project/dispatch/index.d.ts.map +1 -1
  121. package/dist/commands/project/dispatch/index.js +6 -30
  122. package/dist/commands/project/dispatch/record.d.ts +9 -18
  123. package/dist/commands/project/dispatch/record.d.ts.map +1 -1
  124. package/dist/commands/project/dispatch/record.js +15 -212
  125. package/dist/commands/project/index.d.ts.map +1 -1
  126. package/dist/commands/project/index.js +2 -0
  127. package/dist/commands/project/new/scaffold.d.ts +0 -1
  128. package/dist/commands/project/new/scaffold.d.ts.map +1 -1
  129. package/dist/commands/project/new/scaffold.js +19 -20
  130. package/dist/commands/project/promote/promote.d.ts +2 -2
  131. package/dist/commands/project/promote/promote.d.ts.map +1 -1
  132. package/dist/commands/project/promote/promote.js +13 -11
  133. package/dist/commands/shared/frontmatter.d.ts +21 -0
  134. package/dist/commands/shared/frontmatter.d.ts.map +1 -1
  135. package/dist/commands/shared/frontmatter.js +70 -4
  136. package/dist/commands/shared/template-source.d.ts +29 -0
  137. package/dist/commands/shared/template-source.d.ts.map +1 -0
  138. package/dist/commands/{pjm → shared}/template-source.js +24 -7
  139. package/dist/commands/state/generate.js +2 -2
  140. package/dist/commands/template/index.d.ts +10 -0
  141. package/dist/commands/template/index.d.ts.map +1 -0
  142. package/dist/commands/template/index.js +87 -0
  143. package/dist/commands/template/resolve.d.ts +31 -0
  144. package/dist/commands/template/resolve.d.ts.map +1 -0
  145. package/dist/commands/template/resolve.js +86 -0
  146. package/dist/fs/io.d.ts +0 -41
  147. package/dist/fs/io.d.ts.map +1 -1
  148. package/dist/fs/io.js +2 -414
  149. package/dist/providers/identity/absolute-paths.d.ts +4 -13
  150. package/dist/providers/identity/absolute-paths.d.ts.map +1 -1
  151. package/dist/providers/identity/absolute-paths.js +4 -17
  152. package/dist/providers/identity/generic-dispatch-record.d.ts +1 -1
  153. package/dist/providers/identity/generic-dispatch-record.js +2 -2
  154. package/dist/providers/identity/oat-dispatch-record.d.ts +4 -357
  155. package/dist/providers/identity/oat-dispatch-record.d.ts.map +1 -1
  156. package/dist/providers/identity/oat-dispatch-record.js +4 -236
  157. package/dist/release/public-package-contract.d.ts.map +1 -1
  158. package/dist/release/public-package-contract.js +12 -4
  159. package/dist/validation/skills.d.ts.map +1 -1
  160. package/dist/validation/skills.js +32 -6
  161. package/package.json +2 -2
  162. package/dist/commands/pjm/template-source.d.ts +0 -14
  163. package/dist/commands/pjm/template-source.d.ts.map +0 -1
@@ -6,6 +6,12 @@ import { join, resolve } from 'node:path';
6
6
 
7
7
  import { hashFile } from './lib/canonical-json.mjs';
8
8
  import { isDirectExecution } from './lib/cli-entry.mjs';
9
+ import {
10
+ affirmingDispositionByReviewKind,
11
+ classifyUnresolvedIssue,
12
+ unresolvedIssuesBlockClaim,
13
+ } from './lib/contracts.mjs';
14
+ import { reviewBriefEntries } from './lib/review-binding.mjs';
9
15
  import {
10
16
  assertSafeExistingPath,
11
17
  assertSafeOutputPath,
@@ -99,7 +105,7 @@ function intendedRoutingSection(manifest, routing) {
99
105
  '',
100
106
  `- **Manifest routing version:** ${routing.sourceSchemaVersion}`,
101
107
  `- **Approved authority:** ${escapeInline(routing.authority)}`,
102
- `- **Approved limits:** ${routing.waves.length} waves; ${routing.waves.reduce((count, wave) => count + wave.lanes.length, 0)} lanes; concurrency ${routing.maxConcurrency}; deadline ${routing.deadlineSeconds}s; retries ${routing.retryLimit}`,
108
+ `- **Approved limits:** ${routing.waves.length} waves; ${routing.waves.reduce((count, wave) => count + wave.lanes.length, 0)} lanes; concurrency ${routing.maxConcurrency}; deadline ${routing.deadlineSeconds}s; pre-acceptance admission retries per lane ${routing.retryLimit}`,
103
109
  '- **Evidence boundary:** These are normalized approved intended targets and root-recorded condition dispositions. They are not launcher receipts or observations of native runtime identity, usage, cost, or conclusion correctness.',
104
110
  '',
105
111
  '### Waves',
@@ -115,6 +121,115 @@ function intendedRoutingSection(manifest, routing) {
115
121
  ];
116
122
  }
117
123
 
124
+ const assuranceReviewKinds = Object.keys(affirmingDispositionByReviewKind);
125
+
126
+ // The claim IDs a review was briefed on: the entries of the immutable brief
127
+ // its exact reference resolves to, read the way the validator reads them.
128
+ function briefedClaimIds(review, artifacts) {
129
+ const brief = artifacts.find(
130
+ ({ reference, value }) =>
131
+ value.kind === 'recon.review-brief' &&
132
+ reference.path === review.brief?.path &&
133
+ reference.digest === review.brief?.digest,
134
+ )?.value;
135
+ return new Set(
136
+ reviewBriefEntries(brief, review.reviewKind).map((entry) => entry.id),
137
+ );
138
+ }
139
+
140
+ // Every incorporated assurance review, core or thorough, whose brief listed
141
+ // `claimId` but which gave it no disposition. Such a claim is listed as not
142
+ // reviewed. A claim outside a review's brief was never that review's to
143
+ // dispose of, so it is not an omission.
144
+ function omittedReviews(claimId, reviews) {
145
+ return reviews
146
+ .filter(
147
+ ({ value, briefed }) =>
148
+ briefed.has(claimId) &&
149
+ !(value.dispositions ?? []).some((item) => item.claimId === claimId),
150
+ )
151
+ .map(({ value }) => value);
152
+ }
153
+
154
+ function issueText(entry) {
155
+ return typeof entry === 'string' ? entry : (entry?.text ?? '');
156
+ }
157
+
158
+ // Every claim an incorporated review kept below `verified` (an unresolved
159
+ // issue that applies to it, a coverage finding that names it, a
160
+ // non-affirming disposition, or an assurance review that left a briefed claim
161
+ // without a disposition), with the review's own words. Key-claim status
162
+ // alone would let a `complete` packet hide a downgraded non-key claim.
163
+ function reviewDowngradeLines(validatedRun) {
164
+ const { ledger, artifacts, assuranceReviewIds } = validatedRun;
165
+ const assurance = new Set(assuranceReviewIds);
166
+ const reviews = artifacts
167
+ .map(({ value }) => value)
168
+ .filter(
169
+ (value) =>
170
+ value.kind === 'recon.review-result' && assurance.has(value.id),
171
+ );
172
+ // Claim-bearing assurance reviews in table order, each with its brief
173
+ // membership.
174
+ const briefedReviews = reviews
175
+ .filter((review) => assuranceReviewKinds.includes(review.reviewKind))
176
+ .map((value) => ({ value, briefed: briefedClaimIds(value, artifacts) }))
177
+ .sort(
178
+ (left, right) =>
179
+ assuranceReviewKinds.indexOf(left.value.reviewKind) -
180
+ assuranceReviewKinds.indexOf(right.value.reviewKind),
181
+ );
182
+ const lines = [];
183
+ for (const claim of ledger.claims) {
184
+ if (claim.status === 'verified') continue;
185
+ const reasons = [];
186
+ for (const review of reviews) {
187
+ const disposition = (review.dispositions ?? []).find(
188
+ (item) => item.claimId === claim.id,
189
+ );
190
+ if (!disposition) continue;
191
+ if (
192
+ disposition.disposition !==
193
+ affirmingDispositionByReviewKind[review.reviewKind]
194
+ ) {
195
+ reasons.push(`${review.reviewKind} review: ${disposition.disposition}`);
196
+ }
197
+ if (unresolvedIssuesBlockClaim(review, claim.id)) {
198
+ for (const entry of review.unresolvedIssues) {
199
+ const classification = classifyUnresolvedIssue(entry);
200
+ if (
201
+ classification.scope === 'claims' &&
202
+ !classification.claimIds.includes(claim.id)
203
+ ) {
204
+ continue;
205
+ }
206
+ const scope =
207
+ classification.scope === 'claims' ? 'issue' : 'global issue';
208
+ reasons.push(`${review.reviewKind} ${scope}: ${issueText(entry)}`);
209
+ }
210
+ }
211
+ }
212
+ for (const review of omittedReviews(claim.id, briefedReviews)) {
213
+ reasons.push(
214
+ `${review.reviewKind} review: not reviewed (no disposition)`,
215
+ );
216
+ }
217
+ for (const review of reviews) {
218
+ for (const finding of review.coverageFindings ?? []) {
219
+ if (!finding.claimIds.includes(claim.id)) continue;
220
+ reasons.push(
221
+ `${finding.material ? 'material ' : ''}coverage finding ${finding.code}: ${finding.message}`,
222
+ );
223
+ }
224
+ }
225
+ if (reasons.length === 0) continue;
226
+ lines.push(
227
+ `**${escapeInline(claim.id)}** (${escapeInline(claim.status)}): ${escapeInline(claim.statement)} — ${reasons.map(escapeInline).join('; ')}`,
228
+ );
229
+ }
230
+ return lines;
231
+ }
232
+
118
233
  export function renderPacketDocument(validatedRun) {
119
234
  const { manifest, ledger, routing } = assertValidatedRun(validatedRun);
120
235
  const evidenceById = new Map(
@@ -186,6 +301,10 @@ export function renderPacketDocument(validatedRun) {
186
301
  ),
187
302
  ),
188
303
  '',
304
+ '## Review Downgrades',
305
+ '',
306
+ ...bulletLines(reviewDowngradeLines(validatedRun)),
307
+ '',
189
308
  '## Unresolved Questions',
190
309
  '',
191
310
  ...bulletLines(
@@ -3,15 +3,20 @@
3
3
  import { readFile, rm } from 'node:fs/promises';
4
4
  import { join, resolve } from 'node:path';
5
5
 
6
+ import { createReviewBrief } from './create-review-brief.mjs';
6
7
  import { hashCanonicalJson, hashFile, sha256 } from './lib/canonical-json.mjs';
7
8
  import { isDirectExecution } from './lib/cli-entry.mjs';
8
9
  import {
10
+ affirmingDispositionByReviewKind,
9
11
  isDigest,
10
12
  isObject,
11
13
  issue,
12
14
  profiles,
15
+ requiredReviewKinds,
16
+ unresolvedIssuesBlockClaim,
13
17
  validateArtifactShape,
14
18
  } from './lib/contracts.mjs';
19
+ import { reviewBriefEntries } from './lib/review-binding.mjs';
15
20
  import { normalizeManifestRouting } from './lib/routing.mjs';
16
21
  import {
17
22
  assertCanonicalRoot,
@@ -1318,44 +1323,69 @@ function resolveTerminalReconciliation(
1318
1323
  };
1319
1324
  }
1320
1325
 
1321
- function reviewBriefBindsClaim(brief, reviewKind, claim, ledger, manifest) {
1322
- const projected =
1323
- reviewKind === 'semantic' || reviewKind === 'redundant-verification'
1324
- ? brief?.claims?.find((item) => item.id === claim.id)
1325
- : reviewKind === 'adversarial' ||
1326
- reviewKind === 'contradiction-resolution'
1327
- ? brief?.provisionalStatements?.find((item) => item.id === claim.id)
1328
- : brief?.claims?.find((item) => item.id === claim.id);
1329
- if (!projected || projected.statement !== claim.statement) return false;
1330
- if (reviewKind !== 'semantic' && reviewKind !== 'redundant-verification') {
1331
- return Object.keys(projected).sort().join(',') === 'id,statement';
1332
- }
1333
- const evidenceById = new Map(ledger.evidence.map((item) => [item.id, item]));
1334
- const expectedEvidence = claim.evidence.map((link) => {
1335
- const evidence = evidenceById.get(link.evidenceId);
1336
- return evidence
1337
- ? {
1338
- id: evidence.id,
1339
- sourceId: evidence.sourceId,
1340
- displayExcerpt: evidence.displayExcerpt,
1341
- locator: evidence.locator,
1342
- }
1343
- : null;
1344
- });
1345
- if (
1346
- expectedEvidence.some((item) => !item) ||
1347
- hashCanonicalJson(projected.evidence) !==
1348
- hashCanonicalJson(expectedEvidence)
1349
- ) {
1350
- return false;
1351
- }
1352
- const sourceIds = new Set(expectedEvidence.map((item) => item.sourceId));
1353
- const expectedSources = manifest.sources.filter((source) =>
1354
- sourceIds.has(source.id),
1355
- );
1356
- return (
1357
- hashCanonicalJson(brief.sources) === hashCanonicalJson(expectedSources)
1326
+ // Brief integrity is one check: rebuild the brief with the production
1327
+ // generator from the prior ledger and the manifest, for the claim IDs it
1328
+ // lists, and require identical canonical JSON. Any edited, injected,
1329
+ // duplicated, or unprojected field differs, so no field-by-field binding is
1330
+ // needed. Briefs stay blind because the generator is the only projection.
1331
+ function checkReviewBrief(
1332
+ brief,
1333
+ reviewKind,
1334
+ { manifest, ledger, rebuildReviewBrief = createReviewBrief },
1335
+ ) {
1336
+ const entryIds = reviewBriefEntries(brief, reviewKind).map(
1337
+ (entry) => entry?.id,
1358
1338
  );
1339
+ let rebuilt;
1340
+ try {
1341
+ rebuilt = rebuildReviewBrief({
1342
+ id: brief.id,
1343
+ mode: brief.mode,
1344
+ createdAt: brief.createdAt,
1345
+ manifest,
1346
+ ledger,
1347
+ claimIds: entryIds,
1348
+ });
1349
+ } catch (error) {
1350
+ return {
1351
+ ok: false,
1352
+ entryIds,
1353
+ reason: `rebuild failed: ${error instanceof Error ? error.message : error}`,
1354
+ };
1355
+ }
1356
+ if (hashCanonicalJson(rebuilt) === hashCanonicalJson(brief)) {
1357
+ return { ok: true, entryIds };
1358
+ }
1359
+ const keys = [...new Set([...Object.keys(brief), ...Object.keys(rebuilt)])]
1360
+ .sort()
1361
+ .filter(
1362
+ (key) =>
1363
+ !Object.hasOwn(brief, key) ||
1364
+ !Object.hasOwn(rebuilt, key) ||
1365
+ hashCanonicalJson(brief[key]) !== hashCanonicalJson(rebuilt[key]),
1366
+ );
1367
+ return { ok: false, entryIds, reason: `differs at ${keys[0]}` };
1368
+ }
1369
+
1370
+ // One rebuild per distinct brief per validation pass. The key is the exact
1371
+ // artifact reference (path and digest) plus the review kind that selects the
1372
+ // brief's entries; the cache lives only as long as this pass's checker.
1373
+ function createBriefChecker({ manifest, ledger, rebuildReviewBrief }) {
1374
+ const results = new Map();
1375
+ return (briefEntry, reviewKind) => {
1376
+ const key = `${briefEntry.reference?.path}\u0000${briefEntry.reference?.digest}\u0000${reviewKind}`;
1377
+ if (!results.has(key)) {
1378
+ results.set(
1379
+ key,
1380
+ checkReviewBrief(briefEntry.value, reviewKind, {
1381
+ manifest,
1382
+ ledger,
1383
+ rebuildReviewBrief,
1384
+ }),
1385
+ );
1386
+ }
1387
+ return results.get(key);
1388
+ };
1359
1389
  }
1360
1390
 
1361
1391
  function validateReviewBindings(
@@ -1365,6 +1395,7 @@ function validateReviewBindings(
1365
1395
  artifactsByPath,
1366
1396
  reconciliationContext,
1367
1397
  errors,
1398
+ checkBrief,
1368
1399
  ) {
1369
1400
  const claims = new Map(ledger.claims.map((claim) => [claim.id, claim]));
1370
1401
  const priorLedger = reconciliationContext.priorLedger;
@@ -1378,11 +1409,12 @@ function validateReviewBindings(
1378
1409
  ) {
1379
1410
  continue;
1380
1411
  }
1381
- const brief = [...artifactsByPath.values()].find(
1412
+ const briefEntry = [...artifactsByPath.values()].find(
1382
1413
  ({ reference, value }) =>
1383
1414
  sameReference(reference, result.brief) &&
1384
1415
  value.kind === 'recon.review-brief',
1385
- )?.value;
1416
+ );
1417
+ const brief = briefEntry?.value;
1386
1418
  const expectedMode =
1387
1419
  result.reviewKind === 'semantic' ||
1388
1420
  result.reviewKind === 'redundant-verification'
@@ -1401,27 +1433,26 @@ function validateReviewBindings(
1401
1433
  );
1402
1434
  continue;
1403
1435
  }
1436
+ const check = checkBrief(briefEntry, result.reviewKind);
1437
+ if (!check.ok) {
1438
+ errors.push(
1439
+ issue(
1440
+ 'REVIEW_BRIEF_MISMATCH',
1441
+ `Review ${result.id} brief is not the production brief rebuilt from the prior ledger and manifest (${check.reason})`,
1442
+ result.id,
1443
+ ),
1444
+ );
1445
+ }
1404
1446
  const seen = new Set();
1405
1447
  for (const disposition of result.dispositions) {
1406
- const currentClaim = claims.get(disposition.claimId);
1407
- const priorClaim = priorClaims.get(disposition.claimId);
1408
- const claim = priorClaim ?? currentClaim;
1409
- const bindingLedger = priorClaim ? priorLedger : ledger;
1410
1448
  if (
1411
- !claim ||
1412
1449
  seen.has(disposition.claimId) ||
1413
- !reviewBriefBindsClaim(
1414
- brief,
1415
- result.reviewKind,
1416
- claim,
1417
- bindingLedger,
1418
- manifest,
1419
- )
1450
+ !check.entryIds.includes(disposition.claimId)
1420
1451
  ) {
1421
1452
  errors.push(
1422
1453
  issue(
1423
1454
  'REVIEW_BRIEF_MISMATCH',
1424
- `Review ${result.id} dispositions must exactly match claim-bearing brief projections`,
1455
+ `Review ${result.id} dispositions must be unique members of its brief's claims`,
1425
1456
  result.id,
1426
1457
  ),
1427
1458
  );
@@ -1869,16 +1900,13 @@ function validateReconciliation(
1869
1900
  );
1870
1901
  }
1871
1902
  if (disposition?.disposition === 'accepted-gap') {
1903
+ // A material finding forces every named claim below `verified`, the
1904
+ // same downgrade the reconciler applies. The reviewer's per-statement
1905
+ // disposition may stay `covered`: a question omission is not a
1906
+ // statement defect.
1872
1907
  for (const claimId of finding.claimIds) {
1873
1908
  const claim = currentClaims.get(claimId);
1874
- const coverageDisposition = coverage.dispositions.find(
1875
- (item) => item.claimId === claimId,
1876
- );
1877
- if (
1878
- finding.material === true &&
1879
- (claim?.status === 'verified' ||
1880
- coverageDisposition?.disposition !== 'gap')
1881
- ) {
1909
+ if (finding.material === true && claim?.status === 'verified') {
1882
1910
  errors.push(
1883
1911
  issue(
1884
1912
  'MATERIAL_COVERAGE_ASSURANCE_EXCEEDED',
@@ -1969,7 +1997,7 @@ function validateDerivedSourceGaps(manifest, ledger, errors) {
1969
1997
  }
1970
1998
  }
1971
1999
 
1972
- function validateAssurance(validatedRun, errors) {
2000
+ function validateAssurance(validatedRun, errors, checkBrief) {
1973
2001
  const { manifest, ledger, achievedProfile } = validatedRun;
1974
2002
  const exactEvidence = new Set(validatedRun.exactEvidenceIds);
1975
2003
  const assuranceReviewIds = new Set(validatedRun.assuranceReviewIds);
@@ -1979,9 +2007,6 @@ function validateAssurance(validatedRun, errors) {
1979
2007
  const evidenceById = new Map(
1980
2008
  (ledger.evidence ?? []).map((item) => [item.id, item]),
1981
2009
  );
1982
- const priorClaims = new Map(
1983
- (validatedRun.priorLedger?.claims ?? []).map((claim) => [claim.id, claim]),
1984
- );
1985
2010
  const claimIds = new Set((ledger.claims ?? []).map((claim) => claim.id));
1986
2011
  for (const claimId of ledger.synthesis?.keyClaimIds ?? []) {
1987
2012
  if (!claimIds.has(claimId)) {
@@ -2056,14 +2081,12 @@ function validateAssurance(validatedRun, errors) {
2056
2081
  ),
2057
2082
  );
2058
2083
  }
2059
- const required = new Map([
2060
- ['semantic', 'affirmed'],
2061
- ['adversarial', 'unchallenged'],
2062
- ['coverage', 'covered'],
2063
- ]);
2064
- if (achievedProfile === 'thorough') {
2065
- required.set('redundant-verification', 'affirmed');
2066
- }
2084
+ const required = new Map(
2085
+ [
2086
+ ...requiredReviewKinds,
2087
+ ...(achievedProfile === 'thorough' ? ['redundant-verification'] : []),
2088
+ ].map((kind) => [kind, affirmingDispositionByReviewKind[kind]]),
2089
+ );
2067
2090
  const satisfied = new Set();
2068
2091
  const reviewerLanes = new Set();
2069
2092
  for (const reviewId of new Set(claim.reviewIds ?? [])) {
@@ -2128,11 +2151,12 @@ function validateAssurance(validatedRun, errors) {
2128
2151
  );
2129
2152
  }
2130
2153
  reviewerLanes.add(artifact.reviewerLane);
2131
- const brief = [...artifactsById.values()].find(
2154
+ const briefEntry = [...artifactsById.values()].find(
2132
2155
  ({ reference, value }) =>
2133
2156
  sameReference(reference, artifact.brief) &&
2134
2157
  value.kind === 'recon.review-brief',
2135
- )?.value;
2158
+ );
2159
+ const brief = briefEntry?.value;
2136
2160
  const expectedMode =
2137
2161
  artifact.reviewKind === 'semantic' ||
2138
2162
  artifact.reviewKind === 'redundant-verification'
@@ -2141,17 +2165,15 @@ function validateAssurance(validatedRun, errors) {
2141
2165
  artifact.reviewKind === 'contradiction-resolution'
2142
2166
  ? 'adversary'
2143
2167
  : 'coverage';
2168
+ const briefCheck = brief
2169
+ ? checkBrief(briefEntry, artifact.reviewKind)
2170
+ : null;
2144
2171
  if (
2145
2172
  !brief ||
2146
2173
  brief.runId !== ledger.runId ||
2147
2174
  brief.mode !== expectedMode ||
2148
- !reviewBriefBindsClaim(
2149
- brief,
2150
- artifact.reviewKind,
2151
- priorClaims.get(claim.id) ?? claim,
2152
- priorClaims.has(claim.id) ? validatedRun.priorLedger : ledger,
2153
- manifest,
2154
- ) ||
2175
+ !briefCheck.ok ||
2176
+ !briefCheck.entryIds.includes(claim.id) ||
2155
2177
  !artifact.permittedInputs?.some((reference) =>
2156
2178
  sameReference(reference, artifact.brief),
2157
2179
  )
@@ -2168,9 +2190,7 @@ function validateAssurance(validatedRun, errors) {
2168
2190
  (item) => item.claimId === claim.id,
2169
2191
  );
2170
2192
  const expectedDisposition =
2171
- artifact.reviewKind === 'contradiction-resolution'
2172
- ? 'resolved'
2173
- : required.get(artifact.reviewKind);
2193
+ affirmingDispositionByReviewKind[artifact.reviewKind];
2174
2194
  if (
2175
2195
  dispositions.length !== 1 ||
2176
2196
  dispositions[0].disposition !== expectedDisposition
@@ -2183,11 +2203,11 @@ function validateAssurance(validatedRun, errors) {
2183
2203
  ),
2184
2204
  );
2185
2205
  }
2186
- if ((artifact.unresolvedIssues ?? []).length > 0) {
2206
+ if (unresolvedIssuesBlockClaim(artifact, claim.id)) {
2187
2207
  errors.push(
2188
2208
  issue(
2189
2209
  'REVIEW_DISPOSITION_MISMATCH',
2190
- `Review ${reviewId} retains unresolved issues`,
2210
+ `Review ${reviewId} retains an unresolved issue that applies to this claim`,
2191
2211
  claim.id,
2192
2212
  ),
2193
2213
  );
@@ -2280,7 +2300,12 @@ function validateAssurance(validatedRun, errors) {
2280
2300
  }
2281
2301
  }
2282
2302
 
2283
- export async function compileValidatedRun(packetDirectory) {
2303
+ // `rebuildReviewBrief` is a test seam for counting brief rebuilds; production
2304
+ // callers use the default generator.
2305
+ export async function compileValidatedRun(
2306
+ packetDirectory,
2307
+ { rebuildReviewBrief = createReviewBrief } = {},
2308
+ ) {
2284
2309
  const packetRoot = resolve(packetDirectory);
2285
2310
  const errors = [];
2286
2311
  const warnings = [];
@@ -2532,6 +2557,11 @@ export async function compileValidatedRun(packetDirectory) {
2532
2557
  errors,
2533
2558
  );
2534
2559
  }
2560
+ const checkBrief = createBriefChecker({
2561
+ manifest,
2562
+ ledger: reconciliationContext.priorLedger ?? ledger,
2563
+ rebuildReviewBrief,
2564
+ });
2535
2565
  validateReviewBindings(
2536
2566
  manifest,
2537
2567
  ledger,
@@ -2539,6 +2569,7 @@ export async function compileValidatedRun(packetDirectory) {
2539
2569
  artifactsByPath,
2540
2570
  reconciliationContext,
2541
2571
  errors,
2572
+ checkBrief,
2542
2573
  );
2543
2574
  validateReconciliation(
2544
2575
  manifest,
@@ -2586,7 +2617,7 @@ export async function compileValidatedRun(packetDirectory) {
2586
2617
  assuranceReviewIds,
2587
2618
  reconciliationContext,
2588
2619
  });
2589
- validateAssurance(validatedRun, errors);
2620
+ validateAssurance(validatedRun, errors, checkBrief);
2590
2621
  }
2591
2622
 
2592
2623
  const valid = errors.length === 0;
@@ -54,6 +54,7 @@ oat_workflow_origin: native # native | imported
54
54
  # implementation_fingerprint: null # new generations use sha256:effective-delta-v2:<digest>; stored v1 values keep v1 semantics
55
55
  # freshness_head: null # rolling accepted tree checkpoint
56
56
  # freshness_fingerprint: null # full effective delta at freshness_head
57
+ # waivers: [] # append-only operator waivers {waived_by, reason, from_commit, to_commit, covered_fingerprint, waived_at}; never self-issued, never under OAT_AUTONOMOUS=1
57
58
  # launch_state: not_started # not_started | intent_persisted | accepted | result_persisted | not_accepted
58
59
  # launch_attempt_id: null
59
60
  # launch_started_at: null
@@ -1,4 +1,4 @@
1
- import { type PjmTemplateTier } from '../pjm/template-source.js';
1
+ import { type TemplateTier } from '../shared/template-source.js';
2
2
  import { initializeBacklog } from './init.js';
3
3
  import { regenerateBacklogIndex, type RegenerateBacklogIndexResult } from './regenerate-index.js';
4
4
  export interface CreateBacklogItemOptions {
@@ -20,7 +20,7 @@ export interface CreateBacklogItemResult {
20
20
  backlogRoot: string;
21
21
  filePath: string;
22
22
  templatePath: string;
23
- templateTier: PjmTemplateTier;
23
+ templateTier: TemplateTier;
24
24
  index: RegenerateBacklogIndexResult;
25
25
  }
26
26
  export interface CreateBacklogItemDependencies {
@@ -1 +1 @@
1
- {"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/new.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EACL,sBAAsB,EACtB,KAAK,4BAA4B,EAClC,MAAM,oBAAoB,CAAC;AAS5B,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,eAAe,CAAC;IAC9B,KAAK,EAAE,4BAA4B,CAAC;CACrC;AAED,MAAM,WAAW,6BAA6B;IAC5C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;CACvD;AAgKD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,EACjC,SAAS,GAAE,OAAO,CAAC,6BAA6B,CAAM,GACrD,OAAO,CAAC,uBAAuB,CAAC,CAwDlC"}
1
+ {"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/new.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EACL,sBAAsB,EACtB,KAAK,4BAA4B,EAClC,MAAM,oBAAoB,CAAC;AAS5B,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,YAAY,CAAC;IAC3B,KAAK,EAAE,4BAA4B,CAAC;CACrC;AAED,MAAM,WAAW,6BAA6B;IAC5C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;CACvD;AAgKD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,EACjC,SAAS,GAAE,OAAO,CAAC,6BAA6B,CAAM,GACrD,OAAO,CAAC,uBAAuB,CAAC,CAwDlC"}
@@ -1,8 +1,8 @@
1
1
  import { access, readFile, rm, writeFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { parseAssociatedIssues, serializeAssociatedIssues, } from '../pjm/remote/association.js';
4
- import { resolvePjmTemplate, } from '../pjm/template-source.js';
5
4
  import { stripTemplateFrontmatter } from '../shared/strip-template-frontmatter.js';
5
+ import { resolveTemplate, } from '../shared/template-source.js';
6
6
  import YAML from 'yaml';
7
7
  import { initializeBacklog } from './init.js';
8
8
  import { regenerateBacklogIndex, } from './regenerate-index.js';
@@ -106,7 +106,7 @@ export async function createBacklogItem(options, overrides = {}) {
106
106
  if ((await pathExists(filePath)) || (await pathExists(archivedPath))) {
107
107
  throw new Error(`Backlog item ${id} already exists. Use a more specific title to disambiguate.`);
108
108
  }
109
- const template = await resolvePjmTemplate({
109
+ const template = await resolveTemplate({
110
110
  name: 'backlog-item.md',
111
111
  assetsRoot: options.assetsRoot,
112
112
  templatesRoot: options.templatesRoot,
@@ -1,4 +1,4 @@
1
- import { type PjmTemplateTier } from '../pjm/template-source.js';
1
+ import { type TemplateTier } from '../shared/template-source.js';
2
2
  export interface CreateDecisionRecordOptions {
3
3
  decisionsRoot: string;
4
4
  assetsRoot: string;
@@ -16,7 +16,7 @@ export interface CreateDecisionRecordResult {
16
16
  decisionsRoot: string;
17
17
  filePath: string;
18
18
  templatePath: string;
19
- templateTier: PjmTemplateTier;
19
+ templateTier: TemplateTier;
20
20
  }
21
21
  export declare function createDecisionRecord(options: CreateDecisionRecordOptions): Promise<CreateDecisionRecordResult>;
22
22
  //# sourceMappingURL=new.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/decision/new.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AAUvC,MAAM,WAAW,2BAA2B;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,eAAe,CAAC;CAC/B;AAkFD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,0BAA0B,CAAC,CAoDrC"}
1
+ {"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/decision/new.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,kCAAkC,CAAC;AAS1C,MAAM,WAAW,2BAA2B;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,YAAY,CAAC;CAC5B;AAkFD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,0BAA0B,CAAC,CAoDrC"}
@@ -1,7 +1,7 @@
1
1
  import { access, mkdir, rm, writeFile } from 'node:fs/promises';
2
2
  import { dirname, join } from 'node:path';
3
- import { resolvePjmTemplate, } from '../pjm/template-source.js';
4
3
  import { stripTemplateFrontmatter } from '../shared/strip-template-frontmatter.js';
4
+ import { resolveTemplate, } from '../shared/template-source.js';
5
5
  import YAML from 'yaml';
6
6
  import { assertDecisionIndexScaffold, regenerateDecisionIndex, } from './regenerate-index.js';
7
7
  import { generateDecisionId } from './shared/generate-id.js';
@@ -62,7 +62,7 @@ export async function createDecisionRecord(options) {
62
62
  if (await pathExists(filePath)) {
63
63
  throw new Error(`Decision record ${id} already exists. Use a more specific title to disambiguate.`);
64
64
  }
65
- const template = await resolvePjmTemplate({
65
+ const template = await resolveTemplate({
66
66
  name: 'decision.md',
67
67
  assetsRoot: options.assetsRoot,
68
68
  templatesRoot: options.templatesRoot,
@@ -9,6 +9,15 @@ interface BuildDocsNavTreeOptions {
9
9
  dirRelativePath?: string;
10
10
  visitedDirectories?: Set<string>;
11
11
  }
12
+ export declare function resolveEntryTarget(docsRoot: string, directoryPath: string, dirRelativePath: string, entry: DocsContentsEntry,
13
+ /**
14
+ * Page extensions a Contents link may target. MkDocs renders only `.md`;
15
+ * Fumadocs also renders `.mdx` pages.
16
+ */
17
+ pageExtensions?: readonly string[]): Promise<{
18
+ kind: 'page' | 'section';
19
+ path: string;
20
+ }>;
12
21
  export declare function buildDocsNavTree(options: BuildDocsNavTreeOptions): Promise<DocsNavTree>;
13
22
  export {};
14
23
  //# sourceMappingURL=contents.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"contents.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/nav/contents.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,UAAU,SAAa,GACtB,iBAAiB,EAAE,CAqCrB;AAED,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;AAE/E,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAClC;AAgFD,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,WAAW,CAAC,CA8DtB"}
1
+ {"version":3,"file":"contents.d.ts","sourceRoot":"","sources":["../../../../src/commands/docs/nav/contents.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,UAAU,SAAa,GACtB,iBAAiB,EAAE,CAqCrB;AAED,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;AAE/E,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAClC;AAMD,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,iBAAiB;AACxB;;;GAGG;AACH,cAAc,GAAE,SAAS,MAAM,EAAY,GAC1C,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAmErD;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,WAAW,CAAC,CA8DtB"}
@@ -30,7 +30,12 @@ export function parseIndexContents(markdown, sourcePath = 'index.md') {
30
30
  function toPosixPath(path) {
31
31
  return path.replaceAll('\\', '/');
32
32
  }
33
- async function resolveEntryTarget(docsRoot, directoryPath, dirRelativePath, entry) {
33
+ export async function resolveEntryTarget(docsRoot, directoryPath, dirRelativePath, entry,
34
+ /**
35
+ * Page extensions a Contents link may target. MkDocs renders only `.md`;
36
+ * Fumadocs also renders `.mdx` pages.
37
+ */
38
+ pageExtensions = ['.md']) {
34
39
  const href = entry.href.split('#', 1)[0]?.trim() ?? '';
35
40
  if (!href) {
36
41
  throw new Error(`Invalid contents link "${entry.href}" in ${join(docsRoot, dirRelativePath, 'index.md')}`);
@@ -51,7 +56,7 @@ async function resolveEntryTarget(docsRoot, directoryPath, dirRelativePath, entr
51
56
  if (!(await fileExists(targetPath))) {
52
57
  throw new Error(`Contents link "${entry.href}" in ${join(docsRoot, dirRelativePath, 'index.md')} does not resolve to a Markdown file`);
53
58
  }
54
- if (extname(targetPath) !== '.md') {
59
+ if (!pageExtensions.includes(extname(targetPath))) {
55
60
  throw new Error(`Contents link "${entry.href}" in ${join(docsRoot, dirRelativePath, 'index.md')} must point to a Markdown file or directory index`);
56
61
  }
57
62
  if (basename(targetPath) === 'index.md') {