cohorte 3.0.0-dev.5 → 3.0.0-dev.7

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 (95) hide show
  1. package/assets/manifest.json +16 -6
  2. package/assets/prompts/agents/brainstormer.md +3 -0
  3. package/assets/prompts/agents/spec-author.md +3 -0
  4. package/assets/schemas/commands.schema.json +7 -0
  5. package/assets/schemas/config.schema.json +22 -0
  6. package/dist/bundle-manifest.json +72 -48
  7. package/dist/chunks/align-ds-r_X_1aQ_.mjs +93 -0
  8. package/dist/chunks/align-ds-r_X_1aQ_.mjs.map +1 -0
  9. package/dist/chunks/audit-B4ZWQ_DI.mjs +179 -0
  10. package/dist/chunks/audit-B4ZWQ_DI.mjs.map +1 -0
  11. package/dist/chunks/brainstorm-DasVwI1D.mjs +123 -0
  12. package/dist/chunks/brainstorm-DasVwI1D.mjs.map +1 -0
  13. package/dist/chunks/build-crOld8gE.mjs +19 -0
  14. package/dist/chunks/build-crOld8gE.mjs.map +1 -0
  15. package/dist/chunks/{catalogue-B5uFqfzJ.mjs → catalogue-IaCJdfB8.mjs} +4 -4
  16. package/dist/chunks/{catalogue-B5uFqfzJ.mjs.map → catalogue-IaCJdfB8.mjs.map} +1 -1
  17. package/dist/chunks/{compose-CKo_q39e.mjs → compose-DRKjQcn0.mjs} +42 -23
  18. package/dist/chunks/compose-DRKjQcn0.mjs.map +1 -0
  19. package/dist/chunks/{config-DefHA2Lw.mjs → config-Cqu5XQoP.mjs} +2 -2
  20. package/dist/chunks/{config-DefHA2Lw.mjs.map → config-Cqu5XQoP.mjs.map} +1 -1
  21. package/dist/chunks/{contract-BkMjU1kR.mjs → contract-DjssDPpS.mjs} +66 -26
  22. package/dist/chunks/contract-DjssDPpS.mjs.map +1 -0
  23. package/dist/chunks/{diff-D_0hBwHl.mjs → diff-Qw0II0sj.mjs} +2 -2
  24. package/dist/chunks/{diff-D_0hBwHl.mjs.map → diff-Qw0II0sj.mjs.map} +1 -1
  25. package/dist/chunks/{discover-BJMwXyGL.mjs → discover-BZPolSZG.mjs} +2 -2
  26. package/dist/chunks/{discover-BJMwXyGL.mjs.map → discover-BZPolSZG.mjs.map} +1 -1
  27. package/dist/chunks/{doctor-CginW8y8.mjs → doctor-DcDFPvQM.mjs} +6 -6
  28. package/dist/chunks/{doctor-CginW8y8.mjs.map → doctor-DcDFPvQM.mjs.map} +1 -1
  29. package/dist/chunks/fix-B_oGzGVn.mjs +82 -0
  30. package/dist/chunks/fix-B_oGzGVn.mjs.map +1 -0
  31. package/dist/chunks/fleet-yWHQY2ya.mjs +297 -0
  32. package/dist/chunks/fleet-yWHQY2ya.mjs.map +1 -0
  33. package/dist/chunks/{init-CP6puNbE.mjs → init--FxxkrcT.mjs} +2 -2
  34. package/dist/chunks/{init-CP6puNbE.mjs.map → init--FxxkrcT.mjs.map} +1 -1
  35. package/dist/chunks/intake-Dd1KVXnO.mjs +12 -0
  36. package/dist/chunks/intake-Dd1KVXnO.mjs.map +1 -0
  37. package/dist/chunks/loop-BWsUpyus.mjs +212 -0
  38. package/dist/chunks/loop-BWsUpyus.mjs.map +1 -0
  39. package/dist/chunks/{migrate-DybihKzZ.mjs → migrate-DuYCAbE3.mjs} +2 -2
  40. package/dist/chunks/{migrate-DybihKzZ.mjs.map → migrate-DuYCAbE3.mjs.map} +1 -1
  41. package/dist/chunks/obsidian-BHLkXx_H.mjs +235 -0
  42. package/dist/chunks/obsidian-BHLkXx_H.mjs.map +1 -0
  43. package/dist/chunks/patch-sAgVqjpx.mjs +50 -0
  44. package/dist/chunks/patch-sAgVqjpx.mjs.map +1 -0
  45. package/dist/chunks/{policy-Clypo20s.mjs → policy-Du7WVBFX.mjs} +4 -4
  46. package/dist/chunks/{policy-Clypo20s.mjs.map → policy-Du7WVBFX.mjs.map} +1 -1
  47. package/dist/chunks/{reconcile-czNaLOP6.mjs → reconcile-BHn66X3C.mjs} +3 -3
  48. package/dist/chunks/{reconcile-czNaLOP6.mjs.map → reconcile-BHn66X3C.mjs.map} +1 -1
  49. package/dist/chunks/refactor-CoNoqLZr.mjs +153 -0
  50. package/dist/chunks/refactor-CoNoqLZr.mjs.map +1 -0
  51. package/dist/chunks/retro-Dr-2RTNN.mjs +78 -0
  52. package/dist/chunks/retro-Dr-2RTNN.mjs.map +1 -0
  53. package/dist/chunks/review-ZsBqp3vZ.mjs +35 -0
  54. package/dist/chunks/review-ZsBqp3vZ.mjs.map +1 -0
  55. package/dist/chunks/{run-CgTWYlcd.mjs → run-NGAxdsIX.mjs} +28 -5
  56. package/dist/chunks/run-NGAxdsIX.mjs.map +1 -0
  57. package/dist/chunks/{schema-C9QbUW4H.mjs → schema-Bq9qtgsd.mjs} +9 -4
  58. package/dist/chunks/schema-Bq9qtgsd.mjs.map +1 -0
  59. package/dist/chunks/ship-BizfUe62.mjs +140 -0
  60. package/dist/chunks/ship-BizfUe62.mjs.map +1 -0
  61. package/dist/chunks/spec-C2h3U64g.mjs +65 -0
  62. package/dist/chunks/spec-C2h3U64g.mjs.map +1 -0
  63. package/dist/chunks/spec-path-tptqR7W8.mjs +10 -0
  64. package/dist/chunks/spec-path-tptqR7W8.mjs.map +1 -0
  65. package/dist/chunks/{src-r0ECB1UJ.mjs → src-Bfr1yzsn.mjs} +3 -3
  66. package/dist/chunks/{src-r0ECB1UJ.mjs.map → src-Bfr1yzsn.mjs.map} +1 -1
  67. package/dist/chunks/{src-DZazJCp1.mjs → src-CPW2_Ons.mjs} +101 -17
  68. package/dist/chunks/src-CPW2_Ons.mjs.map +1 -0
  69. package/dist/chunks/{src-CmFjh4M0.mjs → src-DNsLSxgm.mjs} +2 -2
  70. package/dist/chunks/{src-CmFjh4M0.mjs.map → src-DNsLSxgm.mjs.map} +1 -1
  71. package/dist/chunks/{src-DcU4IXMe.mjs → src-DbS9gkJy.mjs} +2 -2
  72. package/dist/chunks/{src-DcU4IXMe.mjs.map → src-DbS9gkJy.mjs.map} +1 -1
  73. package/dist/chunks/{src-s7YRvk0t.mjs → src-yi5whv3n.mjs} +4 -2
  74. package/dist/chunks/src-yi5whv3n.mjs.map +1 -0
  75. package/dist/chunks/update-pipeline-Du-Gu1Pl.mjs +56 -0
  76. package/dist/chunks/update-pipeline-Du-Gu1Pl.mjs.map +1 -0
  77. package/dist/cli.mjs +29 -18
  78. package/dist/cli.mjs.map +1 -1
  79. package/package.json +1 -1
  80. package/dist/chunks/brainstorm-BwKA6yZP.mjs +0 -7
  81. package/dist/chunks/brainstorm-BwKA6yZP.mjs.map +0 -1
  82. package/dist/chunks/compose-CKo_q39e.mjs.map +0 -1
  83. package/dist/chunks/contract-BkMjU1kR.mjs.map +0 -1
  84. package/dist/chunks/fix-D4Tj8RVA.mjs +0 -18
  85. package/dist/chunks/fix-D4Tj8RVA.mjs.map +0 -1
  86. package/dist/chunks/review-BDdjb6kB.mjs +0 -17
  87. package/dist/chunks/review-BDdjb6kB.mjs.map +0 -1
  88. package/dist/chunks/run-CgTWYlcd.mjs.map +0 -1
  89. package/dist/chunks/schema-C9QbUW4H.mjs.map +0 -1
  90. package/dist/chunks/ship-DTuLGoaJ.mjs +0 -19
  91. package/dist/chunks/ship-DTuLGoaJ.mjs.map +0 -1
  92. package/dist/chunks/spec-Bq0P3jVh.mjs +0 -20
  93. package/dist/chunks/spec-Bq0P3jVh.mjs.map +0 -1
  94. package/dist/chunks/src-DZazJCp1.mjs.map +0 -1
  95. package/dist/chunks/src-s7YRvk0t.mjs.map +0 -1
