@nikovirtala/adr 0.4.0 → 0.5.0

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.
@@ -1,6 +1,6 @@
1
- import { C as headTree, D as renderDecisionIndex, E as indexPath, S as firstLine, b as INDEX_AFTER, c as followScopeRenames, j as numberDecisions, k as writeIndexCopy, v as BUILTIN_TEMPLATES, w as holdsText, x as decisionIndexCheck } from "./search-CwIPlNGd.js";
2
- import { t as adrKind } from "./kind-DjvA3dyA.js";
3
- import { ConfigError, InvalidArgumentError, UsageError, anchorCheck, assertNoHookConflicts, classifyPath, commitWithRecovery, exitCode, finding, foldChecks, gitTemplates, governedChanges, hookSnippet, installHooks, narrowedScopes, overlayTree, parseEventFileName, projectLog, readToolConfig, renderCommitMessage, replaceFile, resolveAttribution, scopeGlobCheck, stagedRenames, templateHistoryRef, uninstallHooks, verifyLog, worktreeReader, writeIndexFile, writeNewFile } from "@nikovirtala/ledger";
1
+ import { A as numberDecisions, C as INDEX_AFTER, D as renderDecisionIndex, E as indexPath, b as readDecisionSources, c as followScopeRenames, v as BUILTIN_TEMPLATES, w as decisionIndexCheck } from "./search-CTspg2pd.js";
2
+ import { t as adrKind } from "./kind-C3DiBuUp.js";
3
+ import { ConfigError, InvalidArgumentError, UsageError, anchorCheck, assertNoHookConflicts, classifyPath, commitWithRecovery, eventTrailers, exitCode, finding, firstLine, foldChecks, gitTemplates, governedChanges, headTree, holdsText, hookSnippet, installHooks, narrowedScopes, overlayTree, parseEventFileName, projectLog, readToolConfig, renderCommitMessage, replaceFile, resolveAttribution, scopeGlobCheck, sourceFindings, stagedRenames, templateHistoryRef, uninstallHooks, verifyLog, worktreeReader, writeIndexCopy, writeIndexFile, writeNewFile } from "@nikovirtala/ledger";
4
4
  import { access, constants, rm } from "node:fs/promises";
5
5
  import { join, relative, resolve, sep } from "node:path";
6
6
  // src/verify.ts
@@ -76,12 +76,17 @@ function scopeNarrowed(log) {
76
76
  * lint against the templates in the head tree and, for older versions, the history of
77
77
  * `historyRef`, the scope-glob and anchor checks, and the decision checks over code
78
78
  * (`decision/governed-by-inactive`, `decision/scope-narrowed`). With `[log] index = "committed"`,
79
- * also the freshness of `<root>/index.md`.
79
+ * also the freshness of `<root>/index.md`. With `sources` (`readDecisionSources`), qualified
80
+ * references are checked against the cached source logs, and each referenced source that isn't
81
+ * cached gets one `sources/not-fetched` warning.
80
82
  */
81
- function adrChecks(log, historyRef) {
83
+ function adrChecks(log, historyRef, sources) {
82
84
  return [
83
85
  ...foldChecks(adrKind, {
84
- options: { acceptRequiresHuman: log.adr.acceptRequiresHuman },
86
+ options: {
87
+ acceptRequiresHuman: log.adr.acceptRequiresHuman,
88
+ sources: sources?.logs ?? null
89
+ },
85
90
  templates: gitTemplates(log.git, { historyRef })
86
91
  }),
87
92
  scopeGlobCheck(),
@@ -91,17 +96,66 @@ function adrChecks(log, historyRef) {
91
96
  }),
92
97
  governedByInactive(log),
93
98
  scopeNarrowed(log),
94
- ...log.adr.index === "committed" ? [decisionIndexCheck(log)] : []
99
+ ...log.adr.index === "committed" ? [decisionIndexCheck(log)] : [],
100
+ ...sources === void 0 ? [] : [sourcesCheck(log, sources.statuses)]
95
101
  ];
96
102
  }
97
- /** Verifies the decision log in `mode` (staged, range or full) with `adrChecks`. */
103
+ /** The source names that qualified references in a folded decision log name. */
104
+ function referencedSources(head) {
105
+ const out = /* @__PURE__ */ new Set();
106
+ const add = (ref) => {
107
+ const at = ref.indexOf(":");
108
+ if (at > 0) out.add(ref.slice(0, at));
109
+ };
110
+ for (const s of head.subjects.values()) for (const { event } of s.applied) if (event.type === "proposed") for (const ref of event.data.supersedes ?? []) add(ref);
111
+ else if (event.type === "superseded") add(event.data.by);
112
+ else if (event.type === "linked") add(event.data.to);
113
+ return out;
114
+ }
115
+ /**
116
+ * `sources/not-fetched` (warning) once per uncached source that a qualified reference in the head
117
+ * names. It runs in every scope, because it is about the log, not about one decision.
118
+ */
119
+ function sourcesCheck(log, statuses) {
120
+ return {
121
+ id: "decision-sources",
122
+ async run(ctx) {
123
+ const head = ctx.folded?.head;
124
+ if (head === void 0) return [];
125
+ return sourceFindings(statuses, {
126
+ tool: "adr",
127
+ file: log.configPath,
128
+ referenced: referencedSources(head)
129
+ });
130
+ }
131
+ };
132
+ }
133
+ /**
134
+ * The inputs outside the decision events that adr's checks read: both candidate config paths (so
135
+ * adding a config counts) and the log's templates. A staged change to one widens `verify --staged`
136
+ * to every decision, because a policy, scope, severity, index or template change can make an
137
+ * unchanged decision fail.
138
+ */
139
+ function decisionVerifyInputs(log) {
140
+ const root = log.layout.root;
141
+ return [
142
+ "adr.toml",
143
+ `${root}/adr.toml`,
144
+ `${root}/templates/`
145
+ ];
146
+ }
147
+ /**
148
+ * Verifies the decision log in `mode` (staged, range or full) with `adrChecks`, reading the
149
+ * sources from the local cache only.
150
+ */
98
151
  async function verifyDecisions(log, mode) {
99
152
  return verifyLog({
100
153
  git: log.git,
101
154
  kind: adrKind,
102
155
  layout: log.layout,
103
156
  mode,
104
- checks: adrChecks(log, await templateHistoryRef(log.git, mode))
157
+ checks: adrChecks(log, await templateHistoryRef(log.git, mode), await readDecisionSources(log, { fetch: "cache" })),
158
+ widenOn: decisionVerifyInputs(log)
105
159
  });
106
160
  }
107
161
  // src/hooks.ts
@@ -138,7 +192,8 @@ function decisionHookCommands(log) {
138
192
  "--base",
139
193
  "@{upstream}",
140
194
  "--ref",
141
- "HEAD"
195
+ "HEAD",
196
+ "--fetch-sources"
142
197
  ],
143
198
  "post-merge": [
144
199
  ...r,
@@ -206,8 +261,9 @@ function printDecisionHooks(log, manager) {
206
261
  /** The `ADR-Event` trailer key as git compares keys: case-insensitively. */
207
262
  const ADR_EVENT = /^adr-event[ \t]*:/im;
208
263
  /**
209
- * The `commit-msg` step (P1 §7.1): appends one `ADR`/`ADR-Event` pair per staged added event file
210
- * of the log, by path, keeping repeats (`ifExists: "add"`). It does nothing for a message that
264
+ * The `commit-msg` step (P1 §7.1): appends one `ADR`/`ADR-Event` pair per decision and event type
265
+ * among the staged added event files of the log (`eventTrailers`, ?6), in path order, keeping
266
+ * pairs another tool already added (`ifExists: "add"`). It does nothing for a message that
211
267
  * already has an `ADR-Event:` trailer (an `adr … --commit` message), or during a merge, whose
212
268
  * events came with their own commits' trailers. Throws `UsageError` when the file is missing.
213
269
  */
@@ -227,10 +283,17 @@ async function addHookTrailers(log, messageFile) {
227
283
  ]);
228
284
  if (ADR_EVENT.test(Buffer.from(parsed.stdout).toString("utf8"))) return result(log, "trailers", null);
229
285
  if (await git.resolveCommit("MERGE_HEAD") !== null) return result(log, "trailers", null);
230
- const trailers = (await git.stagedPaths([`${layout.root}/${layout.events}`], { addedOnly: true })).flatMap((p) => {
286
+ const refs = (await git.stagedPaths([`${layout.root}/${layout.events}`], { addedOnly: true })).flatMap((p) => {
231
287
  const c = classifyPath(layout, p);
232
288
  const name = c.area === "event" && !c.file.includes("/") ? parseEventFileName(c.file) : null;
233
- return c.area === "event" && c.subject !== null && name !== null ? [["ADR", c.subject], ["ADR-Event", name.type]] : [];
289
+ return c.area === "event" && c.subject !== null && name !== null ? [{
290
+ subject: c.subject,
291
+ type: name.type
292
+ }] : [];
293
+ });
294
+ const trailers = eventTrailers(refs, {
295
+ subject: "ADR",
296
+ event: "ADR-Event"
234
297
  });
235
298
  await git.appendTrailers(file, trailers, { ifExists: "add" });
236
299
  return result(log, "trailers", null, { trailers });
@@ -583,6 +646,6 @@ async function initDecisionLog(log, o = {}) {
583
646
  notes
584
647
  };
585
648
  }
586
- export { installDecisionHooks as a, uninstallDecisionHooks as c, decisionHookCommands as i, adrChecks as l, SKILL_MD as n, printDecisionHooks as o, addHookTrailers as r, runPreCommitHook as s, initDecisionLog as t, verifyDecisions as u };
649
+ export { installDecisionHooks as a, uninstallDecisionHooks as c, verifyDecisions as d, decisionHookCommands as i, adrChecks as l, SKILL_MD as n, printDecisionHooks as o, addHookTrailers as r, runPreCommitHook as s, initDecisionLog as t, decisionVerifyInputs as u };
587
650
 
588
- //# sourceMappingURL=init-YgW7g9Fx.js.map
651
+ //# sourceMappingURL=init-CaKKCINh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-CaKKCINh.js","names":[],"sources":["../src/verify.ts","../src/hooks.ts","../src/skill.ts","../src/init.ts"],"sourcesContent":["import {\n anchorCheck,\n type Check,\n type CheckContext,\n type Finding,\n type FoldedLogOf,\n finding,\n foldChecks,\n type GovernedChange,\n gitTemplates,\n governedChanges,\n narrowedScopes,\n projectLog,\n type SourceStatus,\n scopeGlobCheck,\n sourceFindings,\n templateHistoryRef,\n type VerifyMode,\n verifyLog,\n} from \"@nikovirtala/ledger\";\nimport { adrKind, type DecisionView } from \"./kind.js\";\nimport { type DecisionLog, numberDecisions } from \"./log.js\";\nimport { decisionIndexCheck } from \"./log-index.js\";\nimport { type DecisionSources, readDecisionSources } from \"./sources.js\";\n\n/** At most this many paths are named in a `decision/scope-narrowed` message. */\nconst NARROWED_PATHS = 3;\n\n/** The head's decision views with the read's numbering, for the displays in messages. */\nasync function headViews(\n log: DecisionLog,\n ctx: CheckContext,\n): Promise<ReadonlyMap<string, DecisionView>> {\n const head = ctx.folded?.head;\n if (head === undefined) return new Map();\n return projectLog(adrKind, head as FoldedLogOf<typeof adrKind>, {\n numbering: await numberDecisions(log),\n }).views;\n}\n\n/**\n * `decision/governed-by-inactive` (`[verify] governed_by_inactive`, default warning): a changed\n * code file that a superseded or deprecated decision governs, by glob or anchor (01:366).\n */\nfunction governedByInactive(log: DecisionLog): Check {\n return {\n id: \"decision-governed-by-inactive\",\n async run(ctx) {\n const changes = await governedChanges(ctx, {\n keyword: log.adr.anchorKeyword,\n exclude: log.adr.anchorExclude,\n statuses: [\"superseded\", \"deprecated\"],\n });\n if (changes.length === 0) return [];\n const views = await headViews(log, ctx);\n const display = (id: string) => views.get(id)?.display ?? id;\n const message = (c: GovernedChange): string => {\n const what = `${c.path} is governed by ${display(c.subject)}`;\n if (c.status === \"deprecated\") {\n return `${what}, which is deprecated; check whether the change still needs it`;\n }\n const next = views.get(c.subject)?.superseded_by ?? null;\n return next === null\n ? `${what}, which is superseded; check the change against its successor`\n : `${what}, which is superseded by ${display(next)}; check the change against ${display(next)}`;\n };\n return changes.map((c) =>\n finding({\n severity: log.adr.governedByInactive,\n rule: \"decision/governed-by-inactive\",\n message: message(c),\n file: c.path,\n subject: c.subject,\n }),\n );\n },\n };\n}\n\n/**\n * `decision/scope-narrowed` (warning): an accepted decision whose new scope stops covering files\n * that still exist (01:367), which calls for a check against its Scope section.\n */\nfunction scopeNarrowed(log: DecisionLog): Check {\n return {\n id: \"decision-scope-narrowed\",\n async run(ctx) {\n const narrowed = await narrowedScopes(ctx, { statuses: [\"accepted\"] });\n if (narrowed.length === 0) return [];\n const views = await headViews(log, ctx);\n return narrowed.map((n) => {\n const count = n.lost.length;\n const files = count === 1 ? \"1 file that still exists\" : `${count} files that still exist`;\n const named = n.lost.slice(0, NARROWED_PATHS).join(\", \");\n const more = count > NARROWED_PATHS ? \", …\" : \"\";\n return finding({\n severity: \"warning\",\n rule: \"decision/scope-narrowed\",\n message: `${views.get(n.subject)?.display ?? n.subject}'s scope no longer covers ${files} (${named}${more}); check the change against its Scope section`,\n file: n.file,\n subject: n.subject,\n });\n });\n },\n };\n}\n\n/**\n * Every check for the log: ledger's mechanical and fold checks with adr's actor policy, template\n * lint against the templates in the head tree and, for older versions, the history of\n * `historyRef`, the scope-glob and anchor checks, and the decision checks over code\n * (`decision/governed-by-inactive`, `decision/scope-narrowed`). With `[log] index = \"committed\"`,\n * also the freshness of `<root>/index.md`. With `sources` (`readDecisionSources`), qualified\n * references are checked against the cached source logs, and each referenced source that isn't\n * cached gets one `sources/not-fetched` warning.\n */\nexport function adrChecks(\n log: DecisionLog,\n historyRef: string | null,\n sources?: DecisionSources,\n): readonly Check[] {\n return [\n ...foldChecks(adrKind, {\n options: {\n acceptRequiresHuman: log.adr.acceptRequiresHuman,\n sources: sources?.logs ?? null,\n },\n templates: gitTemplates(log.git, { historyRef }),\n }),\n scopeGlobCheck(),\n anchorCheck({ keyword: log.adr.anchorKeyword, exclude: log.adr.anchorExclude }),\n governedByInactive(log),\n scopeNarrowed(log),\n ...(log.adr.index === \"committed\" ? [decisionIndexCheck(log)] : []),\n ...(sources === undefined ? [] : [sourcesCheck(log, sources.statuses)]),\n ];\n}\n\n/** The source names that qualified references in a folded decision log name. */\nexport function referencedSources(head: FoldedLogOf<typeof adrKind>): Set<string> {\n const out = new Set<string>();\n const add = (ref: string) => {\n const at = ref.indexOf(\":\");\n if (at > 0) out.add(ref.slice(0, at));\n };\n for (const s of head.subjects.values()) {\n for (const { event } of s.applied) {\n if (event.type === \"proposed\") for (const ref of event.data.supersedes ?? []) add(ref);\n else if (event.type === \"superseded\") add(event.data.by);\n else if (event.type === \"linked\") add(event.data.to);\n }\n }\n return out;\n}\n\n/**\n * `sources/not-fetched` (warning) once per uncached source that a qualified reference in the head\n * names. It runs in every scope, because it is about the log, not about one decision.\n */\nfunction sourcesCheck(log: DecisionLog, statuses: readonly SourceStatus[]): Check {\n return {\n id: \"decision-sources\",\n async run(ctx) {\n const head = ctx.folded?.head;\n if (head === undefined) return [];\n return sourceFindings(statuses, {\n tool: \"adr\",\n file: log.configPath,\n referenced: referencedSources(head as FoldedLogOf<typeof adrKind>),\n });\n },\n };\n}\n\n/**\n * The inputs outside the decision events that adr's checks read: both candidate config paths (so\n * adding a config counts) and the log's templates. A staged change to one widens `verify --staged`\n * to every decision, because a policy, scope, severity, index or template change can make an\n * unchanged decision fail.\n */\nexport function decisionVerifyInputs(log: DecisionLog): string[] {\n const root = log.layout.root;\n return [\"adr.toml\", `${root}/adr.toml`, `${root}/templates/`];\n}\n\n/**\n * Verifies the decision log in `mode` (staged, range or full) with `adrChecks`, reading the\n * sources from the local cache only.\n */\nexport async function verifyDecisions(log: DecisionLog, mode: VerifyMode): Promise<Finding[]> {\n return verifyLog({\n git: log.git,\n kind: adrKind,\n layout: log.layout,\n mode,\n checks: adrChecks(\n log,\n await templateHistoryRef(log.git, mode),\n await readDecisionSources(log, { fetch: \"cache\" }),\n ),\n widenOn: decisionVerifyInputs(log),\n });\n}\n","import { access, constants } from \"node:fs/promises\";\nimport { relative, resolve, sep } from \"node:path\";\nimport {\n assertNoHookConflicts,\n classifyPath,\n type EventRef,\n eventTrailers,\n exitCode,\n type Finding,\n type Git,\n type HookInstallResult,\n type HookManager,\n type HookName,\n holdsText,\n hookSnippet,\n installHooks,\n parseEventFileName,\n resolveAttribution,\n type Severity,\n stagedRenames,\n UsageError,\n uninstallHooks,\n writeIndexFile,\n} from \"@nikovirtala/ledger\";\nimport { adrKind } from \"./kind.js\";\nimport type { DecisionLog } from \"./log.js\";\nimport { indexPath, renderDecisionIndex } from \"./log-index.js\";\nimport { verifyDecisions } from \"./verify.js\";\nimport { followScopeRenames } from \"./write.js\";\n\n/** The hooks adr registers, in the order the text output lists them. */\nconst HOOKS: readonly HookName[] = [\"pre-commit\", \"commit-msg\", \"pre-push\", \"post-merge\"];\n\n/** `adr hooks … --json` (design 2b §4.3); `hooks run` prints `ledger-findings/v1` instead. */\nexport interface HooksResultJson {\n readonly format: \"adr-hooks/v1\";\n readonly action: \"install\" | \"uninstall\" | \"print\" | \"trailers\";\n /** Repo-relative shim paths written or removed. */\n readonly written: readonly string[];\n readonly unchanged: readonly string[];\n readonly registrations: readonly { readonly hook: HookName; readonly argv: readonly string[] }[];\n /** `print` only. */\n readonly snippet: string | null;\n /** `trailers` only: the trailers added, in order. */\n readonly trailers: readonly (readonly [string, string])[];\n}\n\n/**\n * adr's command per hook (design 2b D4), after `adr`. `--root <root>` comes first only when the\n * root can't be found without it: no `<repo>/adr.toml` names it and it isn't `docs/adr`. So the\n * hooks see the same config as a plain `adr` run, and never print the root-override note.\n */\nexport function decisionHookCommands(\n log: DecisionLog,\n): Partial<Record<HookName, readonly string[]>> {\n const r =\n log.configPath === \"adr.toml\" || log.layout.root === adrKind.def.defaultRoot\n ? []\n : [\"--root\", log.layout.root];\n return {\n \"pre-commit\": [...r, \"hooks\", \"run\", \"pre-commit\"],\n \"commit-msg\": [...r, \"hooks\", \"trailers\", \"{args}\"],\n \"pre-push\": [...r, \"verify\", \"--base\", \"@{upstream}\", \"--ref\", \"HEAD\", \"--fetch-sources\"],\n \"post-merge\": [...r, \"list\", \"--since\", \"ORIG_HEAD\"],\n };\n}\n\nfunction registrations(log: DecisionLog): HooksResultJson[\"registrations\"] {\n const commands = decisionHookCommands(log);\n return HOOKS.flatMap((hook) => {\n const argv = commands[hook];\n return argv === undefined ? [] : [{ hook, argv }];\n });\n}\n\n/** A shim path as the user sees it: repo-relative, with `/`. */\nfunction shown(git: Git, path: string): string {\n return relative(git.root ?? git.gitDir, path)\n .split(sep)\n .join(\"/\");\n}\n\n/** The shims in hook order: `HOOKS`' order, as the text output lists them. */\nfunction inHookOrder(git: Git, paths: readonly string[]): string[] {\n const rank = (p: string) => HOOKS.findIndex((h) => p.endsWith(`/${h}`));\n return paths.map((p) => shown(git, p)).sort((a, b) => rank(a) - rank(b));\n}\n\nfunction result(\n log: DecisionLog,\n action: HooksResultJson[\"action\"],\n r: HookInstallResult | null,\n extra: Partial<Pick<HooksResultJson, \"snippet\" | \"trailers\">> = {},\n): HooksResultJson {\n return {\n format: \"adr-hooks/v1\",\n action,\n written: r === null ? [] : inHookOrder(log.git, r.written),\n unchanged: r === null ? [] : inHookOrder(log.git, r.unchanged),\n registrations: registrations(log),\n snippet: extra.snippet ?? null,\n trailers: extra.trailers ?? [],\n };\n}\n\n/**\n * Registers adr's four commands in ledger's shims, one per hook and shared with other ark tools\n * (design 2b D4). With `core.hooksPath` set or a foreign hook file present, it writes the rest and\n * then throws `HookConflictError` (exit 2), naming each conflict and the `print` command.\n */\nexport async function installDecisionHooks(log: DecisionLog): Promise<HooksResultJson> {\n const r = await installHooks(log.git, { tool: \"adr\", commands: decisionHookCommands(log) });\n assertNoHookConflicts(r, { tool: \"adr\" });\n return result(log, \"install\", r);\n}\n\n/** Removes adr from every shim; a shim left with no tool is deleted. */\nexport async function uninstallDecisionHooks(log: DecisionLog): Promise<HooksResultJson> {\n return result(log, \"uninstall\", await uninstallHooks(log.git, { tool: \"adr\" }));\n}\n\n/** The config to paste into a hook manager instead of installing shims. */\nexport function printDecisionHooks(log: DecisionLog, manager: HookManager): HooksResultJson {\n const regs = registrations(log).map((r) => ({ tool: \"adr\", ...r }));\n return result(log, \"print\", null, { snippet: hookSnippet(manager, regs) });\n}\n\n/** The `ADR-Event` trailer key as git compares keys: case-insensitively. */\nconst ADR_EVENT = /^adr-event[ \\t]*:/im;\n\n/**\n * The `commit-msg` step (P1 §7.1): appends one `ADR`/`ADR-Event` pair per decision and event type\n * among the staged added event files of the log (`eventTrailers`, ?6), in path order, keeping\n * pairs another tool already added (`ifExists: \"add\"`). It does nothing for a message that\n * already has an `ADR-Event:` trailer (an `adr … --commit` message), or during a merge, whose\n * events came with their own commits' trailers. Throws `UsageError` when the file is missing.\n */\nexport async function addHookTrailers(\n log: DecisionLog,\n messageFile: string,\n): Promise<HooksResultJson> {\n const { git, layout } = log;\n const file = resolve(git.root ?? git.gitDir, messageFile);\n try {\n await access(file, constants.R_OK | constants.W_OK);\n } catch {\n throw new UsageError(`no commit message file at ${messageFile}`);\n }\n const parsed = await git.run([\"interpret-trailers\", \"--parse\", \"--\", file]);\n if (ADR_EVENT.test(Buffer.from(parsed.stdout).toString(\"utf8\"))) {\n return result(log, \"trailers\", null);\n }\n if ((await git.resolveCommit(\"MERGE_HEAD\")) !== null) return result(log, \"trailers\", null);\n const added = await git.stagedPaths([`${layout.root}/${layout.events}`], { addedOnly: true });\n const refs = added.flatMap((p): EventRef[] => {\n const c = classifyPath(layout, p);\n const name = c.area === \"event\" && !c.file.includes(\"/\") ? parseEventFileName(c.file) : null;\n return c.area === \"event\" && c.subject !== null && name !== null\n ? [{ subject: c.subject, type: name.type }]\n : [];\n });\n const trailers = eventTrailers(refs, { subject: \"ADR\", event: \"ADR-Event\" });\n await git.appendTrailers(file, trailers, { ifExists: \"add\" });\n return result(log, \"trailers\", null, { trailers });\n}\n\n/**\n * Whether the hook may stage: only into the commit's real index (design 2b D8). `git commit\n * <paths>` and `--only`, adr's own `--commit` included, run hooks on a temporary index, and\n * staging there leaves the user's index showing the file as deleted.\n */\nfunction stagingSafe(git: Git, env: NodeJS.ProcessEnv): boolean {\n const file = env.GIT_INDEX_FILE;\n if (file === undefined || file === \"\") return true;\n const at = resolve(git.root ?? git.gitDir, file);\n return at === resolve(git.gitDir, \"index\") || at === resolve(git.gitDir, \"index.lock\");\n}\n\n/** `ADR-0003, ADR-0004` for the decisions a rename following changes. */\nconst displays = (views: readonly { readonly display: string }[]) =>\n views.map((v) => v.display).join(\", \");\n\n/**\n * `adr hooks run pre-commit` (design 2b D8): rename following, then the index, then `verify\n * --staged`, stopping at the first step that fails. `worktree` must be opened with the work-tree\n * reader, because rename following writes through `write()`; `index` with the index reader, for\n * the config as staged, the index render and verify. Only the index step stages `index.md`, and\n * only with `[hooks] index = \"stage\"`; rename following passes `index: \"skip\"`. Neither stages\n * into a temporary index (`stagingSafe`). A step that can't run throws (exit 2).\n */\nexport async function runPreCommitHook(o: {\n readonly worktree: DecisionLog;\n readonly index: DecisionLog;\n readonly env: NodeJS.ProcessEnv;\n}): Promise<{\n readonly exit: 0 | 1;\n readonly findings: readonly Finding[];\n readonly notes: readonly string[];\n readonly failOn: Severity;\n}> {\n const { worktree, index, env } = o;\n const config = index.adr;\n const failOn = config.failOn;\n const notes: string[] = [...index.notes];\n const done = (exit: 0 | 1, findings: readonly Finding[]) => ({\n exit,\n findings,\n notes: [...new Set(notes)],\n failOn,\n });\n const safe = stagingSafe(index.git, env);\n\n // 1. Renames, on the work-tree log.\n if (config.scopeFollowRenames !== \"off\" && (await stagedRenames(worktree.git)).length > 0) {\n const { actor, notes: attribution } = await resolveAttribution(worktree.git, { env });\n if (config.scopeFollowRenames === \"warn\") {\n const dry = await followScopeRenames(worktree, { actor }, { dryRun: true });\n for (const v of dry.affected) {\n notes.push(\n `${v.display}'s globs don't follow the renames in this commit (scope_follow_renames = \"warn\"); run adr scope --follow-renames`,\n );\n }\n } else if (safe) {\n const doc = await followScopeRenames(\n worktree,\n { actor },\n { index: \"skip\", notes: attribution },\n );\n notes.push(...doc.notes);\n if (!doc.ok) return done(1, doc.findings);\n } else {\n const dry = await followScopeRenames(worktree, { actor }, { dryRun: true });\n if (dry.affected.length > 0) {\n notes.push(\n `the renames in this commit change the scope of ${displays(dry.affected)}; git commit <paths> can't take the scoped events. Run adr scope --follow-renames, then commit again including the files it lists, or set [hooks] scope_follow_renames = \"warn\"`,\n );\n return done(1, []);\n }\n }\n }\n\n // 2. The index, on the index log: regenerated only with [hooks] index = \"stage\".\n if (config.index === \"committed\" && config.hookIndex === \"stage\" && safe) {\n const path = indexPath(index.layout);\n const text = await renderDecisionIndex(index.git, {\n layout: index.layout,\n adr: config,\n tree: index.tree,\n history: await index.git.resolveCommit(\"HEAD\"),\n });\n if (text !== null && !holdsText(await index.tree.read(path), text)) {\n await writeIndexFile(index.git, path, text);\n await index.git.add([path]);\n notes.push(`regenerated and staged ${path}`);\n }\n }\n\n // 3. verify --staged, which also reports a stale index.md in check mode.\n const findings = await verifyDecisions(index, { type: \"staged\" });\n return done(exitCode(findings, { failOn }), findings);\n}\n","/**\n * The agent skill `adr init --skill` writes (01, \"Agent skill (SKILL.md)\"): the judgment of when\n * to use adr's MCP tools. `skill.test.ts` keeps it equal to the block in docs/01-adr-v1.md.\n */\nexport const SKILL_MD = `---\nname: adr\ndescription: Use before changing architecture, data stores, integration patterns, dependencies or cross-cutting conventions, and whenever asked to record, propose or look up an architectural decision.\n---\n\n# Architectural decisions (adr)\n\nDecisions in this repo are managed by adr. Decision files do not\nshow their own status, so never judge from the raw files whether a\ndecision applies.\n\n## Before changing architecture\n\n1. Call adr_for_path for the files you will touch, and adr_search\n for the area you are about to change. adr_search returns active\n decisions only; adr_for_path also lists inactive ones, each with\n a warning.\n2. If your change conflicts with an accepted decision, stop and tell\n the user which ADR it conflicts with. Do not work around it silently.\n3. If the user still wants the change, propose a superseding ADR.\n\n## Reading decisions\n\n- Use adr_get or adr://decision/{id}, never docs/adr/decisions/*.md.\n- If a response says SUPERSEDED or DEPRECATED, follow it to the\n effective decision before acting.\n- If it says REJECTED or WITHDRAWN, the decision was never in force;\n do not apply it.\n\n## Proposing decisions\n\n1. Call adr_templates and fill every required section.\n2. Use only facts from the conversation and the code. Mark unknowns\n as open questions instead of inventing them.\n3. List at least two options considered when the template has that section.\n4. Set supersedes when the proposal replaces an existing decision.\n5. Do not accept your own proposal. Tell the user the ADR id and\n that they can review it and run: adr accept <id>\n6. If the user decides against your proposal, withdraw it with\n adr_withdraw and give their reason. Don't withdraw other\n people's proposals; tell the user instead.\n\n## Changing scope\n\n- When you move or rename code covered by a decision, let the\n pre-commit hook update its globs (adr hooks install). For your\n own proposals, you can use adr_scope. Rescoping an accepted\n decision needs mcp_can_decide; otherwise tell the user and\n suggest adr scope <id> ….\n- Before any scope change, read the decision's Scope section. If it\n would no longer read true after the change, do not rescope: tell\n the user the decision itself is changing and propose a\n superseding ADR.\n\n## Never\n\n- Edit an accepted decision body, any file under docs/adr/events/,\n or index.md by hand.\n- Use adr_note to change the meaning of a decision. Notes clarify;\n changed decisions need a superseding ADR.\n\n## Without the MCP server\n\nUse the CLI with --json: adr search, adr for, adr show, adr new,\nadr note. Set ARK_AGENT=<your name> and ARK_ON_BEHALF_OF=<the user's\nemail> so your writes are attributed.\n`;\n","import { rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n ConfigError,\n commitWithRecovery,\n firstLine,\n headTree,\n InvalidArgumentError,\n overlayTree,\n readToolConfig,\n renderCommitMessage,\n replaceFile,\n UsageError,\n worktreeReader,\n writeIndexCopy,\n writeNewFile,\n} from \"@nikovirtala/ledger\";\nimport { adrKind } from \"./kind.js\";\nimport type { DecisionLog } from \"./log.js\";\nimport { INDEX_AFTER, indexPath, renderDecisionIndex } from \"./log-index.js\";\nimport { SKILL_MD } from \"./skill.js\";\nimport { BUILTIN_TEMPLATES } from \"./templates/builtin.js\";\n\n/** `adr init --json` (design §6.2). */\nexport interface InitResultJson {\n readonly format: \"adr-init/v1\";\n readonly root: string;\n /** The config file of the log after init, repo-relative; `null` if there is none. */\n readonly config: string | null;\n /** Repo-relative paths written, in write order. */\n readonly written: readonly string[];\n /** Files that were already there and were left as they are. */\n readonly kept: readonly string[];\n /** With `--skill`, the SKILL.md path (written or kept); else `null`. */\n readonly skill: string | null;\n readonly commit: string | null;\n readonly notes: readonly string[];\n}\n\n/**\n * What the recovery hint says about rerunning `adr init` after `git commit` failed (design 2b D5):\n * init keeps existing files, so it would commit nothing.\n */\nexport const INIT_RERUN =\n \"Running adr init again won't commit them: the files exist now, so it keeps them and commits nothing.\";\n\nconst CONFIG = \"adr.toml\";\nconst DEFAULT_ROOT = adrKind.def.defaultRoot;\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\nconst AGENTS = \"AGENTS.md\";\n\n/** The line `adr init --skill` adds to AGENTS.md for tools that don't load skills. */\nexport const agentsPointer = (skill: string): string =>\n `Architecture decisions in this repository are managed by adr: read ${skill} before changing architecture, and follow it.`;\n\n/** `text` with `line` appended after one blank line, ending with a newline. */\nfunction appendLine(text: string, line: string): string {\n if (text === \"\") return `${line}\\n`;\n const ended = text.endsWith(\"\\n\") ? text : `${text}\\n`;\n return `${ended.endsWith(\"\\n\\n\") ? ended : `${ended}\\n`}${line}\\n`;\n}\n\n/** The starter config; `root` only for a repo-level file naming a log elsewhere. */\nfunction configText(o: { readonly root: string | null; readonly template: string }): string {\n return [\n \"# adr configuration. Every key is optional: https://github.com/nikovirtala/ark/tree/main/packages/adr#config-adrtoml\",\n \"[log]\",\n ...(o.root === null ? [] : [`root = ${JSON.stringify(o.root)}`]),\n `default_template = ${JSON.stringify(o.template)}`,\n \"\",\n \"[commit]\",\n '# subject = \"docs(adr): {action} {title}\"',\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * `adr init` (design §6.2): writes a config for the log, unless one is found, and each built-in\n * template in `o.templates` (default `nygard`) to `<root>/templates/`, never overwriting a file.\n * A `--root` other than `docs/adr` gets a repo-level `adr.toml` naming it, so later commands find\n * the log without `--root`; when that file exists for another log, the config goes in the root.\n * With `[log] index = \"committed\"` it also writes `<root>/index.md` unless one exists. With\n * `commit` (default `[policy] auto_commit`) the written files are committed on their own;\n * nothing written means nothing committed. No events are written. With `o.skill` it also writes\n * the agent skill, unless a SKILL.md is there, and adds the pointer line to `AGENTS.md`, unless a\n * line already names the skill; an AGENTS.md with unstaged changes or not tracked is refused\n * with a commit and left unstaged without one.\n */\nexport async function initDecisionLog(\n log: DecisionLog,\n o: {\n readonly templates?: readonly string[];\n readonly commit?: boolean;\n readonly message?: string | null;\n /**\n * Also write `<dir>/SKILL.md` (`dir` repo-relative, `\"\"` for the root) and the AGENTS.md\n * pointer (design §9); `version` is adr's, for the note on a SKILL.md that differs.\n */\n readonly skill?: { readonly dir: string; readonly version: string };\n } = {},\n): Promise<InitResultJson> {\n if (log.ref !== null || log.tree.label !== \"worktree\") {\n throw new InvalidArgumentError(\"init writes the work tree; open the log without a ref\");\n }\n const known = Object.keys(BUILTIN_TEMPLATES);\n const templates = [...new Set(o.templates === undefined ? [\"nygard\"] : o.templates)];\n for (const name of templates) {\n if (!known.includes(name)) {\n throw new UsageError(`unknown built-in template ${name}; use nygard, madr or y-statement`);\n }\n }\n const root = log.layout.root;\n const notes = [...log.notes];\n const files: [string, string][] = [];\n const kept: string[] = [];\n let config = log.configPath;\n if (config !== null) {\n kept.push(config);\n } else {\n const template = templates[0] ?? \"nygard\";\n const repo = await readToolConfig(log.tree, CONFIG);\n if (root === DEFAULT_ROOT) {\n config = `${root}/${CONFIG}`;\n files.push([config, configText({ root: null, template })]);\n } else if (repo === null) {\n config = CONFIG;\n files.push([config, configText({ root, template })]);\n } else {\n // The repo file names another log (openToolLog ignored it with a note); leave it alone.\n const section = repo.raw.log as { root?: unknown } | undefined;\n const other = typeof section?.root === \"string\" ? section.root : DEFAULT_ROOT;\n config = `${root}/${CONFIG}`;\n files.push([config, configText({ root: null, template })]);\n notes.push(`${CONFIG} names ${other}; pass --root ${root} to use this log`);\n }\n }\n for (const name of templates) {\n const path = `${root}/templates/${name}.md`;\n if ((await log.tree.read(path)) !== null) kept.push(path);\n else files.push([path, BUILTIN_TEMPLATES[name] ?? \"\"]);\n }\n const commitRequested = o.commit ?? log.adr.autoCommit;\n const logFiles = files.length;\n\n // The skill (design §9.1): SKILL.md is never overwritten; AGENTS.md gets one pointer line.\n // Checked before anything is written.\n const skill =\n o.skill === undefined ? null : o.skill.dir === \"\" ? \"SKILL.md\" : `${o.skill.dir}/SKILL.md`;\n let agents: {\n readonly bytes: Uint8Array;\n readonly expected: Uint8Array | null;\n readonly stage: boolean;\n } | null = null;\n if (skill !== null) {\n const current = await log.tree.read(skill);\n if (current === null) {\n files.push([skill, SKILL_MD]);\n } else {\n kept.push(skill);\n if (decoder.decode(current) !== SKILL_MD) {\n notes.push(\n `${skill} differs from adr ${o.skill?.version ?? \"\"}'s skill; delete it and run adr init --skill again to replace it`,\n );\n }\n }\n const old = await log.tree.read(AGENTS);\n const text = old === null ? null : decoder.decode(old);\n if ((text ?? \"\").split(\"\\n\").some((l) => l.includes(skill))) {\n kept.push(AGENTS);\n } else {\n let stage = true;\n if (old !== null) {\n const unstaged = (await log.git.unstagedPaths([AGENTS])).length > 0;\n const untracked = (await log.git.untrackedPaths([AGENTS])).length > 0;\n if (commitRequested && unstaged) {\n throw new UsageError(\n `${AGENTS} has unstaged changes; stage or discard them, or run without --commit`,\n );\n }\n if (commitRequested && untracked) {\n throw new UsageError(`${AGENTS} is not tracked; git add it, or run without --commit`);\n }\n if (unstaged || untracked) {\n stage = false;\n notes.push(\n untracked\n ? `${AGENTS} is not tracked, so the pointer is added but not staged; review it and git add it`\n : `${AGENTS} had unstaged changes, so the pointer is added but not staged; review it and git add it`,\n );\n }\n }\n agents = {\n bytes: encoder.encode(appendLine(text ?? \"\", agentsPointer(skill))),\n expected: old,\n stage,\n };\n }\n }\n const at = indexPath(log.layout);\n let indexed = false;\n if (log.adr.index === \"committed\") {\n // Rendered before anything is written, from the tree the commit (or the work tree) will hold.\n if ((await log.tree.read(at)) !== null) {\n kept.push(at);\n } else {\n const head = await log.git.resolveCommit(\"HEAD\");\n const added = Object.fromEntries(files.map(([p, t]) => [p, encoder.encode(t)]));\n const base = commitRequested ? await headTree(log.git, head) : log.tree;\n const text = await renderDecisionIndex(log.git, {\n layout: log.layout,\n adr: log.adr,\n tree: overlayTree(base, added),\n history: head,\n });\n if (text === null) {\n notes.push(\n `${at} not updated: numbers need history in this shallow clone; run git fetch --unshallow, then adr index`,\n );\n } else {\n files.push([at, text]);\n indexed = true;\n }\n }\n }\n\n const commit = (files.length > 0 || agents !== null) && commitRequested;\n if (\n commit &&\n ((await log.git.identity()) === null || (await log.git.committerIdentity()) === null)\n ) {\n throw new ConfigError(\"--commit needs a git identity; set git user.name and user.email\");\n }\n const written: string[] = [];\n let replaced = false;\n try {\n for (const [path, text] of files) {\n await writeNewFile(log.git, path, encoder.encode(text));\n written.push(path);\n }\n if (agents !== null) {\n if (agents.expected === null) await writeNewFile(log.git, AGENTS, agents.bytes);\n else await replaceFile(log.git, AGENTS, agents.bytes, { expected: agents.expected });\n replaced = agents.expected !== null;\n written.push(AGENTS);\n }\n } catch (err) {\n const top = log.git.root ?? \"\";\n const restore = replaced ? (agents?.expected ?? null) : null;\n for (const path of written) {\n if (path === AGENTS && restore !== null && agents !== null) {\n await replaceFile(log.git, AGENTS, restore, { expected: agents.bytes }).catch(() => {});\n } else {\n await rm(join(top, path), { force: true });\n }\n }\n throw err;\n }\n const staged = written.filter((p) => p !== AGENTS || agents?.stage === true);\n if (staged.length > 0) await log.git.add(staged);\n let sha: string | null = null;\n if (commit) {\n const message =\n o.message ??\n renderCommitMessage(log.adr.commitSubject, {\n title: logFiles > 0 ? \"the decision log\" : \"the adr agent skill\",\n vars: { action: \"set up\", id: \"\", display: \"\", command: \"init\" },\n }).message;\n sha = await commitWithRecovery(log.git, {\n message,\n only: staged,\n rerun: INIT_RERUN,\n after: indexed ? INDEX_AFTER(root) : null,\n });\n if (indexed) {\n // The commit's index leaves out uncommitted log changes; the work tree and the index get\n // them back (design 2b §4.5 step 5).\n try {\n const again = await renderDecisionIndex(log.git, {\n layout: log.layout,\n adr: log.adr,\n tree: worktreeReader(log.git),\n history: sha,\n });\n if (again !== null) {\n const r = await writeIndexCopy(log.git, at, again);\n if (r.stage) await log.git.add([at]);\n }\n } catch (err) {\n notes.push(`${at} may be stale: ${firstLine(err)}; run adr index`);\n }\n }\n }\n return { format: \"adr-init/v1\", root, config, written, kept, skill, commit: sha, notes };\n}\n"],"mappings":"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"}
@@ -145,23 +145,52 @@ function invariants(subject, options) {
145
145
  event: event.id
146
146
  }] : []);
147
147
  }