@@ -0,0 +1,78 @@
1
+ import { appendFile, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ //#region src/commands/retro/index.ts
4
+ const normalise = (line) => line.replace(/^-\s+\[[ xX]\]\s+/u, "").replace(/\b[A-Za-z0-9_./-]+:\d+\b/gu, "<file>").replace(/\s+/gu, " ").trim().toLowerCase();
5
+ const listFiles = async (dir) => {
6
+ try {
7
+ return (await readdir(dir, { withFileTypes: true })).filter((x) => x.isFile()).map((x) => join(dir, x.name));
8
+ } catch {
9
+ return [];
10
+ }
11
+ };
12
+ const retro = {
13
+ verb: "retro",
14
+ async run(ctx, args) {
15
+ const reportDir = join(ctx.cwd, "specs", "reports");
16
+ let files = [...await listFiles(reportDir), ...await listFiles(join(ctx.cwd, "specs"))].filter((file) => !file.endsWith("_decisions.md"));
17
+ if (args.positionals.find((value, index) => value === "last" && args.positionals[index + 1])) {
18
+ const count = Number(args.positionals[args.positionals.indexOf("last") + 1]);
19
+ if (!Number.isInteger(count) || count < 1) return 2;
20
+ files = files.sort().slice(-count);
21
+ }
22
+ const grouped = /* @__PURE__ */ new Map();
23
+ for (const file of files) {
24
+ let source = "";
25
+ try {
26
+ source = await readFile(file, "utf8");
27
+ } catch {
28
+ continue;
29
+ }
30
+ for (const line of source.split(/\r?\n/u).filter((x) => /\b(CRITICAL|HIGH|MEDIUM|LOW)\b|blocking_items|Remediation/iu.test(x))) {
31
+ const key = normalise(line);
32
+ if (key.length < 12) continue;
33
+ const item = grouped.get(key) ?? {
34
+ key,
35
+ count: 0,
36
+ evidence: [],
37
+ rule: `Enforce: ${line.trim()}`
38
+ };
39
+ item.count++;
40
+ if (item.evidence.length < 5) item.evidence.push(`${file.replace(`${ctx.cwd}/`, "")} · ${line.trim()}`);
41
+ grouped.set(key, item);
42
+ }
43
+ }
44
+ const patterns = [...grouped.values()].map((pattern) => ({
45
+ ...pattern,
46
+ count: new Set(pattern.evidence.map((value) => value.split(" · ")[0])).size
47
+ })).filter((x) => x.count >= 2).sort((a, b) => b.count - a.count);
48
+ const result = {
49
+ patterns,
50
+ evidenceFiles: files.map((x) => x.replace(`${ctx.cwd}/`, ""))
51
+ };
52
+ await mkdir(reportDir, { recursive: true });
53
+ await writeFile(join(reportDir, "retro-scan.txt"), `${ctx.clock.now()}\n${patterns.map((pattern) => `${pattern.count}x ${pattern.rule}\n${pattern.evidence.join("\n")}`).join("\n")}\n`, "utf8");
54
+ if (!args.positionals.includes("--apply")) {
55
+ ctx.stdio.stdout.write(`${args.json ? JSON.stringify(result) : patterns.length ? patterns.map((x) => `${x.count}x · ${x.rule}`).join("\n") : "no recurring review patterns found"}\n`);
56
+ return 0;
57
+ }
58
+ const rules = args.positionals.flatMap((value, index, all) => value === "--rule" && all[index + 1] ? [all[index + 1]] : []);
59
+ if (!rules.length) return 2;
60
+ const pipeline = join(ctx.cwd, "PIPELINE.md");
61
+ let source = "";
62
+ try {
63
+ source = await readFile(pipeline, "utf8");
64
+ } catch {
65
+ source = "# Cohorte Pipeline\n\n## Conventions\n\n";
66
+ }
67
+ if (!/^## Conventions\s*$/mu.test(source)) source += `${source.endsWith("\n") ? "" : "\n"}\n## Conventions\n\n`;
68
+ await writeFile(pipeline, `${source}${source.endsWith("\n") ? "" : "\n"}${rules.map((x) => `- ${x}`).join("\n")}\n`, "utf8");
69
+ await mkdir(join(ctx.cwd, "specs"), { recursive: true });
70
+ await appendFile(join(ctx.cwd, "specs", "_decisions.md"), `${ctx.clock.now().slice(0, 10)} · conventions · ${rules.join("; ")} — because retro ratification\n`);
71
+ ctx.stdio.stdout.write(`applied ${rules.length} convention(s); run cohorte update-pipeline to refresh agents\n`);
72
+ return 0;
73
+ }
74
+ };
75
+ //#endregion
76
+ export { retro as default };
77
+
78
+ //# sourceMappingURL=retro-Dr-2RTNN.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retro-Dr-2RTNN.mjs","names":[],"sources":["../../../../apps/cli/src/commands/retro/index.ts"],"sourcesContent":["import { appendFile, mkdir, readdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { CommandModule } from '../../contract/index.ts';\n\ntype Pattern = { key: string; count: number; evidence: string[]; rule: string };\nconst normalise = (line: string) =>\n line\n .replace(/^-\\s+\\[[ xX]\\]\\s+/u, '')\n .replace(/\\b[A-Za-z0-9_./-]+:\\d+\\b/gu, '<file>')\n .replace(/\\s+/gu, ' ')\n .trim()\n .toLowerCase();\nconst listFiles = async (dir: string) => {\n try {\n return (await readdir(dir, { withFileTypes: true })).filter((x) => x.isFile()).map((x) => join(dir, x.name));\n } catch {\n return [];\n }\n};\n\nconst retro: CommandModule = {\n verb: 'retro',\n async run(ctx, args) {\n const reportDir = join(ctx.cwd, 'specs', 'reports');\n let files = [...(await listFiles(reportDir)), ...(await listFiles(join(ctx.cwd, 'specs')))].filter(\n (file) => !file.endsWith('_decisions.md'),\n );\n const last = args.positionals.find((value, index) => value === 'last' && args.positionals[index + 1]);\n if (last) {\n const count = Number(args.positionals[args.positionals.indexOf('last') + 1]);\n if (!Number.isInteger(count) || count < 1) return 2;\n files = files.sort().slice(-count);\n }\n const grouped = new Map<string, Pattern>();\n for (const file of files) {\n let source = '';\n try {\n source = await readFile(file, 'utf8');\n } catch {\n continue;\n }\n for (const line of source\n .split(/\\r?\\n/u)\n .filter((x) => /\\b(CRITICAL|HIGH|MEDIUM|LOW)\\b|blocking_items|Remediation/iu.test(x))) {\n const key = normalise(line);\n if (key.length < 12) continue;\n const item = grouped.get(key) ?? { key, count: 0, evidence: [], rule: `Enforce: ${line.trim()}` };\n item.count++;\n if (item.evidence.length < 5) item.evidence.push(`${file.replace(`${ctx.cwd}/`, '')} · ${line.trim()}`);\n grouped.set(key, item);\n }\n }\n const patterns = [...grouped.values()]\n .map((pattern) => ({ ...pattern, count: new Set(pattern.evidence.map((value) => value.split(' · ')[0])).size }))\n .filter((x) => x.count >= 2)\n .sort((a, b) => b.count - a.count);\n const result = { patterns, evidenceFiles: files.map((x) => x.replace(`${ctx.cwd}/`, '')) };\n await mkdir(reportDir, { recursive: true });\n await writeFile(\n join(reportDir, 'retro-scan.txt'),\n `${ctx.clock.now()}\\n${patterns.map((pattern) => `${pattern.count}x ${pattern.rule}\\n${pattern.evidence.join('\\n')}`).join('\\n')}\\n`,\n 'utf8',\n );\n if (!args.positionals.includes('--apply')) {\n ctx.stdio.stdout.write(\n `${args.json ? JSON.stringify(result) : patterns.length ? patterns.map((x) => `${x.count}x · ${x.rule}`).join('\\n') : 'no recurring review patterns found'}\\n`,\n );\n return 0;\n }\n const rules = args.positionals.flatMap((value, index, all) =>\n value === '--rule' && all[index + 1] ? [all[index + 1] as string] : [],\n );\n if (!rules.length) return 2;\n const pipeline = join(ctx.cwd, 'PIPELINE.md');\n let source = '';\n try {\n source = await readFile(pipeline, 'utf8');\n } catch {\n source = '# Cohorte Pipeline\\n\\n## Conventions\\n\\n';\n }\n if (!/^## Conventions\\s*$/mu.test(source)) source += `${source.endsWith('\\n') ? '' : '\\n'}\\n## Conventions\\n\\n`;\n await writeFile(\n pipeline,\n `${source}${source.endsWith('\\n') ? '' : '\\n'}${rules.map((x) => `- ${x}`).join('\\n')}\\n`,\n 'utf8',\n );\n await mkdir(join(ctx.cwd, 'specs'), { recursive: true });\n await appendFile(\n join(ctx.cwd, 'specs', '_decisions.md'),\n `${ctx.clock.now().slice(0, 10)} · conventions · ${rules.join('; ')} — because retro ratification\\n`,\n );\n ctx.stdio.stdout.write(`applied ${rules.length} convention(s); run cohorte update-pipeline to refresh agents\\n`);\n return 0;\n },\n};\nexport default retro;\n"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { t as resolveSpecPath } from "./spec-path-tptqR7W8.mjs";
2
+ //#region src/commands/review/index.ts
3
+ const review = {
4
+ verb: "review",
5
+ async run(ctx, args) {
6
+ const valueAfter = (flag) => {
7
+ const index = args.positionals.indexOf(flag);
8
+ return index >= 0 ? args.positionals[index + 1] : void 0;
9
+ };
10
+ const target = args.positionals.find((value, index) => !value.startsWith("--") && args.positionals[index - 1] !== "--ref" && args.positionals[index - 1] !== "--base" && args.positionals[index - 1] !== "--head" && args.positionals[index - 1] !== "--pr");
11
+ const ref = valueAfter("--ref");
12
+ const base = valueAfter("--base");
13
+ const head = valueAfter("--head");
14
+ const pr = valueAfter("--pr");
15
+ const surfaces = args.positionals.flatMap((value, index) => value === "--surface" && args.positionals[index + 1] ? [args.positionals[index + 1]] : []).concat((valueAfter("--surfaces") ?? "").split(",").filter(Boolean));
16
+ if (args.positionals.includes("--pr") && !pr) return 2;
17
+ const reviewTarget = pr ? { ref: `refs/pull/${pr}/head` } : base || head ? {
18
+ base: base ?? "HEAD~1",
19
+ head: head ?? "HEAD"
20
+ } : ref ? { ref } : target?.startsWith("run_") ? { runId: target } : target?.includes("/") || target?.startsWith("refs/") ? { ref: target } : void 0;
21
+ const result = await ctx.controller.send("start", {
22
+ profile: "review",
23
+ unattended: false,
24
+ ...surfaces.length > 0 ? { surfaces } : {},
25
+ ...reviewTarget ? { reviewTarget } : {},
26
+ ...!reviewTarget && target ? { spec: { path: resolveSpecPath(ctx.cwd, target) } } : {}
27
+ });
28
+ ctx.stdio.stdout.write(`${JSON.stringify(result)}\n`);
29
+ return result.status === "rejected" ? 3 : result.status === "pending" ? 4 : 0;
30
+ }
31
+ };
32
+ //#endregion
33
+ export { review as default };
34
+
35
+ //# sourceMappingURL=review-ZsBqp3vZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-ZsBqp3vZ.mjs","names":[],"sources":["../../../../apps/cli/src/commands/review/index.ts"],"sourcesContent":["import type { CommandModule } from '../../contract/index.ts';\nimport { resolveSpecPath } from '../../project/spec-path.ts';\n\nconst review: CommandModule = {\n verb: 'review',\n async run(ctx, args) {\n const valueAfter = (flag: string): string | undefined => {\n const index = args.positionals.indexOf(flag);\n return index >= 0 ? args.positionals[index + 1] : undefined;\n };\n const target = args.positionals.find(\n (value, index) =>\n !value.startsWith('--') &&\n args.positionals[index - 1] !== '--ref' &&\n args.positionals[index - 1] !== '--base' &&\n args.positionals[index - 1] !== '--head' &&\n args.positionals[index - 1] !== '--pr',\n );\n const ref = valueAfter('--ref');\n const base = valueAfter('--base');\n const head = valueAfter('--head');\n const pr = valueAfter('--pr');\n const surfaces = args.positionals\n .flatMap((value, index) =>\n value === '--surface' && args.positionals[index + 1] ? [args.positionals[index + 1] as never] : [],\n )\n .concat((valueAfter('--surfaces') ?? '').split(',').filter(Boolean) as never[]);\n if (args.positionals.includes('--pr') && !pr) return 2;\n const reviewTarget: { ref: string } | { base: string; head: string } | { runId: never } | undefined = pr\n ? { ref: `refs/pull/${pr}/head` }\n : base || head\n ? { base: base ?? 'HEAD~1', head: head ?? 'HEAD' }\n : ref\n ? { ref }\n : target?.startsWith('run_')\n ? { runId: target as never }\n : target?.includes('/') || target?.startsWith('refs/')\n ? { ref: target }\n : undefined;\n const result = await ctx.controller.send('start', {\n profile: 'review',\n unattended: false,\n ...(surfaces.length > 0 ? { surfaces } : {}),\n ...(reviewTarget ? { reviewTarget } : {}),\n ...(!reviewTarget && target ? { spec: { path: resolveSpecPath(ctx.cwd, target) } } : {}),\n });\n ctx.stdio.stdout.write(`${JSON.stringify(result)}\\n`);\n return result.status === 'rejected' ? 3 : result.status === 'pending' ? 4 : 0;\n },\n};\n\nexport default review;\n"],"mappings":";;AAGA,MAAM,SAAwB;CAC5B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,cAAc,SAAqC;GACvD,MAAM,QAAQ,KAAK,YAAY,QAAQ,IAAI;GAC3C,OAAO,SAAS,IAAI,KAAK,YAAY,QAAQ,KAAK,KAAA;EACpD;EACA,MAAM,SAAS,KAAK,YAAY,MAC7B,OAAO,UACN,CAAC,MAAM,WAAW,IAAI,KACtB,KAAK,YAAY,QAAQ,OAAO,WAChC,KAAK,YAAY,QAAQ,OAAO,YAChC,KAAK,YAAY,QAAQ,OAAO,YAChC,KAAK,YAAY,QAAQ,OAAO,MACpC;EACA,MAAM,MAAM,WAAW,OAAO;EAC9B,MAAM,OAAO,WAAW,QAAQ;EAChC,MAAM,OAAO,WAAW,QAAQ;EAChC,MAAM,KAAK,WAAW,MAAM;EAC5B,MAAM,WAAW,KAAK,YACnB,SAAS,OAAO,UACf,UAAU,eAAe,KAAK,YAAY,QAAQ,KAAK,CAAC,KAAK,YAAY,QAAQ,EAAW,IAAI,CAAC,CACnG,CAAC,CACA,QAAQ,WAAW,YAAY,KAAK,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CAAY;EAChF,IAAI,KAAK,YAAY,SAAS,MAAM,KAAK,CAAC,IAAI,OAAO;EACrD,MAAM,eAAgG,KAClG,EAAE,KAAK,aAAa,GAAG,OAAO,IAC9B,QAAQ,OACN;GAAE,MAAM,QAAQ;GAAU,MAAM,QAAQ;EAAO,IAC/C,MACE,EAAE,IAAI,IACN,QAAQ,WAAW,MAAM,IACvB,EAAE,OAAO,OAAgB,IACzB,QAAQ,SAAS,GAAG,KAAK,QAAQ,WAAW,OAAO,IACjD,EAAE,KAAK,OAAO,IACd,KAAA;EACZ,MAAM,SAAS,MAAM,IAAI,WAAW,KAAK,SAAS;GAChD,SAAS;GACT,YAAY;GACZ,GAAI,SAAS,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;GAC1C,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;GACvC,GAAI,CAAC,gBAAgB,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,IAAI,KAAK,MAAM,EAAE,EAAE,IAAI,CAAC;EACxF,CAAC;EACD,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,EAAE,GAAG;EACpD,OAAO,OAAO,WAAW,aAAa,IAAI,OAAO,WAAW,YAAY,IAAI;CAC9E;AACF"}
@@ -1,10 +1,13 @@
1
+ import { i as __exportAll, n as moveConfiguredCard } from "./obsidian-BHLkXx_H.mjs";
1
2
  import { n as sha256Hex } from "./canonical-C8UxeEmB.mjs";
2
3
  import { N as errorOf, j as CohorteError } from "./src-Cu3TxNob.mjs";
3
4
  import { n as createKeyStore, r as createTrustStore } from "./auth-BT5oVp76.mjs";
4
- import { i as resolveConfig, n as loadConfig } from "./src-DcU4IXMe.mjs";
5
+ import { t as resolveSpecPath } from "./spec-path-tptqR7W8.mjs";
6
+ import { i as resolveConfig, n as loadConfig } from "./src-DbS9gkJy.mjs";
5
7
  import { join } from "node:path";
6
8
  import { existsSync } from "node:fs";
7
9
  //#region src/commands/run/index.ts
10
+ var run_exports = /* @__PURE__ */ __exportAll({ default: () => run });
8
11
  const run = {
9
12
  verb: "run",
10
13
  async run(ctx, args) {
@@ -22,12 +25,24 @@ const run = {
22
25
  "bugfix",
23
26
  "review"
24
27
  ].includes(profile)) return 2;
25
- const spec = args.positionals.find((value, index) => !value.startsWith("--") && args.positionals[index - 1] !== "--profile" && args.positionals[index - 1] !== "--runtime" && args.positionals[index - 1] !== "--script" && args.positionals[index - 1] !== "--model" && ![
28
+ const spec = args.positionals.find((value, index) => !value.startsWith("--") && args.positionals[index - 1] !== "--profile" && args.positionals[index - 1] !== "--runtime" && args.positionals[index - 1] !== "--script" && args.positionals[index - 1] !== "--model" && args.positionals[index - 1] !== "--wait" && args.positionals[index - 1] !== "--phases" && ![
26
29
  "feature",
27
30
  "bugfix",
28
31
  "review"
29
32
  ].includes(value));
30
33
  const model = valueAfter("--model");
34
+ const phasesValue = valueAfter("--phases");
35
+ const surfaces = args.positionals.flatMap((value, index) => value === "--surface" && args.positionals[index + 1] ? [args.positionals[index + 1]] : []).concat((valueAfter("--surfaces") ?? "").split(",").filter(Boolean));
36
+ const phases = phasesValue?.split(",").map((value) => value.trim().toUpperCase()).filter((value) => [
37
+ "BRAINSTORM",
38
+ "SPEC",
39
+ "PREFLIGHT",
40
+ "BUILD",
41
+ "TEST",
42
+ "REVIEW",
43
+ "FIX",
44
+ "SHIP"
45
+ ].includes(value));
31
46
  const waitValue = valueAfter("--wait");
32
47
  const waitMs = waitValue === void 0 ? void 0 : Number(waitValue) * 1e3;
33
48
  if (waitValue !== void 0 && (!Number.isFinite(waitMs) || waitMs < 0)) return 2;
@@ -57,9 +72,13 @@ const run = {
57
72
  const payload = {
58
73
  profile,
59
74
  unattended: false,
60
- ...spec ? { spec: { path: spec } } : {},
75
+ ...spec ? { spec: { path: resolveSpecPath(ctx.cwd, spec) } } : {},
61
76
  ...valueAfter("--runtime") ? { runtime: valueAfter("--runtime") } : {},
62
77
  ...valueAfter("--script") ? { fakeScript: valueAfter("--script") } : {},
78
+ ...phasesValue !== void 0 ? { phases: phases ?? [] } : {},
79
+ ...surfaces.length > 0 ? { surfaces } : {},
80
+ ...args.positionals.includes("--with-fix") ? { withFix: true } : {},
81
+ ...args.positionals.includes("--unattended") ? { unattended: true } : {},
63
82
  ...model ? { modelOverrides: { implementer: {
64
83
  provider: "default",
65
84
  model
@@ -73,10 +92,14 @@ const run = {
73
92
  }
74
93
  if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(result)}\n`);
75
94
  else ctx.stdio.stdout.write(`${result.status}\n`);
95
+ if (result.status === "pending" || result.status === "completed") {
96
+ const card = spec?.replace(/\.ya?ml$/, "").split(/[\\/]/).pop();
97
+ if (card) await moveConfiguredCard(ctx.env.HOME ?? ctx.cwd, card, profile === "review" ? "review" : "building");
98
+ }
76
99
  return result.status === "rejected" ? 3 : result.status === "pending" ? 4 : 0;
77
100
  }
78
101
  };
79
102
  //#endregion
80
- export { run as default };
103
+ export { run_exports as n, run as t };
81
104
 
82
- //# sourceMappingURL=run-CgTWYlcd.mjs.map
105
+ //# sourceMappingURL=run-NGAxdsIX.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-NGAxdsIX.mjs","names":[],"sources":["../../../../apps/cli/src/commands/run/index.ts"],"sourcesContent":["// apps/cli/src/commands/run/index.ts — DESIGN §9 verb `run` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/run/**`.\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { CohorteError, errorOf, type Sha256, type SurfaceId, sha256Hex } from '@cohorte/base';\nimport { loadConfig, resolveConfig } from '@cohorte/config';\nimport { createKeyStore, createTrustStore } from '@cohorte/security/auth';\nimport type { CommandModule } from '../../contract/index.ts';\nimport { resolveSpecPath } from '../../project/spec-path.ts';\nimport { moveConfiguredCard } from '../obsidian/index.ts';\n\nconst run: CommandModule = {\n verb: 'run',\n async run(ctx, args) {\n const valueAfter = (flag: string): string | undefined => {\n const index = args.positionals.indexOf(flag);\n return index >= 0 ? args.positionals[index + 1] : undefined;\n };\n const profile = (valueAfter('--profile') ??\n args.positionals.find((value) => ['feature', 'bugfix', 'review'].includes(value)) ??\n 'feature') as 'feature' | 'bugfix' | 'review';\n if (!['feature', 'bugfix', 'review'].includes(profile)) return 2;\n const spec = args.positionals.find(\n (value, index) =>\n !value.startsWith('--') &&\n args.positionals[index - 1] !== '--profile' &&\n args.positionals[index - 1] !== '--runtime' &&\n args.positionals[index - 1] !== '--script' &&\n args.positionals[index - 1] !== '--model' &&\n args.positionals[index - 1] !== '--wait' &&\n args.positionals[index - 1] !== '--phases' &&\n !['feature', 'bugfix', 'review'].includes(value),\n );\n const model = valueAfter('--model');\n const phasesValue = valueAfter('--phases');\n const surfaces = args.positionals\n .flatMap((value, index) =>\n value === '--surface' && args.positionals[index + 1] ? [args.positionals[index + 1] as string] : [],\n )\n .concat((valueAfter('--surfaces') ?? '').split(',').filter(Boolean));\n const phases = phasesValue\n ?.split(',')\n .map((value) => value.trim().toUpperCase())\n .filter((value): value is 'BRAINSTORM' | 'SPEC' | 'PREFLIGHT' | 'BUILD' | 'TEST' | 'REVIEW' | 'FIX' | 'SHIP' =>\n ['BRAINSTORM', 'SPEC', 'PREFLIGHT', 'BUILD', 'TEST', 'REVIEW', 'FIX', 'SHIP'].includes(value),\n );\n const waitValue = valueAfter('--wait');\n const waitMs = waitValue === undefined ? undefined : Number(waitValue) * 1000;\n if (waitValue !== undefined && (!Number.isFinite(waitMs) || (waitMs as number) < 0)) return 2;\n const trustProjectConfig = args.positionals.includes('--trust-project-config');\n let consent: { policySha256: Sha256; via: 'cli-flag' } | undefined;\n // Test/fake contexts intentionally omit HOME; a real CLI invocation always\n // supplies it and is the only context allowed to resolve project trust.\n if (ctx.env.HOME && existsSync(join(ctx.cwd, '.cohorte', 'config.yaml'))) {\n const home = ctx.env.HOME ?? ctx.cwd;\n const loaded = await loadConfig({ cwd: ctx.cwd, home });\n const keys = createKeyStore({ directory: join(home, '.cohorte', 'keys') });\n const trustStore = createTrustStore({ directory: join(home, '.cohorte', 'trust'), keys });\n const resolved = await resolveConfig(loaded, {\n trustStore,\n projectKeyId: sha256Hex(loaded.projectRoot).slice(0, 24),\n });\n if (resolved.status === 'untrusted' && !trustProjectConfig) {\n throw new CohorteError(\n errorOf(\n 'security/project-policy-untrusted',\n `project policy is untrusted (${resolved.loosenedKeys.join(', ') || 'unknown key'})`,\n ),\n );\n }\n if (resolved.status === 'untrusted') consent = { policySha256: resolved.policySha256, via: 'cli-flag' };\n }\n const payload = {\n profile,\n unattended: false,\n ...(spec ? { spec: { path: resolveSpecPath(ctx.cwd, spec) } } : {}),\n ...(valueAfter('--runtime') ? { runtime: valueAfter('--runtime') as string } : {}),\n ...(valueAfter('--script') ? { fakeScript: valueAfter('--script') as string } : {}),\n ...(phasesValue !== undefined ? { phases: phases ?? [] } : {}),\n ...(surfaces.length > 0 ? { surfaces: surfaces as SurfaceId[] } : {}),\n ...(args.positionals.includes('--with-fix') ? { withFix: true } : {}),\n ...(args.positionals.includes('--unattended') ? { unattended: true } : {}),\n ...(model ? { modelOverrides: { implementer: { provider: 'default', model } } } : {}),\n ...(consent ? { consent } : {}),\n };\n const result =\n waitMs === undefined\n ? await ctx.controller.send('start', payload)\n : await ctx.controller.send('start', payload, { waitMs });\n if (result.status === 'pending' && result.result && typeof result.result === 'object' && 'runId' in result.result) {\n const runId = (result.result as { runId?: string }).runId;\n if (runId) await ctx.hostSpawner.spawnDetached(runId);\n }\n if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(result)}\\n`);\n else ctx.stdio.stdout.write(`${result.status}\\n`);\n if (result.status === 'pending' || result.status === 'completed') {\n const card = spec\n ?.replace(/\\.ya?ml$/, '')\n .split(/[\\\\/]/)\n .pop();\n if (card) await moveConfiguredCard(ctx.env.HOME ?? ctx.cwd, card, profile === 'review' ? 'review' : 'building');\n }\n return result.status === 'rejected' ? 3 : result.status === 'pending' ? 4 : 0;\n },\n};\n\nexport default run;\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { n as sha256Hex, t as canonicalJson } from "./canonical-C8UxeEmB.mjs";
2
2
  import { S as IsoInstant, T as Sha256, d as ModelRef, l as AuthMode, p as ThinkingLevel, t as BudgetCounters, u as ModelCapability, x as ID_PATTERN } from "./src-Cu3TxNob.mjs";
3
- import { d as EscalationPolicy, u as ActivePipelineState } from "./src-s7YRvk0t.mjs";
3
+ import { d as ActivePipelineState, f as EscalationPolicy } from "./src-yi5whv3n.mjs";
4
4
  import { Type } from "typebox";
5
5
  //#region ../../packages/config/src/schema/policy.ts
6
6
  const closed$5 = { additionalProperties: false };
@@ -212,7 +212,11 @@ const CohorteConfigSchema = Type.Object({
212
212
  artifactsDays: count(),
213
213
  compressAfterDays: count()
214
214
  }, closed$4),
215
- telemetry: Type.Object({ remote: Type.Boolean() }, closed$4)
215
+ telemetry: Type.Object({ remote: Type.Boolean() }, closed$4),
216
+ integrations: Type.Optional(Type.Object({ obsidian: Type.Optional(Type.Object({
217
+ vaultPath: Type.String({ minLength: 1 }),
218
+ board: Type.String({ minLength: 1 })
219
+ }, closed$4)) }, closed$4))
216
220
  }, closed$4);
217
221
  /** [S]. Annotated on purpose: Biome must not infer this type (docs/v3/requests/U0.02.md R1, U0.05.md R1). */
218
222
  const CohorteConfig = Type.Unsafe(CohorteConfigSchema);
@@ -563,7 +567,8 @@ Object.freeze([
563
567
  neutral("/git/commitIdentity"),
564
568
  neutral("/git/keepWorktrees"),
565
569
  tighten("/retention"),
566
- neutral("/telemetry")
570
+ neutral("/telemetry"),
571
+ neutral("/integrations")
567
572
  ]);
568
573
  Type.Unsafe(Type.Object({
569
574
  policySha256: Sha256,
@@ -575,4 +580,4 @@ Type.Unsafe(Type.Object({
575
580
  //#endregion
576
581
  export { literalPrefixOf as a, CommandRule as c, Ownership as i, DEFAULT_NETWORK_POLICY as l, frozenSpecProblems as n, CohorteConfig as o, specContentSha256 as r, DEFAULT_CONFIG as s, Spec as t };
577
582
 
578
- //# sourceMappingURL=schema-C9QbUW4H.mjs.map
583
+ //# sourceMappingURL=schema-Bq9qtgsd.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-Bq9qtgsd.mjs","names":["closed","closed","closed","closed","names","closed"],"sources":["../../../../packages/config/src/schema/policy.ts","../../../../packages/config/src/schema/config.ts","../../../../packages/config/src/schema/manifest.ts","../../../../packages/config/src/schema/ownership.ts","../../../../packages/config/src/schema/skill.ts","../../../../packages/config/src/schema/spec.ts","../../../../packages/config/src/schema/trust.ts"],"sourcesContent":["// Policy DATA shapes (DESIGN 2.6.3, 2.6.4): declared here, evaluated by @cohorte/security. `config` never imports `security`.\nimport { type Static, type TUnsafe, Type } from 'typebox';\n\nconst closed = { additionalProperties: false } as const;\nconst tokens = () => Type.Array(Type.String({ minLength: 1 }));\n\nexport const COMMAND_RULE_ORIGINS = ['builtin', 'project-config', 'project-checks'] as const;\n\n/** DESIGN 2.6.4, verbatim. */\nexport interface CommandRule {\n id: string;\n program: string;\n /** exact tokens, e.g. ['run','test'] */\n subcommand?: readonly string[];\n /** anything not allowed is denied */\n flags?: { allow: readonly string[]; deny?: readonly string[] };\n positionals?:\n | { kind: 'none' }\n | { kind: 'paths-in-worktree'; max: number }\n | { kind: 'enum'; values: readonly string[]; max: number }\n | { kind: 'exact'; values: readonly string[] };\n decision: 'allow' | 'ask' | 'deny';\n when?: { branch?: 'any' | 'unprotected-only'; roles?: readonly string[]; phases?: readonly string[] };\n /** drives recovery (DESIGN 4.1) */\n replay: 'idempotent' | 'at-most-once';\n /** true => denied under L0; under L1 it fails closed in the sandbox */\n network: boolean;\n origin: (typeof COMMAND_RULE_ORIGINS)[number];\n}\n\nconst max = () => Type.Integer({ minimum: 0 });\n\nconst CommandRuleSchema = Type.Object(\n {\n id: Type.String({ minLength: 1 }),\n // A bare program name: a path separator can never name a program (DESIGN 2.6.4 step 1).\n program: Type.String({ pattern: '^[^/\\\\\\\\\\\\s]+$' }),\n subcommand: Type.Optional(tokens()),\n flags: Type.Optional(Type.Object({ allow: tokens(), deny: Type.Optional(tokens()) }, closed)),\n positionals: Type.Optional(\n Type.Union([\n Type.Object({ kind: Type.Literal('none') }, closed),\n Type.Object({ kind: Type.Literal('paths-in-worktree'), max: max() }, closed),\n Type.Object({ kind: Type.Literal('enum'), values: tokens(), max: max() }, closed),\n Type.Object({ kind: Type.Literal('exact'), values: Type.Array(Type.String()) }, closed),\n ]),\n ),\n decision: Type.Union([Type.Literal('allow'), Type.Literal('ask'), Type.Literal('deny')]),\n when: Type.Optional(\n Type.Object(\n {\n branch: Type.Optional(Type.Union([Type.Literal('any'), Type.Literal('unprotected-only')])),\n roles: Type.Optional(tokens()),\n phases: Type.Optional(tokens()),\n },\n closed,\n ),\n ),\n replay: Type.Union([Type.Literal('idempotent'), Type.Literal('at-most-once')]),\n network: Type.Boolean(),\n origin: Type.Union([Type.Literal('builtin'), Type.Literal('project-config'), Type.Literal('project-checks')]),\n },\n closed,\n);\n\n/** [S]. The explicit annotation keeps Biome's type inference out of the union (docs/v3/requests/U0.02.md R1). */\nexport const CommandRule: TUnsafe<CommandRule> = Type.Unsafe<CommandRule>(CommandRuleSchema);\n\n/** [S] DESIGN 2.6.3. Default: deny-outgoing + deny. */\nexport const SymlinkPolicy = Type.Object(\n {\n mode: Type.Union([Type.Literal('deny-outgoing'), Type.Literal('deny-all'), Type.Literal('allow')]),\n hardlinksOnWrite: Type.Union([Type.Literal('deny'), Type.Literal('allow')]),\n },\n closed,\n);\nexport type SymlinkPolicy = Static<typeof SymlinkPolicy>;\n\n/**\n * [S] What stage 4 evaluates for `network_request`. V3.0 has exactly one value: the tool is registered and always\n * denied (DESIGN 2.6.2, §9 \"proxy-based host allowlisting\" is out). The shape exists so a later version can add\n * an allowlist without moving the type.\n */\nexport const NetworkPolicyConfig = Type.Object({ default: Type.Literal('deny') }, closed);\nexport type NetworkPolicyConfig = Static<typeof NetworkPolicyConfig>;\n\nexport const DEFAULT_SYMLINK_POLICY: SymlinkPolicy = Object.freeze({ mode: 'deny-outgoing', hardlinksOnWrite: 'deny' });\nexport const DEFAULT_NETWORK_POLICY: NetworkPolicyConfig = Object.freeze({ default: 'deny' });\n","// `.cohorte/config.yaml` (DESIGN 2.10). NO open question of spec 32 is frozen as a literal type: `runtime.id`,\n// provider names and role keys are open strings; what 3.0 accepts among them is a LOADER rule, not a schema rule.\nimport { AuthMode, BudgetCounters, ID_PATTERN, ModelCapability, ModelRef, ThinkingLevel } from '@cohorte/base';\nimport { ActivePipelineState, type CohorteRole, EscalationPolicy } from '@cohorte/protocol';\nimport { type TUnsafe, Type } from 'typebox';\nimport { CommandRule, DEFAULT_SYMLINK_POLICY, SymlinkPolicy } from './policy.ts';\n\nconst closed = { additionalProperties: false } as const;\n\n/** The ONLY env channel of a provisioning effect (DESIGN 5.7): a closed allowlist of NAMES. */\nexport const PROVISION_ENV_NAMES = [\n 'npm_config_store_dir',\n 'npm_config_cache',\n 'YARN_CACHE_FOLDER',\n 'COREPACK_HOME',\n] as const;\nexport type ProvisionEnvName = (typeof PROVISION_ENV_NAMES)[number];\n\nexport interface TierTarget {\n ref: ModelRef;\n thinking: ThinkingLevel;\n}\nexport interface ApprovalsConfig {\n unattended: 'deny' | 'wait';\n expiryMinutes?: number;\n /** 10 */\n parkAfterMinutes: number;\n ship: 'human' | 'auto';\n notify: boolean;\n /** approve may spawn a host */\n autoResume: boolean;\n}\n\n/** DESIGN 2.10, verbatim. [S] schemas/config.schema.json */\nexport interface CohorteConfig {\n schemaVersion: 1;\n project: { id: string; defaultBranch: string; protectedBranches: string[] };\n runtime: { id: string /* 'pi' | 'fake' */; pi?: { loadFrom: 'package' | 'bundle' } };\n authentication: {\n mode: AuthMode;\n allowApiKeys: boolean;\n /** D2: all three true; accounted METERED (DESIGN 3.7) */\n anthropicSubscriptionViaPi?: {\n enabled: boolean;\n acknowledgePerTokenBilling: boolean;\n acknowledgeProviderTermsRisk: boolean;\n };\n };\n routing: {\n allowedProviders: string[];\n defaults: Partial<Record<CohorteRole, ModelCapability>>;\n tiers: Partial<Record<ModelCapability, TierTarget>>;\n escalation: EscalationPolicy;\n fallback: { enabled: boolean };\n };\n budgets: {\n run: BudgetCounters;\n phase: BudgetCounters;\n agent: BudgetCounters;\n provider: Record<string, BudgetCounters>;\n tool: Record<string, BudgetCounters>;\n /** 3 */\n concurrency: number;\n /** 5 */\n maxIncarnations: number;\n };\n loop: {\n maxFixRounds: number;\n noProgressWindow: number;\n maxDeniedCallsPerAgent: number;\n leftovers: Record<'major' | 'minor' | 'info', 'fix' | 'park' | 'ask'>;\n };\n /** argv arrays */\n checks: { typecheck?: string[]; lint?: string[]; test?: string[]; timeoutMs: number };\n provision: {\n argv?: string[];\n network: boolean;\n cacheDirs: string[];\n lockfiles: string[];\n env: Partial<Record<ProvisionEnvName, string>>;\n // default: every node_modules directory; read-only for agent commands and checks\n dependencyDirs: string[];\n /** writable, excluded from the dependency manifest, wiped before every check sequence */\n writableCaches: string[];\n };\n policy: {\n commands: { allow: CommandRule[]; ask: CommandRule[]; deny: CommandRule[] };\n dangerousCommands: CommandRule[];\n symlinks: SymlinkPolicy;\n approvals: ApprovalsConfig;\n /** default ask: an in-doubt effect needs a human ack before the agent continues (DESIGN 4.4) */\n inDoubt: 'ask' | 'continue';\n skip: ActivePipelineState[];\n steer: { enabled: boolean };\n admin: { runTool: boolean };\n quota: { autoResume: boolean };\n };\n host: { idleExitMinutes: number; pauseKeepAliveMinutes: number; pollMs: number };\n /** forward HTTP(S)_PROXY to the brain; default false */\n network: { proxyEnv: boolean };\n /** `require` absent = the computed default of DESIGN 2.6.6 */\n sandbox: { require?: 'native' | 'best-effort'; brain: 'os-if-available' | 'os' | 'process' };\n git: {\n worktreeRoot?: string;\n branchPrefix: string;\n commitIdentity: 'user' | 'cohorte';\n keepWorktrees: 'on-failure' | 'always' | 'never';\n };\n retention: {\n transcriptsDays: number;\n eventsDays: number | 'forever';\n artifactsDays: number;\n compressAfterDays: number;\n };\n /** default false; `true` is rejected by the LOADER in 3.0 (configuration/telemetry-remote-unavailable, ADR-0013) */\n telemetry: { remote: boolean };\n integrations?: { obsidian?: { vaultPath: string; board: string } };\n}\n\nconst oneOf = <const V extends readonly string[]>(values: V): TUnsafe<V[number]> =>\n Type.Unsafe<V[number]>({ type: 'string', enum: [...values] });\nconst count = (minimum = 0) => Type.Integer({ minimum });\nconst argv = () => Type.Array(Type.String(), { minItems: 1 });\nconst strings = () => Type.Array(Type.String({ minLength: 1 }));\nconst rules = () => Type.Array(CommandRule);\n// Explicit return types: a `Static<TRecord>` that Biome has to infer overflows its stack (docs/v3/requests/U0.03.md R1).\nconst budgetMap = (): TUnsafe<Record<string, BudgetCounters>> =>\n Type.Unsafe<Record<string, BudgetCounters>>(Type.Record(Type.String(), BudgetCounters));\nconst roleDefaults = (): TUnsafe<Partial<Record<CohorteRole, ModelCapability>>> =>\n Type.Unsafe<Partial<Record<CohorteRole, ModelCapability>>>(Type.Record(Type.String(), ModelCapability));\nconst tier = () => Type.Optional(Type.Object({ ref: ModelRef, thinking: ThinkingLevel }, closed));\nconst envValue = () => Type.Optional(Type.String({ minLength: 1 }));\nconst leftover = () => oneOf(['fix', 'park', 'ask']);\n\nconst CohorteConfigSchema = Type.Object(\n {\n schemaVersion: Type.Literal(1),\n project: Type.Object(\n {\n id: Type.String({ pattern: ID_PATTERN }),\n defaultBranch: Type.String({ minLength: 1 }),\n protectedBranches: strings(),\n },\n closed,\n ),\n runtime: Type.Object(\n {\n id: Type.String({ minLength: 1 }),\n pi: Type.Optional(Type.Object({ loadFrom: oneOf(['package', 'bundle']) }, closed)),\n },\n closed,\n ),\n authentication: Type.Object(\n {\n mode: AuthMode,\n allowApiKeys: Type.Boolean(),\n anthropicSubscriptionViaPi: Type.Optional(\n Type.Object(\n {\n enabled: Type.Boolean(),\n acknowledgePerTokenBilling: Type.Boolean(),\n acknowledgeProviderTermsRisk: Type.Boolean(),\n },\n closed,\n ),\n ),\n },\n closed,\n ),\n routing: Type.Object(\n {\n allowedProviders: strings(),\n defaults: roleDefaults(),\n tiers: Type.Object({ fast: tier(), coding: tier(), reasoning: tier(), vision: tier(), cheap: tier() }, closed),\n escalation: EscalationPolicy,\n fallback: Type.Object({ enabled: Type.Boolean() }, closed),\n },\n closed,\n ),\n budgets: Type.Object(\n {\n run: BudgetCounters,\n phase: BudgetCounters,\n agent: BudgetCounters,\n provider: budgetMap(),\n tool: budgetMap(),\n concurrency: count(1),\n maxIncarnations: count(1),\n },\n closed,\n ),\n loop: Type.Object(\n {\n maxFixRounds: count(),\n noProgressWindow: count(1),\n maxDeniedCallsPerAgent: count(),\n leftovers: Type.Object({ major: leftover(), minor: leftover(), info: leftover() }, closed),\n },\n closed,\n ),\n checks: Type.Object(\n {\n typecheck: Type.Optional(argv()),\n lint: Type.Optional(argv()),\n test: Type.Optional(argv()),\n timeoutMs: count(1),\n },\n closed,\n ),\n provision: Type.Object(\n {\n argv: Type.Optional(argv()),\n network: Type.Boolean(),\n cacheDirs: strings(),\n lockfiles: strings(),\n env: Type.Object(\n {\n npm_config_store_dir: envValue(),\n npm_config_cache: envValue(),\n YARN_CACHE_FOLDER: envValue(),\n COREPACK_HOME: envValue(),\n },\n closed,\n ),\n dependencyDirs: strings(),\n writableCaches: strings(),\n },\n closed,\n ),\n policy: Type.Object(\n {\n commands: Type.Object({ allow: rules(), ask: rules(), deny: rules() }, closed),\n dangerousCommands: rules(),\n symlinks: SymlinkPolicy,\n approvals: Type.Object(\n {\n unattended: oneOf(['deny', 'wait']),\n expiryMinutes: Type.Optional(count(1)),\n parkAfterMinutes: count(),\n ship: oneOf(['human', 'auto']),\n notify: Type.Boolean(),\n autoResume: Type.Boolean(),\n },\n closed,\n ),\n inDoubt: oneOf(['ask', 'continue']),\n skip: Type.Array(ActivePipelineState),\n steer: Type.Object({ enabled: Type.Boolean() }, closed),\n admin: Type.Object({ runTool: Type.Boolean() }, closed),\n quota: Type.Object({ autoResume: Type.Boolean() }, closed),\n },\n closed,\n ),\n host: Type.Object({ idleExitMinutes: count(), pauseKeepAliveMinutes: count(), pollMs: count(1) }, closed),\n network: Type.Object({ proxyEnv: Type.Boolean() }, closed),\n sandbox: Type.Object(\n {\n require: Type.Optional(oneOf(['native', 'best-effort'])),\n brain: oneOf(['os-if-available', 'os', 'process']),\n },\n closed,\n ),\n git: Type.Object(\n {\n worktreeRoot: Type.Optional(Type.String({ minLength: 1 })),\n branchPrefix: Type.String({ minLength: 1 }),\n commitIdentity: oneOf(['user', 'cohorte']),\n keepWorktrees: oneOf(['on-failure', 'always', 'never']),\n },\n closed,\n ),\n retention: Type.Object(\n {\n transcriptsDays: count(),\n eventsDays: Type.Union([count(), Type.Literal('forever')]),\n artifactsDays: count(),\n compressAfterDays: count(),\n },\n closed,\n ),\n telemetry: Type.Object({ remote: Type.Boolean() }, closed),\n integrations: Type.Optional(\n Type.Object(\n {\n obsidian: Type.Optional(\n Type.Object({ vaultPath: Type.String({ minLength: 1 }), board: Type.String({ minLength: 1 }) }, closed),\n ),\n },\n closed,\n ),\n ),\n },\n closed,\n);\n\n/** [S]. Annotated on purpose: Biome must not infer this type (docs/v3/requests/U0.02.md R1, U0.05.md R1). */\nexport const CohorteConfig: TUnsafe<CohorteConfig> = Type.Unsafe<CohorteConfig>(CohorteConfigSchema);\n\nfunction deepFreeze<T>(value: T): T {\n if (typeof value === 'object' && value !== null && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const member of Object.values(value)) deepFreeze(member);\n }\n return value;\n}\n\nconst MINUTE = 60_000;\n\n/**\n * The lowest layer of the load order (DESIGN 2.10): shipped defaults < user file < project file < CLI flags.\n * `project.id` is a placeholder that `cohorte init` always replaces. DESIGN gives the numbers it cares about\n * (concurrency 3, maxIncarnations 5, loop 5/3/5, host 30/30/250, parkAfterMinutes 10); the budget and retention\n * figures are provisional defaults of this file.\n */\nexport const DEFAULT_CONFIG: CohorteConfig = deepFreeze<CohorteConfig>({\n schemaVersion: 1,\n project: { id: 'project', defaultBranch: 'main', protectedBranches: ['main', 'master'] },\n runtime: { id: 'pi', pi: { loadFrom: 'package' } },\n authentication: { mode: 'subscription', allowApiKeys: false },\n routing: {\n allowedProviders: ['openai-codex'],\n defaults: { implementer: 'coding', fixer: 'coding', reviewer: 'reasoning', 'security-reviewer': 'reasoning' },\n tiers: {\n coding: { ref: { provider: 'openai-codex', model: 'gpt-5.5', capability: 'coding' }, thinking: 'medium' },\n reasoning: { ref: { provider: 'openai-codex', model: 'gpt-5.5', capability: 'reasoning' }, thinking: 'high' },\n fast: { ref: { provider: 'openai-codex', model: 'gpt-5.4-mini', capability: 'fast' }, thinking: 'low' },\n cheap: { ref: { provider: 'openai-codex', model: 'gpt-5.4-mini', capability: 'cheap' }, thinking: 'low' },\n },\n escalation: { sameFailureCount: 2, ladder: [{ kind: 'human' }], maxPerRun: 2 },\n fallback: { enabled: false },\n },\n budgets: {\n run: { modelRequests: 2000, toolCalls: 10_000, wallClockMs: 360 * MINUTE },\n phase: { wallClockMs: 120 * MINUTE },\n agent: { modelRequests: 200, toolCalls: 400, wallClockMs: 45 * MINUTE, contextTokens: 200_000 },\n provider: {},\n tool: {},\n concurrency: 3,\n maxIncarnations: 5,\n },\n loop: {\n maxFixRounds: 5,\n noProgressWindow: 3,\n maxDeniedCallsPerAgent: 5,\n leftovers: { major: 'fix', minor: 'park', info: 'park' },\n },\n checks: { timeoutMs: 10 * MINUTE },\n provision: {\n network: false,\n cacheDirs: [],\n lockfiles: ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock'],\n env: {},\n dependencyDirs: ['**/node_modules'],\n writableCaches: ['node_modules/.cache', 'node_modules/.vite', 'node_modules/.vitest', 'node_modules/.vite-temp'],\n },\n policy: {\n commands: { allow: [], ask: [], deny: [] },\n dangerousCommands: [],\n symlinks: { ...DEFAULT_SYMLINK_POLICY },\n approvals: { unattended: 'deny', parkAfterMinutes: 10, ship: 'human', notify: false, autoResume: false },\n inDoubt: 'ask',\n skip: [],\n steer: { enabled: false },\n admin: { runTool: false },\n quota: { autoResume: false },\n },\n host: { idleExitMinutes: 30, pauseKeepAliveMinutes: 30, pollMs: 250 },\n network: { proxyEnv: false },\n sandbox: { brain: 'os-if-available' },\n git: { branchPrefix: 'cohorte/', commitIdentity: 'user', keepWorktrees: 'on-failure' },\n retention: { transcriptsDays: 30, eventsDays: 'forever', artifactsDays: 90, compressAfterDays: 7 },\n telemetry: { remote: false },\n});\n","// `.cohorte/manifest.yaml` (DESIGN 2.10): the detection marker and the previous-hash guard of spec 14.\nimport { Sha256 } from '@cohorte/base';\nimport { type Static, Type } from 'typebox';\n\nconst closed = { additionalProperties: false } as const;\n\nexport const GeneratedFile = Type.Object(\n {\n /** `.cohorte`-relative POSIX path */\n path: Type.String({ minLength: 1 }),\n templateId: Type.String({ minLength: 1 }),\n templateSha256: Sha256,\n /** a generated file is replaced only if its CURRENT hash equals this one */\n renderedSha256: Sha256,\n },\n closed,\n);\nexport type GeneratedFile = Static<typeof GeneratedFile>;\n\n/** [S] */\nexport const Manifest = Type.Object(\n {\n schemaVersion: Type.Literal(1),\n cohorteVersion: Type.String({ minLength: 1 }),\n createdWith: Type.String({ minLength: 1 }),\n protocol: Type.Object({ min: Type.String({ minLength: 1 }), max: Type.String({ minLength: 1 }) }, closed),\n stateSchemaVersion: Type.Integer({ minimum: 0 }),\n generated: Type.Array(GeneratedFile),\n },\n closed,\n);\nexport type Manifest = Static<typeof Manifest>;\n","// `.cohorte/ownership.yaml` (DESIGN 2.10): surfaces, their paths, owners and reviewers.\nimport { ID_PATTERN } from '@cohorte/base';\nimport { type TUnsafe, Type } from 'typebox';\n\nconst closed = { additionalProperties: false } as const;\n\n/** The one surface whose paths may overlap any other surface's (DESIGN 2.10, 5.6). */\nexport const SHARED_SURFACE_ID = 'shared';\n\nexport interface Surface {\n /** worktree-relative POSIX globs */\n paths: string[];\n /** role ids allowed to WRITE the surface */\n owners: string[];\n reviewers: string[];\n /** a write to one of these paths is an `ask` (DESIGN 5.6) */\n approval?: 'human';\n}\nexport interface Ownership {\n surfaces: Record<string, Surface>;\n}\n\nconst names = () => Type.Array(Type.String({ minLength: 1 }));\nconst SurfaceSchema = Type.Object(\n {\n paths: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),\n owners: names(),\n reviewers: names(),\n approval: Type.Optional(Type.Literal('human')),\n },\n closed,\n);\nconst OwnershipSchema = Type.Object(\n { surfaces: Type.Record(Type.String({ pattern: ID_PATTERN }), SurfaceSchema) },\n closed,\n);\n\n/** [S]. Annotated: a `Static<TRecord>` inferred by Biome overflows its stack (docs/v3/requests/U0.03.md R1). */\nexport const Ownership: TUnsafe<Ownership> = Type.Unsafe<Ownership>(OwnershipSchema);\n\nconst GLOB_META = /[*?[\\]{}()!]/;\n\n/** The literal prefix of a glob, by path SEGMENT: the segments before the first one holding a glob metacharacter (DESIGN 5.6). */\nexport function literalPrefixOf(pattern: string): string[] {\n const prefix: string[] = [];\n for (const segment of pattern.split('/')) {\n if (segment === '' || segment === '.') continue;\n if (GLOB_META.test(segment)) break;\n prefix.push(segment);\n }\n return prefix;\n}\n\nconst isSegmentAncestorOrEqual = (a: readonly string[], b: readonly string[]): boolean =>\n a.length <= b.length && a.every((segment, index) => segment === b[index]);\n\n/** Two patterns overlap when one literal prefix is a segment-ancestor of (or equal to) the other; no literal prefix overlaps everything. */\nexport function patternsOverlap(a: string, b: string): boolean {\n const left = literalPrefixOf(a);\n const right = literalPrefixOf(b);\n return isSegmentAncestorOrEqual(left, right) || isSegmentAncestorOrEqual(right, left);\n}\n\nexport interface OwnershipProblem {\n /** JSON pointer into the ownership document */\n path: string;\n message: string;\n}\n\n/**\n * The rule a JSON Schema cannot say: every surface path is DISJOINT from the paths of every other surface, unless\n * one of the two surfaces is `shared`. Conservative on purpose (prefix overlap, not glob intersection): a false\n * \"overlap\" costs an edit of ownership.yaml, a missed one lets two agents write the same file.\n */\nexport function ownershipProblems(ownership: Ownership): OwnershipProblem[] {\n const problems: OwnershipProblem[] = [];\n const entries = Object.entries(ownership.surfaces).filter(([id]) => id !== SHARED_SURFACE_ID);\n entries.forEach(([id, surface], at) => {\n for (const [otherId, other] of entries.slice(at + 1)) {\n surface.paths.forEach((path, index) => {\n const clash = other.paths.find((candidate) => patternsOverlap(path, candidate));\n if (clash !== undefined) {\n problems.push({\n path: `/surfaces/${id}/paths/${index}`,\n message: `\"${path}\" of surface \"${id}\" overlaps \"${clash}\" of surface \"${otherId}\": make them disjoint or move the path to the \"${SHARED_SURFACE_ID}\" surface`,\n });\n }\n });\n }\n });\n return problems;\n}\n","// `.cohorte/skills/<id>/skill.yaml` (DESIGN 2.10, ADR-0011). Spec 8 with ONE change: `checks[].command`, a shell\n// string, becomes `argv` (D-25, I3). Checks are declarative in V3.0: never run, and they create no CommandRule.\nimport { ID_PATTERN } from '@cohorte/base';\nimport { type Static, Type } from 'typebox';\n\nconst closed = { additionalProperties: false } as const;\nconst names = () => Type.Optional(Type.Array(Type.String({ minLength: 1 })));\n\n/** [S] schemas/skill.schema.json */\nexport const SkillManifest = Type.Object(\n {\n id: Type.String({ pattern: ID_PATTERN }),\n version: Type.String({ minLength: 1 }),\n /** matched against the Project Model, deterministically */\n appliesWhen: Type.Object(\n { languages: names(), frameworks: names(), packageManagers: names(), roles: names() },\n closed,\n ),\n /** relative path of the Markdown, inside the skill directory */\n prompt: Type.String({ minLength: 1 }),\n checks: Type.Optional(\n Type.Array(\n Type.Object(\n { name: Type.Optional(Type.String({ minLength: 1 })), argv: Type.Array(Type.String(), { minItems: 1 }) },\n closed,\n ),\n ),\n ),\n /** reserved (ADR-0011): no signature is verified in V3.0 */\n signature: Type.Optional(Type.String()),\n /** reserved (ADR-0011) */\n source: Type.Optional(Type.String()),\n },\n closed,\n);\nexport type SkillManifest = Static<typeof SkillManifest>;\n","// `.cohorte/specs/<id>.yaml` (DESIGN 2.10): a feature or a patch, draft or frozen; immutable once frozen.\nimport { canonicalJson, ID_PATTERN, type JsonValue, Sha256, sha256Hex } from '@cohorte/base';\nimport { type TUnsafe, Type } from 'typebox';\n\nconst closed = { additionalProperties: false } as const;\n\ninterface SpecBody {\n id: string;\n kind: 'feature' | 'patch';\n title: string;\n acceptance: string[];\n surfaces: Record<string, { tasks: string[] }>;\n /** worktree-relative path of the contract file */\n contract?: string;\n openQuestions: string[];\n patch?: { repro: string; regressionTest: string; causeConfirmed: boolean };\n}\nexport interface DraftSpec extends SpecBody {\n status: 'draft';\n}\nexport interface FrozenSpec extends SpecBody {\n status: 'frozen';\n /** sha256 of the canonical JSON of the spec WITHOUT `status` and `sha256`: see {@link specContentSha256} */\n sha256: Sha256;\n}\nexport type Spec = DraftSpec | FrozenSpec;\n\nconst texts = () => Type.Array(Type.String({ minLength: 1 }));\nconst body = {\n id: Type.String({ pattern: ID_PATTERN }),\n kind: Type.Union([Type.Literal('feature'), Type.Literal('patch')]),\n title: Type.String({ minLength: 1 }),\n acceptance: texts(),\n surfaces: Type.Record(Type.String({ pattern: ID_PATTERN }), Type.Object({ tasks: texts() }, closed)),\n contract: Type.Optional(Type.String({ minLength: 1 })),\n openQuestions: texts(),\n patch: Type.Optional(\n Type.Object({ repro: Type.String(), regressionTest: Type.String(), causeConfirmed: Type.Boolean() }, closed),\n ),\n};\nconst SpecSchema = Type.Union([\n Type.Object({ ...body, status: Type.Literal('draft') }, closed),\n Type.Object({ ...body, status: Type.Literal('frozen'), sha256: Sha256 }, closed),\n]);\n\n/** [S]. A frozen spec WITHOUT a sha256, and a draft WITH one, are both invalid. */\nexport const Spec: TUnsafe<Spec> = Type.Unsafe<Spec>(SpecSchema);\n\n/** What freezing signs: everything a human wrote, nothing the freeze itself adds. */\nexport function specContentSha256(spec: Spec): Sha256 {\n const content: Record<string, JsonValue> = {};\n for (const [key, value] of Object.entries(spec)) {\n if (key !== 'status' && key !== 'sha256' && value !== undefined) content[key] = value as JsonValue;\n }\n return sha256Hex(canonicalJson(content));\n}\n\nexport interface SpecProblem {\n path: string;\n message: string;\n}\n\n/** A frozen spec rejects edits: its content must still hash to the sha256 it was frozen with. A draft has nothing to prove. */\nexport function frozenSpecProblems(spec: Spec): SpecProblem[] {\n if (spec.status !== 'frozen') return [];\n const actual = specContentSha256(spec);\n if (actual === spec.sha256) return [];\n return [\n {\n path: '/sha256',\n message: `spec \"${spec.id}\" is frozen and was edited: content hashes to ${actual}, frozen as ${spec.sha256}. Revert the edit, or write a new spec`,\n },\n ];\n}\n","// What the repository may decide, and what only the local user may decide (DESIGN 2.10.1, ADR-0026): the key-trust\n// classification as frozen DATA, the trust record, and the TrustStore PORT that `security` implements and the\n// loader consumes (`config` may not import `security`).\nimport { IsoInstant, type JsonValue, Sha256 } from '@cohorte/base';\nimport { type TUnsafe, Type } from 'typebox';\n\nexport const TRUST_CLASSES = ['tighten-only', 'loosen', 'neutral'] as const;\nexport type TrustClass = (typeof TRUST_CLASSES)[number];\n\nexport interface ConfigKeyTrustRule {\n /**\n * JSON pointer into `CohorteConfig`. A rule covers the key it names AND its whole subtree. Rules are prefix-disjoint:\n * no pointer is an ancestor of another one, so a key has exactly one class.\n */\n pointer: string;\n class: TrustClass;\n /**\n * Only on a `loosen` rule whose class depends on the VALUE (DESIGN 2.10.1: `sandbox.require: best-effort`,\n * `policy.symlinks` towards `allow`, ...): the values that loosen. Every other value of the key is `tighten-only`.\n */\n loosenOnly?: readonly JsonValue[];\n}\n\nconst tighten = (pointer: string): ConfigKeyTrustRule => Object.freeze({ pointer, class: 'tighten-only' });\nconst neutral = (pointer: string): ConfigKeyTrustRule => Object.freeze({ pointer, class: 'neutral' });\nconst loosen = (pointer: string, ...loosenOnly: JsonValue[]): ConfigKeyTrustRule =>\n Object.freeze(\n loosenOnly.length === 0\n ? { pointer, class: 'loosen' }\n : { pointer, class: 'loosen', loosenOnly: Object.freeze(loosenOnly) },\n );\n\n/** DESIGN 2.10.1, as data. TOTAL over `CohorteConfig` (a unit test enumerates the schema). */\nexport const CONFIG_KEY_TRUST: readonly ConfigKeyTrustRule[] = Object.freeze([\n neutral('/schemaVersion'),\n neutral('/project'),\n neutral('/runtime/id'),\n loosen('/runtime/pi'),\n neutral('/authentication/mode'),\n loosen('/authentication/allowApiKeys'),\n loosen('/authentication/anthropicSubscriptionViaPi'),\n loosen('/routing/allowedProviders'),\n neutral('/routing/defaults'),\n neutral('/routing/tiers'),\n neutral('/routing/escalation'),\n loosen('/routing/fallback'),\n tighten('/budgets'),\n tighten('/loop'),\n loosen('/checks'),\n loosen('/provision/argv'),\n loosen('/provision/network'),\n loosen('/provision/cacheDirs'),\n neutral('/provision/lockfiles'),\n loosen('/provision/env'),\n loosen('/provision/dependencyDirs'),\n loosen('/provision/writableCaches'),\n loosen('/policy/commands/allow'),\n tighten('/policy/commands/ask'),\n tighten('/policy/commands/deny'),\n loosen('/policy/dangerousCommands'),\n loosen('/policy/symlinks/mode', 'allow'),\n loosen('/policy/symlinks/hardlinksOnWrite', 'allow'),\n loosen('/policy/approvals/unattended', 'wait'),\n tighten('/policy/approvals/expiryMinutes'),\n tighten('/policy/approvals/parkAfterMinutes'),\n loosen('/policy/approvals/ship', 'auto'),\n neutral('/policy/approvals/notify'),\n loosen('/policy/approvals/autoResume'),\n loosen('/policy/inDoubt', 'continue'),\n loosen('/policy/skip'),\n loosen('/policy/steer'),\n loosen('/policy/admin'),\n neutral('/policy/quota'),\n neutral('/host'),\n loosen('/network/proxyEnv'),\n loosen('/sandbox/require', 'best-effort'),\n loosen('/sandbox/brain', 'process'),\n loosen('/git/worktreeRoot'),\n neutral('/git/branchPrefix'),\n neutral('/git/commitIdentity'),\n neutral('/git/keepWorktrees'),\n tighten('/retention'),\n neutral('/telemetry'),\n neutral('/integrations'),\n]);\n\nconst covers = (rule: ConfigKeyTrustRule, pointer: string): boolean =>\n pointer === rule.pointer || pointer.startsWith(`${rule.pointer}/`);\n\n/** Every rule that covers `pointer`. The data is total and prefix-disjoint, so this has exactly one member for a real key. */\nexport function trustRulesFor(pointer: string): ConfigKeyTrustRule[] {\n return CONFIG_KEY_TRUST.filter((rule) => covers(rule, pointer));\n}\n\n/**\n * The class of one key of the PROJECT file. `value` decides for the keys classed by value; without it such a key is\n * reported `loosen` (fail closed). An unknown key is `loosen` too: nothing unclassified is ever honoured silently.\n */\nexport function trustClassOf(pointer: string, value?: JsonValue): TrustClass {\n const [rule, ...others] = trustRulesFor(pointer);\n if (rule === undefined || others.length > 0) return 'loosen';\n if (rule.class !== 'loosen' || rule.loosenOnly === undefined || value === undefined) return rule.class;\n if (pointer !== rule.pointer) return 'loosen';\n return rule.loosenOnly.some((candidate) => candidate === value) ? 'loosen' : 'tighten-only';\n}\n\n/** One file per project: `~/.cohorte/trust/<projectKeyId>.json`, `0600` in a `0700` directory, a protected root. */\nexport interface TrustRecord {\n /** sha256(canonicalJson(the project file's values for every loosen-class key + ownership.yaml)) */\n policySha256: Sha256;\n /** JSON pointers of the loosening keys the user saw and accepted */\n loosenedKeys: string[];\n grantedAt: IsoInstant;\n /** a label for the audit trail (the OS user name); the MAC proves \"a key holder\", never which one (ADR-0022) */\n grantedBy: string;\n /** HMAC-SHA256 with the project key over the canonical JSON of the record minus `mac` */\n mac: string;\n}\n\n/**\n * [S]. The type is written by hand and the const annotated: a `Promise<Static<...>>` that Biome has to resolve for\n * nursery/noFloatingPromises overflows its stack, and a crashed Biome exits 0 (docs/v3/requests/U0.02.md R1).\n */\nexport const TrustRecord: TUnsafe<TrustRecord> = Type.Unsafe<TrustRecord>(\n Type.Object(\n {\n policySha256: Sha256,\n loosenedKeys: Type.Array(Type.String()),\n grantedAt: IsoInstant,\n grantedBy: Type.String({ minLength: 1 }),\n mac: Type.String({ minLength: 1 }),\n },\n { additionalProperties: false },\n ),\n);\n\nexport interface TrustGrant {\n policySha256: Sha256;\n loosenedKeys: string[];\n grantedBy: string;\n}\n\n/**\n * PORT (file implementation: `@cohorte/security/auth`, `createTrustStore`). A record with a bad MAC or wrong file\n * modes is ABSENT: `lookup` fails closed.\n */\nexport interface TrustStore {\n /** The record, only if it is authentic AND was granted for exactly this policy hash. */\n lookup(projectKeyId: string, policySha256: Sha256): Promise<TrustRecord | undefined>;\n grant(projectKeyId: string, grant: TrustGrant): Promise<TrustRecord>;\n /** false = there was nothing to revoke */\n revoke(projectKeyId: string): Promise<boolean>;\n}\n"],"mappings":"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"}
@@ -0,0 +1,140 @@
1
+ import { n as moveConfiguredCard } from "./obsidian-BHLkXx_H.mjs";
2
+ import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
3
+ import { join, resolve } from "node:path";
4
+ import { execFile } from "node:child_process";
5
+ import { promisify } from "node:util";
6
+ //#region src/commands/ship/index.ts
7
+ const exec = promisify(execFile);
8
+ function markShipped(source) {
9
+ if (/^status:\s*shipped\s*$/mu.test(source)) return source;
10
+ return source.replace(/^status:\s*\S+\s*$/mu, "status: shipped");
11
+ }
12
+ const ship = {
13
+ verb: "ship",
14
+ async run(ctx, args) {
15
+ const [runId, approvalId] = args.positionals;
16
+ if (!runId) return 2;
17
+ if (!runId.startsWith("run_") && !approvalId) {
18
+ const specPath = resolve(ctx.cwd, "specs", `${runId}.md`);
19
+ let spec = "";
20
+ try {
21
+ spec = await readFile(specPath, "utf8");
22
+ } catch {
23
+ ctx.stdio.stderr.write(`frozen spec not found: ${specPath}\n`);
24
+ return 1;
25
+ }
26
+ if (!/^status:\s*frozen\s*$/mu.test(spec.slice(0, 1500))) {
27
+ ctx.stdio.stderr.write("ship requires a frozen spec\n");
28
+ return 11;
29
+ }
30
+ const reports = join(ctx.cwd, "specs", "reports");
31
+ let verdict = false;
32
+ let verdictMtime = 0;
33
+ try {
34
+ for (const name of await readdir(reports)) {
35
+ if (!name.includes(runId)) continue;
36
+ const text = await readFile(join(reports, name), "utf8");
37
+ if (/\bSHIP\b/u.test(text)) {
38
+ verdict = true;
39
+ verdictMtime = Math.max(verdictMtime, (await stat(join(reports, name))).mtimeMs);
40
+ }
41
+ }
42
+ } catch {}
43
+ if (!verdict) {
44
+ ctx.stdio.stderr.write("ship requires a durable SHIP review verdict\n");
45
+ return 11;
46
+ }
47
+ if (verdictMtime < (await stat(specPath)).mtimeMs) {
48
+ ctx.stdio.stderr.write("ship requires a fresh SHIP review verdict\n");
49
+ return 11;
50
+ }
51
+ const { stdout: branch } = await exec("git", ["branch", "--show-current"], { cwd: ctx.cwd });
52
+ if (branch.trim() === "main" || branch.trim() === "master") {
53
+ ctx.stdio.stderr.write("ship requires a feature branch\n");
54
+ return 11;
55
+ }
56
+ const { stdout: status } = await exec("git", [
57
+ "status",
58
+ "--porcelain",
59
+ "--",
60
+ ".",
61
+ ":(exclude).cohorte"
62
+ ], { cwd: ctx.cwd });
63
+ const { stdout: diffStat } = await exec("git", ["diff", "--stat"], { cwd: ctx.cwd });
64
+ const reportPath = join(reports, `${runId}-release.md`);
65
+ await mkdir(reports, { recursive: true });
66
+ await writeFile(reportPath, `# Release — ${runId}\n\n- generated: ${ctx.clock.now()}\n- branch: ${branch.trim()}\n- clean: ${status.trim() === ""}\n\n## Diff\n\n${diffStat.trim() || "(empty)"}\n\n## Handoff\n\nReview the branch and run the repository release procedure.\n`, "utf8");
67
+ if (!args.positionals.includes("--apply")) {
68
+ ctx.stdio.stdout.write(`${JSON.stringify({
69
+ feature: runId,
70
+ reportPath,
71
+ branch: branch.trim(),
72
+ clean: status.trim() === ""
73
+ })}\n`);
74
+ return status.trim() === "" ? 0 : 11;
75
+ }
76
+ const specSource = await readFile(specPath, "utf8");
77
+ await moveConfiguredCard(ctx.env.HOME ?? ctx.cwd, runId, "ship");
78
+ await writeFile(specPath, markShipped(specSource), "utf8");
79
+ await exec("git", [
80
+ "add",
81
+ "-A",
82
+ "--",
83
+ ":!.cohorte"
84
+ ], { cwd: ctx.cwd });
85
+ await exec("git", [
86
+ "commit",
87
+ "-m",
88
+ `cohorte(${runId}): ship`
89
+ ], { cwd: ctx.cwd });
90
+ await exec("git", [
91
+ "push",
92
+ "origin",
93
+ branch.trim()
94
+ ], { cwd: ctx.cwd });
95
+ const { stdout: prUrl } = await exec("gh", [
96
+ "pr",
97
+ "create",
98
+ "--base",
99
+ "main",
100
+ "--head",
101
+ branch.trim(),
102
+ "--title",
103
+ `cohorte(${runId}): ship`,
104
+ "--body",
105
+ `Automated Cohorte V3 ship for ${runId}.\n\nReview report: ${reportPath}`
106
+ ], { cwd: ctx.cwd });
107
+ const pr = /\/pull\/(\d+)/u.exec(prUrl)?.[1];
108
+ await moveConfiguredCard(ctx.env.HOME ?? ctx.cwd, runId, "shipped", pr ? `${runId} — PR #${pr}` : runId);
109
+ if (args.positionals.includes("--watch")) await exec("gh", [
110
+ "pr",
111
+ "checks",
112
+ prUrl.trim(),
113
+ "--watch"
114
+ ], {
115
+ cwd: ctx.cwd,
116
+ maxBuffer: 2e6
117
+ });
118
+ ctx.stdio.stdout.write(`${JSON.stringify({
119
+ feature: runId,
120
+ reportPath,
121
+ branch: branch.trim(),
122
+ clean: false,
123
+ prUrl: prUrl.trim()
124
+ })}\n`);
125
+ return 0;
126
+ }
127
+ if (!approvalId) return 2;
128
+ const result = await ctx.controller.send("approve", {
129
+ approvalId,
130
+ scope: "run",
131
+ answer: "ship"
132
+ }, { runId });
133
+ ctx.stdio.stdout.write(`${JSON.stringify(result)}\n`);
134
+ return result.status === "rejected" ? 3 : result.status === "pending" ? 4 : 0;
135
+ }
136
+ };
137
+ //#endregion
138
+ export { ship as default };
139
+
140
+ //# sourceMappingURL=ship-BizfUe62.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ship-BizfUe62.mjs","names":[],"sources":["../../../../apps/cli/src/commands/ship/index.ts"],"sourcesContent":["// apps/cli/src/commands/ship/index.ts — DESIGN §9 verb `ship` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/ship/**`.\nimport { execFile } from 'node:child_process';\nimport { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { CommandModule } from '../../contract/index.ts';\nimport { moveConfiguredCard } from '../obsidian/index.ts';\n\nconst exec = promisify(execFile);\n\nfunction markShipped(source: string): string {\n if (/^status:\\s*shipped\\s*$/mu.test(source)) return source;\n return source.replace(/^status:\\s*\\S+\\s*$/mu, 'status: shipped');\n}\n\nconst ship: CommandModule = {\n verb: 'ship',\n async run(ctx, args) {\n const [runId, approvalId] = args.positionals;\n if (!runId) return 2;\n if (!runId.startsWith('run_') && !approvalId) {\n const specPath = resolve(ctx.cwd, 'specs', `${runId}.md`);\n let spec = '';\n try {\n spec = await readFile(specPath, 'utf8');\n } catch {\n ctx.stdio.stderr.write(`frozen spec not found: ${specPath}\\n`);\n return 1;\n }\n if (!/^status:\\s*frozen\\s*$/mu.test(spec.slice(0, 1500))) {\n ctx.stdio.stderr.write('ship requires a frozen spec\\n');\n return 11;\n }\n const reports = join(ctx.cwd, 'specs', 'reports');\n let verdict = false;\n let verdictMtime = 0;\n try {\n for (const name of await readdir(reports)) {\n if (!name.includes(runId)) continue;\n const text = await readFile(join(reports, name), 'utf8');\n if (/\\bSHIP\\b/u.test(text)) {\n verdict = true;\n verdictMtime = Math.max(verdictMtime, (await stat(join(reports, name))).mtimeMs);\n }\n }\n } catch {}\n if (!verdict) {\n ctx.stdio.stderr.write('ship requires a durable SHIP review verdict\\n');\n return 11;\n }\n if (verdictMtime < (await stat(specPath)).mtimeMs) {\n ctx.stdio.stderr.write('ship requires a fresh SHIP review verdict\\n');\n return 11;\n }\n const { stdout: branch } = await exec('git', ['branch', '--show-current'], { cwd: ctx.cwd });\n if (branch.trim() === 'main' || branch.trim() === 'master') {\n ctx.stdio.stderr.write('ship requires a feature branch\\n');\n return 11;\n }\n const { stdout: status } = await exec('git', ['status', '--porcelain', '--', '.', ':(exclude).cohorte'], {\n cwd: ctx.cwd,\n });\n const { stdout: diffStat } = await exec('git', ['diff', '--stat'], { cwd: ctx.cwd });\n const reportPath = join(reports, `${runId}-release.md`);\n await mkdir(reports, { recursive: true });\n await writeFile(\n reportPath,\n `# Release — ${runId}\\n\\n- generated: ${ctx.clock.now()}\\n- branch: ${branch.trim()}\\n- clean: ${status.trim() === ''}\\n\\n## Diff\\n\\n${diffStat.trim() || '(empty)'}\\n\\n## Handoff\\n\\nReview the branch and run the repository release procedure.\\n`,\n 'utf8',\n );\n const apply = args.positionals.includes('--apply');\n if (!apply) {\n ctx.stdio.stdout.write(\n `${JSON.stringify({ feature: runId, reportPath, branch: branch.trim(), clean: status.trim() === '' })}\\n`,\n );\n return status.trim() === '' ? 0 : 11;\n }\n const specSource = await readFile(specPath, 'utf8');\n await moveConfiguredCard(ctx.env.HOME ?? ctx.cwd, runId, 'ship');\n await writeFile(specPath, markShipped(specSource), 'utf8');\n await exec('git', ['add', '-A', '--', ':!.cohorte'], { cwd: ctx.cwd });\n await exec('git', ['commit', '-m', `cohorte(${runId}): ship`], { cwd: ctx.cwd });\n await exec('git', ['push', 'origin', branch.trim()], { cwd: ctx.cwd });\n const { stdout: prUrl } = await exec(\n 'gh',\n [\n 'pr',\n 'create',\n '--base',\n 'main',\n '--head',\n branch.trim(),\n '--title',\n `cohorte(${runId}): ship`,\n '--body',\n `Automated Cohorte V3 ship for ${runId}.\\n\\nReview report: ${reportPath}`,\n ],\n { cwd: ctx.cwd },\n );\n const pr = /\\/pull\\/(\\d+)/u.exec(prUrl)?.[1];\n await moveConfiguredCard(ctx.env.HOME ?? ctx.cwd, runId, 'shipped', pr ? `${runId} — PR #${pr}` : runId);\n if (args.positionals.includes('--watch')) {\n await exec('gh', ['pr', 'checks', prUrl.trim(), '--watch'], { cwd: ctx.cwd, maxBuffer: 2_000_000 });\n }\n ctx.stdio.stdout.write(\n `${JSON.stringify({ feature: runId, reportPath, branch: branch.trim(), clean: false, prUrl: prUrl.trim() })}\\n`,\n );\n return 0;\n }\n if (!approvalId) return 2;\n const result = await ctx.controller.send(\n 'approve',\n { approvalId: approvalId as never, scope: 'run', answer: 'ship' },\n { runId },\n );\n ctx.stdio.stdout.write(`${JSON.stringify(result)}\\n`);\n return result.status === 'rejected' ? 3 : result.status === 'pending' ? 4 : 0;\n },\n};\n\nexport default ship;\n"],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import { t as resolveSpecPath } from "./spec-path-tptqR7W8.mjs";
2
+ import { r as loadSpec, t as freezeSpec } from "./src-DbS9gkJy.mjs";
3
+ import { access, appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
4
+ import { resolve } from "node:path";
5
+ import { stringify } from "yaml";
6
+ //#region src/commands/spec/index.ts
7
+ const spec = {
8
+ verb: "spec",
9
+ async run(ctx, args) {
10
+ const input = args.positionals.find((item) => !item.startsWith("--"));
11
+ if (!input) return 2;
12
+ let file = resolveSpecPath(ctx.cwd, input);
13
+ if (!input.includes("/") && !input.endsWith(".yaml") && !input.endsWith(".yml") && !input.endsWith(".md")) for (const candidate of [resolveSpecPath(ctx.cwd, `specs/${input}.md`), file]) try {
14
+ await access(candidate);
15
+ file = candidate;
16
+ break;
17
+ } catch {}
18
+ if (file.endsWith(".md")) {
19
+ const source = await readFile(file, "utf8");
20
+ const frontmatter = /^---\n([\s\S]*?)\n---/u.exec(source)?.[1] ?? "";
21
+ const status = /^status:\s*(\S+)/mu.exec(frontmatter)?.[1] ?? "draft";
22
+ if (args.subVerb === "freeze") {
23
+ if (status === "frozen") {
24
+ ctx.stdio.stdout.write(`${JSON.stringify({
25
+ path: file,
26
+ status,
27
+ valid: true
28
+ })}\n`);
29
+ return 0;
30
+ }
31
+ const next = frontmatter ? frontmatter.replace(/^status:\s*\S+/mu, "status: frozen") : "status: frozen";
32
+ const body = source.startsWith("---\n") ? source.replace(/^---\n[\s\S]*?\n---/u, `---\n${next}\n---`) : `---\n${next}\n---\n\n${source}`;
33
+ await writeFile(file, body, "utf8");
34
+ await mkdir(resolve(ctx.cwd, "specs"), { recursive: true });
35
+ await appendFile(resolve(ctx.cwd, "specs", "_decisions.md"), `${ctx.clock.now().slice(0, 10)} · spec/${input} · frozen\n`);
36
+ ctx.stdio.stdout.write(`${JSON.stringify({
37
+ path: file,
38
+ status: "frozen",
39
+ valid: true
40
+ })}\n`);
41
+ return 0;
42
+ }
43
+ const result = {
44
+ path: file,
45
+ status,
46
+ valid: source.trim().length > 0
47
+ };
48
+ ctx.stdio.stdout.write(`${JSON.stringify(result)}\n`);
49
+ return result.valid ? 0 : 11;
50
+ }
51
+ const frozen = args.subVerb === "freeze";
52
+ const value = frozen ? await freezeSpec(file) : await loadSpec(file);
53
+ if (frozen) await writeFile(file, stringify(value));
54
+ if (frozen) {
55
+ await mkdir(resolve(ctx.cwd, "specs"), { recursive: true });
56
+ await appendFile(resolve(ctx.cwd, "specs", "_decisions.md"), `${ctx.clock.now().slice(0, 10)} · spec/${input} · frozen\n`);
57
+ }
58
+ ctx.stdio.stdout.write(`${JSON.stringify(value)}\n`);
59
+ return 0;
60
+ }
61
+ };
62
+ //#endregion
63
+ export { spec as default };
64
+
65
+ //# sourceMappingURL=spec-C2h3U64g.mjs.map