148
- /**
149
- * Qualified ids (`<source>:<id>`) name decisions in other logs. Phase 1 checks local references
150
- * only and skips qualified ones, which only hand-written events can hold; CI checks them at the
151
- * pinned ref in phase 2 (design §E).
152
- */
148
+ /** Qualified ids (`<source>:<id>`) name decisions in other logs. */
153
149
  const isQualified = (ref) => ref.includes(":");
154
150
  /**
155
151
  * Cross-subject rules: `decision/reference-missing` for a local `supersedes`, `superseded.by` or
156
152
  * `linked.to` naming no subject in the log; `decision/superseded-by-unaccepted` when the target of
157
153
  * an applied `superseded.by` has no applied `accepted` event in this tree; and
158
- * `decision/supersession-cycle`, once per cycle on its smallest id.
154
+ * `decision/supersession-cycle`, once per cycle on its smallest id. With `options.sources`, a
155
+ * qualified reference naming a source that isn't configured is `decision/unknown-source`, and one
156
+ * into a cached source that doesn't hold the id is `decision/reference-missing`; references into
157
+ * an uncached source aren't checked.
159
158
  */
160
- function relate(log) {
159
+ function relate(log, options) {
161
160
  const out = [];
161
+ const sources = options?.sources ?? null;
162
162
  const isAccepted = (id) => log.subjects.get(id)?.applied.some((a) => a.event.type === "accepted") ?? false;
163
+ const foreign = (subject, event, ref, what) => {
164
+ if (sources === null) return;
165
+ const at = ref.indexOf(":");
166
+ const name = ref.slice(0, at);
167
+ const id = ref.slice(at + 1);
168
+ const source = sources.get(name);
169
+ if (source === void 0) {
170
+ out.push({
171
+ severity: "error",
172
+ rule: "decision/unknown-source",
173
+ message: `${what} ${ref}, whose source ${name} isn't in [[sources]]`,
174
+ subject,
175
+ event
176
+ });
177
+ return;
178
+ }
179
+ if (source.subjects === null || source.subjects.has(id)) return;
180
+ out.push({
181
+ severity: "error",
182
+ rule: "decision/reference-missing",
183
+ message: `${what} ${ref}, which is not in source ${name} at ${(source.commit ?? "").slice(0, 12)}`,
184
+ subject,
185
+ event
186
+ });
187
+ };
163
188
  const missing = (subject, event, ref, what) => {
164
- if (isQualified(ref) || log.subjects.has(ref)) return false;
189
+ if (isQualified(ref)) {
190
+ foreign(subject, event, ref, what);
191
+ return false;
192
+ }
193
+ if (log.subjects.has(ref)) return false;
165
194
  out.push({
166
195
  severity: "error",
167
196
  rule: "decision/reference-missing",
@@ -402,10 +431,14 @@ const adrKind = defineKind({
402
431
  },
403
432
  scope: {
404
433
  event: "scoped",
405
- activeIn: ["proposed", "accepted"]
434
+ activeIn: ["proposed", "accepted"],
435
+ requireMatchIn: ["accepted"]
406
436
  },
407
- options: Object.freeze({ acceptRequiresHuman: true })
437
+ options: Object.freeze({
438
+ acceptRequiresHuman: true,
439
+ sources: null
440
+ })
408
441
  });
409
442
  export { parseDecisionNumber as a, formatDecisionNumber as i, DECISION_STATUSES as n, REF as r, adrKind as t };
410
443
 
411
- //# sourceMappingURL=kind-DjvA3dyA.js.map
444
+ //# sourceMappingURL=kind-C3DiBuUp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kind-C3DiBuUp.js","names":[],"sources":["../src/kind/project.ts","../src/kind/reduce.ts","../src/kind/rules.ts","../src/kind/schemas.ts","../src/kind.ts"],"sourcesContent":["import type { FoldedSubject, ProjectContext } from \"@nikovirtala/ledger\";\nimport type { DecisionState } from \"./reduce.js\";\nimport type { DecisionEvents, DecisionStatus } from \"./schemas.js\";\n\n/** One decision as the CLI, the library and MCP show it (01:173–192, plus the added fields). */\nexport interface DecisionView {\n readonly id: string;\n readonly number: number | null;\n /** `ADR-0007`, or the id when unnumbered. */\n readonly display: string;\n readonly title: string;\n readonly status: DecisionStatus;\n readonly superseded_by: string | null;\n /** Ids whose applied `superseded.by` is this id, sorted. */\n readonly supersedes: readonly string[];\n /** The proposal's intent (01:164). */\n readonly declared_supersedes: readonly string[];\n /** Head of the `superseded_by` chain; `null` only on a cycle. */\n readonly effective: string | null;\n /** `[id, …, effective]`, following `superseded_by` forward. */\n readonly chain: readonly string[];\n readonly template: { readonly name: string; readonly version: number };\n readonly body_path: string | null;\n /** Recorded at acceptance. */\n readonly body_sha256: string | null;\n /** `null` until accepted; whether the current body hash equals the recorded one. */\n readonly body_intact: boolean | null;\n readonly links: readonly { readonly to: string; readonly rel: string }[];\n readonly applies_to: readonly string[];\n readonly notes: number;\n readonly proposed_at: string;\n readonly decided_at: string | null;\n readonly status_at: string;\n /** `proposed 2026-03-12`: state-changing events, in fold order. */\n readonly history: readonly string[];\n}\n\ntype Subject = FoldedSubject<DecisionStatus, DecisionState, DecisionEvents>;\ntype Context = ProjectContext<DecisionStatus, DecisionState, DecisionEvents>;\n\nconst NUMBER = /^(?:adr-?)?0*([1-9][0-9]{0,8})$/i;\n\n/** `ADR-0007`: at least 4 digits, growing past 9999. */\nexport function formatDecisionNumber(n: number): string {\n return `ADR-${String(n).padStart(4, \"0\")}`;\n}\n\n/** `7`, `0007`, `ADR-7`, `adr-0007` → 7; `null` for anything else. */\nexport function parseDecisionNumber(s: string): number | null {\n const m = NUMBER.exec(s.trim());\n return m === null ? null : Number(m[1]);\n}\n\n/**\n * Follows `superseded_by` from `id`. A visited set stops on a cycle (`effective: null`); a\n * reference to a subject that isn't folded ends the chain there.\n */\nfunction chainOf(id: string, ctx: Context): { chain: string[]; effective: string | null } {\n const chain = [id];\n let cur = id;\n for (;;) {\n const next = ctx.log.subjects.get(cur)?.state?.supersededBy ?? null;\n if (next === null) return { chain, effective: cur };\n if (chain.includes(next)) return { chain, effective: null };\n chain.push(next);\n cur = next;\n }\n}\n\n/** Pure: the view of one folded decision. */\nexport function project(subject: Subject, ctx: Context): DecisionView {\n const { id } = subject;\n const s = subject.state;\n if (s === null || subject.status === null) {\n throw new Error(`decision ${id} is not folded`);\n }\n const supersedes = [...ctx.log.subjects.values()]\n .filter((o) => o.id !== id && o.state?.supersededBy === id)\n .map((o) => o.id)\n .sort();\n const { chain, effective } = chainOf(id, ctx);\n const current = ctx.bodySha256(id);\n return {\n id,\n number: ctx.number(id),\n display: ctx.display(id),\n title: s.title,\n status: subject.status,\n superseded_by: s.supersededBy,\n supersedes,\n declared_supersedes: s.declaredSupersedes,\n effective,\n chain,\n template: s.template,\n body_path: subject.loaded.body?.path ?? subject.loaded.bodyPaths[0] ?? null,\n body_sha256: s.bodySha256,\n body_intact: s.bodySha256 === null ? null : current === s.bodySha256,\n links: s.links,\n applies_to: s.appliesTo,\n notes: s.notes,\n proposed_at: s.proposedAt,\n decided_at: s.decidedAt,\n status_at: s.statusAt,\n history: s.history.map((h) => `${h.type} ${h.at.slice(0, 10)}`),\n };\n}\n","import type { Actor, FoldEvent } from \"@nikovirtala/ledger\";\nimport type { DecisionEvents } from \"./schemas.js\";\n\n/** A decision's own state, maintained by `reduce` over its applied events. */\nexport interface DecisionState {\n readonly title: string;\n readonly template: { readonly name: string; readonly version: number };\n readonly declaredSupersedes: readonly string[];\n /** From the applied `accepted` event. */\n readonly bodySha256: string | null;\n readonly acceptedBy: Actor | null;\n readonly supersededBy: string | null;\n /** In fold order, duplicates kept once. */\n readonly links: readonly { readonly to: string; readonly rel: string }[];\n /** The latest `scoped` set (latest wins). */\n readonly appliesTo: readonly string[];\n readonly notes: number;\n /** State-changing events only, in fold order. */\n readonly history: readonly { readonly type: string; readonly at: string }[];\n readonly proposedAt: string;\n /** `at` of `accepted`, `rejected` or `withdrawn`. */\n readonly decidedAt: string | null;\n /** `at` of the latest state-changing event. */\n readonly statusAt: string;\n}\n\nconst EMPTY: DecisionState = {\n title: \"\",\n template: { name: \"\", version: 0 },\n declaredSupersedes: [],\n bodySha256: null,\n acceptedBy: null,\n supersededBy: null,\n links: [],\n appliesTo: [],\n notes: 0,\n history: [],\n proposedAt: \"\",\n decidedAt: null,\n statusAt: \"\",\n};\n\n/** Pure: the state after one applied event. The fold only passes events the machine allows. */\nexport function reduce(state: DecisionState | null, e: FoldEvent<DecisionEvents>): DecisionState {\n const s = state ?? EMPTY;\n const changed = (extra: Partial<DecisionState> = {}): DecisionState => ({\n ...s,\n ...extra,\n history: [...s.history, { type: e.type, at: e.at }],\n statusAt: e.at,\n });\n switch (e.type) {\n case \"proposed\":\n return changed({\n title: e.data.title,\n template: { name: e.data.template, version: e.data.template_version },\n declaredSupersedes: [...(e.data.supersedes ?? [])],\n proposedAt: e.at,\n });\n case \"accepted\":\n return changed({ bodySha256: e.data.body_sha256, acceptedBy: e.actor, decidedAt: e.at });\n case \"rejected\":\n case \"withdrawn\":\n return changed({ decidedAt: e.at });\n case \"deprecated\":\n return changed();\n case \"superseded\":\n return changed({ supersededBy: e.data.by });\n case \"noted\":\n return { ...s, notes: s.notes + 1 };\n case \"linked\": {\n const { to, rel } = e.data;\n const known = s.links.some((l) => l.to === to && l.rel === rel);\n return known ? s : { ...s, links: [...s.links, { to, rel }] };\n }\n case \"scoped\":\n return { ...s, appliesTo: [...e.data.set] };\n }\n}\n","import type { FoldedLog, FoldedSubject, KindFinding } from \"@nikovirtala/ledger\";\nimport type { DecisionState } from \"./reduce.js\";\nimport type { DecisionEvents, DecisionStatus } from \"./schemas.js\";\n\n/** A decision in another repository's log, as the reference rules and `--source` listings see it. */\nexport interface ForeignDecision {\n readonly status: DecisionStatus;\n readonly display: string;\n readonly title: string;\n}\n\n/** One `[[sources]]` entry of kind `adr`, as `AdrOptions.sources` holds it. */\nexport interface AdrSource {\n /** The decisions at the cached commit; `null` when the source isn't cached (not checked). */\n readonly subjects: ReadonlyMap<string, ForeignDecision> | null;\n /** The cached commit, or `null`. */\n readonly commit: string | null;\n}\n\n/** Options of the decision kind's `invariants` and `relate`. */\nexport interface AdrOptions {\n /** Default `true`: an agent may propose, but only a human may accept (01:98). */\n readonly acceptRequiresHuman: boolean;\n /**\n * The `kind = \"adr\"` `[[sources]]` by name, for checking qualified references; `null` (the\n * default) skips qualified references, as reads that don't load sources do.\n */\n readonly sources: ReadonlyMap<string, AdrSource> | null;\n}\n\ntype Subject = FoldedSubject<DecisionStatus, DecisionState, DecisionEvents>;\ntype Log = FoldedLog<DecisionStatus, DecisionState, DecisionEvents>;\n\n/** `decision/agent-accepted`: an applied `accepted` event written by an agent under the human policy. */\nexport function invariants(subject: Subject, options: AdrOptions): readonly KindFinding[] {\n if (!options.acceptRequiresHuman) return [];\n return subject.applied.flatMap(({ event }) =>\n event.type === \"accepted\" && event.actor.kind === \"agent\"\n ? [\n {\n severity: \"error\" as const,\n rule: \"decision/agent-accepted\",\n message: `accepted by agent ${event.actor.name}; accept_requires_human needs a human`,\n subject: subject.id,\n event: event.id,\n },\n ]\n : [],\n );\n}\n\n/** Qualified ids (`<source>:<id>`) name decisions in other logs. */\nconst isQualified = (ref: string) => ref.includes(\":\");\n\n/**\n * Cross-subject rules: `decision/reference-missing` for a local `supersedes`, `superseded.by` or\n * `linked.to` naming no subject in the log; `decision/superseded-by-unaccepted` when the target of\n * an applied `superseded.by` has no applied `accepted` event in this tree; and\n * `decision/supersession-cycle`, once per cycle on its smallest id. With `options.sources`, a\n * qualified reference naming a source that isn't configured is `decision/unknown-source`, and one\n * into a cached source that doesn't hold the id is `decision/reference-missing`; references into\n * an uncached source aren't checked.\n */\nexport function relate(log: Log, options?: AdrOptions): readonly KindFinding[] {\n const out: KindFinding[] = [];\n const sources = options?.sources ?? null;\n const isAccepted = (id: string) =>\n log.subjects.get(id)?.applied.some((a) => a.event.type === \"accepted\") ?? false;\n const foreign = (subject: string, event: string, ref: string, what: string) => {\n if (sources === null) return;\n const at = ref.indexOf(\":\");\n const name = ref.slice(0, at);\n const id = ref.slice(at + 1);\n const source = sources.get(name);\n if (source === undefined) {\n out.push({\n severity: \"error\",\n rule: \"decision/unknown-source\",\n message: `${what} ${ref}, whose source ${name} isn't in [[sources]]`,\n subject,\n event,\n });\n return;\n }\n if (source.subjects === null || source.subjects.has(id)) return;\n out.push({\n severity: \"error\",\n rule: \"decision/reference-missing\",\n message: `${what} ${ref}, which is not in source ${name} at ${(source.commit ?? \"\").slice(0, 12)}`,\n subject,\n event,\n });\n };\n const missing = (subject: string, event: string, ref: string, what: string) => {\n if (isQualified(ref)) {\n foreign(subject, event, ref, what);\n return false;\n }\n if (log.subjects.has(ref)) return false;\n out.push({\n severity: \"error\",\n rule: \"decision/reference-missing\",\n message: `${what} ${ref}, which is not in the log`,\n subject,\n event,\n related: [ref],\n });\n return true;\n };\n\n for (const [id, s] of log.subjects) {\n for (const { event } of s.applied) {\n switch (event.type) {\n case \"proposed\":\n for (const ref of event.data.supersedes ?? []) missing(id, event.id, ref, \"supersedes\");\n break;\n case \"superseded\": {\n const by = event.data.by;\n if (missing(id, event.id, by, \"superseded by\") || isQualified(by)) break;\n if (!isAccepted(by)) {\n out.push({\n severity: \"error\",\n rule: \"decision/superseded-by-unaccepted\",\n message: `superseded by ${by}, which is not accepted`,\n subject: id,\n event: event.id,\n related: [by],\n });\n }\n break;\n }\n case \"linked\":\n missing(id, event.id, event.data.to, \"links to\");\n break;\n default:\n break;\n }\n }\n }\n\n const next = (id: string) => log.subjects.get(id)?.state?.supersededBy ?? null;\n const reported = new Set<string>();\n for (const start of log.subjects.keys()) {\n const path: string[] = [];\n let cur: string | null = start;\n while (cur !== null && !path.includes(cur)) {\n path.push(cur);\n cur = next(cur);\n }\n if (cur === null) continue;\n const cycle = path.slice(path.indexOf(cur));\n const smallest = [...cycle].sort()[0];\n if (smallest === undefined || reported.has(smallest)) continue;\n reported.add(smallest);\n const from = cycle.indexOf(smallest);\n const ordered = [...cycle.slice(from), ...cycle.slice(0, from), smallest];\n const event = log.subjects.get(smallest)?.applied.find((a) => a.event.type === \"superseded\")\n ?.event.id;\n out.push({\n severity: \"error\",\n rule: \"decision/supersession-cycle\",\n message: `supersession cycle: ${ordered.join(\" → \")}`,\n subject: smallest,\n ...(event === undefined ? {} : { event }),\n related: [...cycle].sort(),\n });\n }\n return out;\n}\n","import { eventType, setEventType } from \"@nikovirtala/ledger\";\n\n/** A decision's computed status. */\nexport type DecisionStatus =\n | \"proposed\"\n | \"accepted\"\n | \"rejected\"\n | \"withdrawn\"\n | \"deprecated\"\n | \"superseded\";\n\n/** Every status, in lifecycle order. */\nexport const DECISION_STATUSES: readonly DecisionStatus[] = Object.freeze([\n \"proposed\",\n \"accepted\",\n \"rejected\",\n \"withdrawn\",\n \"deprecated\",\n \"superseded\",\n]);\n\n/** A local subject id, or a qualified `<source>:<id>` one. */\nexport const REF = \"^(?:[a-z][a-z0-9-]*:)?[0-9a-hjkmnp-tv-z]{8}$\";\n\nconst ref = { type: \"string\", pattern: REF } as const;\nconst SINGLE_LINE = \"^[^\\\\r\\\\n]*$\";\n\n/** How a `linked` decision relates to its target. */\nexport type LinkRel = \"relates-to\" | \"refines\" | \"depends-on\";\n\n/** The decision kind's event types (01:128–136): data schemas, allowed states and targets. */\nexport const decisionEvents = {\n proposed: eventType<{\n title: string;\n template: string;\n template_version: number;\n supersedes?: string[];\n }>()({\n data: {\n type: \"object\",\n required: [\"title\", \"template\", \"template_version\"],\n properties: {\n title: { type: \"string\", minLength: 1, maxLength: 200, pattern: SINGLE_LINE },\n template: { type: \"string\", pattern: \"^[a-z][a-z0-9-]*$\" },\n template_version: { type: \"integer\", minimum: 1 },\n supersedes: { type: \"array\", items: ref, uniqueItems: true, maxItems: 20 },\n },\n },\n allowedIn: [],\n to: \"proposed\",\n }),\n accepted: eventType<{ body_sha256: string }>()({\n data: {\n type: \"object\",\n required: [\"body_sha256\"],\n properties: { body_sha256: { type: \"string\", pattern: \"^[0-9a-f]{64}$\" } },\n },\n allowedIn: [\"proposed\"],\n to: \"accepted\",\n }),\n rejected: eventType()({\n data: { type: \"object\" },\n allowedIn: [\"proposed\"],\n to: \"rejected\",\n reason: \"required\",\n }),\n withdrawn: eventType()({ data: { type: \"object\" }, allowedIn: [\"proposed\"], to: \"withdrawn\" }),\n deprecated: eventType()({\n data: { type: \"object\" },\n allowedIn: [\"accepted\"],\n to: \"deprecated\",\n reason: \"required\",\n }),\n superseded: eventType<{ by: string }>()({\n data: { type: \"object\", required: [\"by\"], properties: { by: ref } },\n allowedIn: [\"accepted\", \"deprecated\"],\n to: \"superseded\",\n }),\n noted: eventType<{ text: string }>()({\n data: {\n type: \"object\",\n required: [\"text\"],\n properties: { text: { type: \"string\", minLength: 1 } },\n },\n allowedIn: \"any\",\n to: null,\n }),\n linked: eventType<{ to: string; rel: LinkRel }>()({\n data: {\n type: \"object\",\n required: [\"to\", \"rel\"],\n properties: { to: ref, rel: { enum: [\"relates-to\", \"refines\", \"depends-on\"] } },\n },\n allowedIn: \"any\",\n to: null,\n }),\n // ledger's whole-set event (G11): the same schema as before, so decision/v1 is unchanged.\n scoped: setEventType({\n item: { type: \"string\", minLength: 1, maxLength: 500 },\n maxItems: 200,\n allowedIn: [\"proposed\", \"accepted\", \"deprecated\"],\n }),\n} as const;\n\nexport type DecisionEvents = typeof decisionEvents;\n","import { defineKind, type Kind } from \"@nikovirtala/ledger\";\nimport {\n type DecisionView,\n formatDecisionNumber,\n parseDecisionNumber,\n project,\n} from \"./kind/project.js\";\nimport { type DecisionState, reduce } from \"./kind/reduce.js\";\nimport { type AdrOptions, invariants, relate } from \"./kind/rules.js\";\nimport { type DecisionEvents, type DecisionStatus, decisionEvents } from \"./kind/schemas.js\";\n\nexport type { DecisionView } from \"./kind/project.js\";\nexport { formatDecisionNumber, parseDecisionNumber } from \"./kind/project.js\";\nexport type { DecisionState } from \"./kind/reduce.js\";\nexport type { AdrOptions, AdrSource, ForeignDecision } from \"./kind/rules.js\";\nexport {\n DECISION_STATUSES,\n type DecisionEvents,\n type DecisionStatus,\n type LinkRel,\n REF,\n} from \"./kind/schemas.js\";\n\n/**\n * The decision kind (01:128–194), as imported by other tools through `@nikovirtala/adr/kind`.\n *\n * Decisions are proposed, then accepted (which freezes the body and earns a number on main),\n * rejected or withdrawn; accepted ones can be deprecated or superseded. This module imports only\n * `@nikovirtala/ledger` and `./kind/*`, so importing the kind never loads CLI or MCP code.\n */\nexport const adrKind: Kind<\n DecisionStatus,\n DecisionEvents,\n DecisionState,\n DecisionView,\n AdrOptions\n> = defineKind<DecisionStatus, DecisionEvents, DecisionState, DecisionView, AdrOptions>({\n name: \"decision\",\n schema: \"decision/v1\",\n states: [\"proposed\", \"accepted\", \"rejected\", \"withdrawn\", \"deprecated\", \"superseded\"],\n events: decisionEvents,\n initial: \"proposed\",\n reduce,\n invariants,\n relate,\n project,\n body: { freezeOn: \"accepted\" },\n layout: { bodies: \"decisions\", events: \"events\" },\n defaultRoot: \"docs/adr\",\n sourceName: \"adr\",\n numbering: { on: \"accepted\", format: formatDecisionNumber, parse: parseDecisionNumber },\n templates: {\n dir: \"templates\",\n nameField: \"template\",\n versionField: \"template_version\",\n lintIn: [\"proposed\"],\n lintOn: \"accepted\",\n requireSections: [\"scope\"],\n },\n // A deprecated decision's globs no longer have to match code (design 2b §4.8), and a proposed\n // one's may name code that doesn't exist yet: only accepted decisions' globs must match (?7).\n scope: { event: \"scoped\", activeIn: [\"proposed\", \"accepted\"], requireMatchIn: [\"accepted\"] },\n options: Object.freeze({ acceptRequiresHuman: true, sources: null }),\n});\n"],"mappings":"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"}
package/dist/kind.js CHANGED
@@ -1 +1 @@
1
- export { n as DECISION_STATUSES, r as REF, t as adrKind, i as formatDecisionNumber, a as parseDecisionNumber } from "./kind-DjvA3dyA.js";
1
+ export { n as DECISION_STATUSES, r as REF, t as adrKind, i as formatDecisionNumber, a as parseDecisionNumber } from "./kind-C3DiBuUp.js";
@@ -1,5 +1,5 @@
1
- import { v as BUILTIN_TEMPLATES } from "./search-CwIPlNGd.js";
2
- import { t as adrKind } from "./kind-DjvA3dyA.js";
1
+ import { v as BUILTIN_TEMPLATES } from "./search-CTspg2pd.js";
2
+ import { t as adrKind } from "./kind-C3DiBuUp.js";
3
3
  import { lintTemplate, loadTemplates, parseTemplate } from "@nikovirtala/ledger";
4
4
  // src/templates/list.ts
5
5
  const lint = (t) => lintTemplate(t, { requireSections: adrKind.def.templates?.requireSections ?? [] });
@@ -55,4 +55,4 @@ async function listTemplates(read) {
55
55
  }
56
56
  export { listTemplates as t };
57
57
 
58
- //# sourceMappingURL=list-Dt4KGxvj.js.map
58
+ //# sourceMappingURL=list-CJi9vZqJ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"list-Dt4KGxvj.js","names":[],"sources":["../src/templates/list.ts"],"sourcesContent":["import {\n type Finding,\n lintTemplate,\n loadTemplates,\n parseTemplate,\n type Template,\n} from \"@nikovirtala/ledger\";\nimport { adrKind } from \"../kind.js\";\nimport type { DecisionsRead } from \"../log.js\";\nimport { BUILTIN_TEMPLATES } from \"./builtin.js\";\n\n/** `adr_templates` (and phase 4's `adr template list --json`): the templates a proposal can use. */\nexport interface TemplatesJson {\n readonly format: \"adr-templates/v1\";\n readonly root: string;\n /** `[log] default_template`. */\n readonly default: string;\n readonly templates: readonly {\n readonly name: string;\n readonly version: number;\n readonly source: \"repo\" | \"builtin\";\n /** `<root>/templates/<name>.md` for a repo template; `null` for a built-in one. */\n readonly path: string | null;\n readonly sections: readonly {\n readonly id: string;\n readonly heading: string;\n readonly required: boolean;\n readonly guidance: string | null;\n }[];\n }[];\n /** Why a repo template is left out: it doesn't parse or fails the template lint. */\n readonly findings: readonly Finding[];\n readonly notes: readonly string[];\n}\n\nconst lint = (t: Template) =>\n lintTemplate(t, { requireSections: adrKind.def.templates?.requireSections ?? [] });\n\n/**\n * The templates of `<root>/templates` that parse and pass the template lint, and the built-in\n * ones no repo template or file of the same name shadows (`adr new` copies those on first use),\n * sorted by name.\n */\nexport async function listTemplates(read: DecisionsRead): Promise<TemplatesJson> {\n const loaded = await loadTemplates(read.tree, `${read.root}/templates`);\n const findings: Finding[] = [...loaded.findings];\n const listed: { template: Template; source: \"repo\" | \"builtin\" }[] = [];\n for (const t of loaded.templates.values()) {\n const problems = lint(t);\n findings.push(...problems);\n if (problems.length === 0) listed.push({ template: t, source: \"repo\" });\n }\n for (const [name, text] of Object.entries(BUILTIN_TEMPLATES)) {\n // A repo file of that name shadows it even when broken: `adr new` would read the file.\n if (loaded.templates.has(name)) continue;\n if ((await read.tree.read(`${read.root}/templates/${name}.md`)) !== null) continue;\n const parsed = parseTemplate(text);\n if (parsed.ok) listed.push({ template: parsed.template, source: \"builtin\" });\n }\n listed.sort((a, b) => (a.template.name < b.template.name ? -1 : 1));\n return {\n format: \"adr-templates/v1\",\n root: read.root,\n default: read.adr.defaultTemplate,\n templates: listed.map(({ template: t, source }) => ({\n name: t.name,\n version: t.version,\n source,\n path: source === \"repo\" ? t.path : null,\n sections: t.sections.map((s) => ({\n id: s.id,\n heading: s.heading,\n required: s.required,\n guidance: s.guidance,\n })),\n })),\n findings: findings.map((f) => ({ ...f, kind: adrKind.name })),\n notes: read.notes,\n };\n}\n"],"mappings":";;;;AAmCA,MAAM,QAAQ,MACZ,aAAa,GAAG,EAAE,iBAAiB,QAAQ,IAAI,WAAW,mBAAmB,CAAC,EAAE,CAAC;;;;;;AAOnF,eAAsB,cAAc,MAA6C;CAC/E,MAAM,SAAS,MAAM,cAAc,KAAK,MAAM,GAAG,KAAK,KAAK,WAAW;CACtE,MAAM,WAAsB,CAAC,GAAG,OAAO,QAAQ;CAC/C,MAAM,SAA+D,CAAC;CACtE,KAAK,MAAM,KAAK,OAAO,UAAU,OAAO,GAAG;EACzC,MAAM,WAAW,KAAK,CAAC;EACvB,SAAS,KAAK,GAAG,QAAQ;EACzB,IAAI,SAAS,WAAW,GAAG,OAAO,KAAK;GAAE,UAAU;GAAG,QAAQ;EAAO,CAAC;CACxE;CACA,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,iBAAiB,GAAG;EAE5D,IAAI,OAAO,UAAU,IAAI,IAAI,GAAG;EAChC,IAAK,MAAM,KAAK,KAAK,KAAK,GAAG,KAAK,KAAK,aAAa,KAAK,IAAI,MAAO,MAAM;EAC1E,MAAM,SAAS,cAAc,IAAI;EACjC,IAAI,OAAO,IAAI,OAAO,KAAK;GAAE,UAAU,OAAO;GAAU,QAAQ;EAAU,CAAC;CAC7E;CACA,OAAO,MAAM,GAAG,MAAO,EAAE,SAAS,OAAO,EAAE,SAAS,OAAO,KAAK,CAAE;CAClE,OAAO;EACL,QAAQ;EACR,MAAM,KAAK;EACX,SAAS,KAAK,IAAI;EAClB,WAAW,OAAO,KAAK,EAAE,UAAU,GAAG,cAAc;GAClD,MAAM,EAAE;GACR,SAAS,EAAE;GACX;GACA,MAAM,WAAW,SAAS,EAAE,OAAO;GACnC,UAAU,EAAE,SAAS,KAAK,OAAO;IAC/B,IAAI,EAAE;IACN,SAAS,EAAE;IACX,UAAU,EAAE;IACZ,UAAU,EAAE;GACd,EAAE;EACJ,EAAE;EACF,UAAU,SAAS,KAAK,OAAO;GAAE,GAAG;GAAG,MAAM,QAAQ;EAAK,EAAE;EAC5D,OAAO,KAAK;CACd;AACF"}
1
+ {"version":3,"file":"list-CJi9vZqJ.js","names":[],"sources":["../src/templates/list.ts"],"sourcesContent":["import {\n type Finding,\n lintTemplate,\n loadTemplates,\n parseTemplate,\n type Template,\n} from \"@nikovirtala/ledger\";\nimport { adrKind } from \"../kind.js\";\nimport type { DecisionsRead } from \"../log.js\";\nimport { BUILTIN_TEMPLATES } from \"./builtin.js\";\n\n/** `adr_templates` (and phase 4's `adr template list --json`): the templates a proposal can use. */\nexport interface TemplatesJson {\n readonly format: \"adr-templates/v1\";\n readonly root: string;\n /** `[log] default_template`. */\n readonly default: string;\n readonly templates: readonly {\n readonly name: string;\n readonly version: number;\n readonly source: \"repo\" | \"builtin\";\n /** `<root>/templates/<name>.md` for a repo template; `null` for a built-in one. */\n readonly path: string | null;\n readonly sections: readonly {\n readonly id: string;\n readonly heading: string;\n readonly required: boolean;\n readonly guidance: string | null;\n }[];\n }[];\n /** Why a repo template is left out: it doesn't parse or fails the template lint. */\n readonly findings: readonly Finding[];\n readonly notes: readonly string[];\n}\n\nconst lint = (t: Template) =>\n lintTemplate(t, { requireSections: adrKind.def.templates?.requireSections ?? [] });\n\n/**\n * The templates of `<root>/templates` that parse and pass the template lint, and the built-in\n * ones no repo template or file of the same name shadows (`adr new` copies those on first use),\n * sorted by name.\n */\nexport async function listTemplates(read: DecisionsRead): Promise<TemplatesJson> {\n const loaded = await loadTemplates(read.tree, `${read.root}/templates`);\n const findings: Finding[] = [...loaded.findings];\n const listed: { template: Template; source: \"repo\" | \"builtin\" }[] = [];\n for (const t of loaded.templates.values()) {\n const problems = lint(t);\n findings.push(...problems);\n if (problems.length === 0) listed.push({ template: t, source: \"repo\" });\n }\n for (const [name, text] of Object.entries(BUILTIN_TEMPLATES)) {\n // A repo file of that name shadows it even when broken: `adr new` would read the file.\n if (loaded.templates.has(name)) continue;\n if ((await read.tree.read(`${read.root}/templates/${name}.md`)) !== null) continue;\n const parsed = parseTemplate(text);\n if (parsed.ok) listed.push({ template: parsed.template, source: \"builtin\" });\n }\n listed.sort((a, b) => (a.template.name < b.template.name ? -1 : 1));\n return {\n format: \"adr-templates/v1\",\n root: read.root,\n default: read.adr.defaultTemplate,\n templates: listed.map(({ template: t, source }) => ({\n name: t.name,\n version: t.version,\n source,\n path: source === \"repo\" ? t.path : null,\n sections: t.sections.map((s) => ({\n id: s.id,\n heading: s.heading,\n required: s.required,\n guidance: s.guidance,\n })),\n })),\n findings: findings.map((f) => ({ ...f, kind: adrKind.name })),\n notes: read.notes,\n };\n}\n"],"mappings":";;;;AAmCA,MAAM,QAAQ,MACZ,aAAa,GAAG,EAAE,iBAAiB,QAAQ,IAAI,WAAW,mBAAmB,CAAC,EAAE,CAAC;;;;;;AAOnF,eAAsB,cAAc,MAA6C;CAC/E,MAAM,SAAS,MAAM,cAAc,KAAK,MAAM,GAAG,KAAK,KAAK,WAAW;CACtE,MAAM,WAAsB,CAAC,GAAG,OAAO,QAAQ;CAC/C,MAAM,SAA+D,CAAC;CACtE,KAAK,MAAM,KAAK,OAAO,UAAU,OAAO,GAAG;EACzC,MAAM,WAAW,KAAK,CAAC;EACvB,SAAS,KAAK,GAAG,QAAQ;EACzB,IAAI,SAAS,WAAW,GAAG,OAAO,KAAK;GAAE,UAAU;GAAG,QAAQ;EAAO,CAAC;CACxE;CACA,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,iBAAiB,GAAG;EAE5D,IAAI,OAAO,UAAU,IAAI,IAAI,GAAG;EAChC,IAAK,MAAM,KAAK,KAAK,KAAK,GAAG,KAAK,KAAK,aAAa,KAAK,IAAI,MAAO,MAAM;EAC1E,MAAM,SAAS,cAAc,IAAI;EACjC,IAAI,OAAO,IAAI,OAAO,KAAK;GAAE,UAAU,OAAO;GAAU,QAAQ;EAAU,CAAC;CAC7E;CACA,OAAO,MAAM,GAAG,MAAO,EAAE,SAAS,OAAO,EAAE,SAAS,OAAO,KAAK,CAAE;CAClE,OAAO;EACL,QAAQ;EACR,MAAM,KAAK;EACX,SAAS,KAAK,IAAI;EAClB,WAAW,OAAO,KAAK,EAAE,UAAU,GAAG,cAAc;GAClD,MAAM,EAAE;GACR,SAAS,EAAE;GACX;GACA,MAAM,WAAW,SAAS,EAAE,OAAO;GACnC,UAAU,EAAE,SAAS,KAAK,OAAO;IAC/B,IAAI,EAAE;IACN,SAAS,EAAE;IACX,UAAU,EAAE;IACZ,UAAU,EAAE;GACd,EAAE;EACJ,EAAE;EACF,UAAU,SAAS,KAAK,OAAO;GAAE,GAAG;GAAG,MAAM,QAAQ;EAAK,EAAE;EAC5D,OAAO,KAAK;CACd;AACF"}