@clossys/launcher 0.3.1 → 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.
Files changed (290) hide show
  1. package/README.md +1373 -60
  2. package/contracts/conversation-contract.md +2 -2
  3. package/contracts/product-ci-workflow.yml +74 -0
  4. package/contracts/repository-inventory.json +53 -0
  5. package/dist/admission-fixture.d.ts +168 -0
  6. package/dist/admission-fixture.d.ts.map +1 -0
  7. package/dist/admission-fixture.js +467 -0
  8. package/dist/admission-fixture.js.map +1 -0
  9. package/dist/admission.d.ts +124 -0
  10. package/dist/admission.d.ts.map +1 -0
  11. package/dist/admission.js +804 -0
  12. package/dist/admission.js.map +1 -0
  13. package/dist/agents-guide.d.ts +9 -0
  14. package/dist/agents-guide.d.ts.map +1 -0
  15. package/dist/agents-guide.js +26 -0
  16. package/dist/agents-guide.js.map +1 -0
  17. package/dist/apply-command-options.check.d.ts +12 -0
  18. package/dist/apply-command-options.check.d.ts.map +1 -0
  19. package/dist/apply-command-options.check.js +20 -0
  20. package/dist/apply-command-options.check.js.map +1 -0
  21. package/dist/apply-plan-cli.d.ts +39 -1
  22. package/dist/apply-plan-cli.d.ts.map +1 -1
  23. package/dist/apply-plan-cli.js +432 -15
  24. package/dist/apply-plan-cli.js.map +1 -1
  25. package/dist/apply-plan.d.ts +46 -59
  26. package/dist/apply-plan.d.ts.map +1 -1
  27. package/dist/apply-plan.js +112 -97
  28. package/dist/apply-plan.js.map +1 -1
  29. package/dist/apply-step-fixture.d.ts +87 -0
  30. package/dist/apply-step-fixture.d.ts.map +1 -0
  31. package/dist/apply-step-fixture.js +199 -0
  32. package/dist/apply-step-fixture.js.map +1 -0
  33. package/dist/apply-store.d.ts +93 -0
  34. package/dist/apply-store.d.ts.map +1 -0
  35. package/dist/apply-store.js +625 -0
  36. package/dist/apply-store.js.map +1 -0
  37. package/dist/approval-sheet.d.ts +21 -0
  38. package/dist/approval-sheet.d.ts.map +1 -0
  39. package/dist/approval-sheet.js +163 -0
  40. package/dist/approval-sheet.js.map +1 -0
  41. package/dist/body-command.d.ts +42 -0
  42. package/dist/body-command.d.ts.map +1 -0
  43. package/dist/body-command.js +143 -0
  44. package/dist/body-command.js.map +1 -0
  45. package/dist/change-set-contract.d.ts +403 -0
  46. package/dist/change-set-contract.d.ts.map +1 -0
  47. package/dist/change-set-contract.js +781 -0
  48. package/dist/change-set-contract.js.map +1 -0
  49. package/dist/change-set-digest.d.ts +28 -0
  50. package/dist/change-set-digest.d.ts.map +1 -0
  51. package/dist/change-set-digest.js +65 -0
  52. package/dist/change-set-digest.js.map +1 -0
  53. package/dist/check-cli.d.ts.map +1 -1
  54. package/dist/check-cli.js +14 -3
  55. package/dist/check-cli.js.map +1 -1
  56. package/dist/cli.d.ts +17 -6
  57. package/dist/cli.d.ts.map +1 -1
  58. package/dist/cli.js +84 -23
  59. package/dist/cli.js.map +1 -1
  60. package/dist/core.d.ts +79 -22
  61. package/dist/core.d.ts.map +1 -1
  62. package/dist/core.js +843 -268
  63. package/dist/core.js.map +1 -1
  64. package/dist/dry-materialize.d.ts +63 -0
  65. package/dist/dry-materialize.d.ts.map +1 -0
  66. package/dist/dry-materialize.js +330 -0
  67. package/dist/dry-materialize.js.map +1 -0
  68. package/dist/existing-declaration-adoption.check.d.ts +2 -0
  69. package/dist/existing-declaration-adoption.check.d.ts.map +1 -0
  70. package/dist/existing-declaration-adoption.check.js +10 -0
  71. package/dist/existing-declaration-adoption.check.js.map +1 -0
  72. package/dist/generated/contract-schema.generated.d.ts +97 -0
  73. package/dist/generated/contract-schema.generated.d.ts.map +1 -0
  74. package/dist/generated/contract-schema.generated.js +496 -0
  75. package/dist/generated/contract-schema.generated.js.map +1 -0
  76. package/dist/generated/package-scope.generated.d.ts +6 -0
  77. package/dist/generated/package-scope.generated.d.ts.map +1 -0
  78. package/dist/generated/package-scope.generated.js +10 -0
  79. package/dist/generated/package-scope.generated.js.map +1 -0
  80. package/dist/generated/plan-contracts.generated.d.ts +3 -0
  81. package/dist/generated/plan-contracts.generated.d.ts.map +1 -0
  82. package/dist/generated/plan-contracts.generated.js +3101 -0
  83. package/dist/generated/plan-contracts.generated.js.map +1 -0
  84. package/dist/host.d.ts.map +1 -1
  85. package/dist/host.js +11 -0
  86. package/dist/host.js.map +1 -1
  87. package/dist/identity.d.ts +15 -0
  88. package/dist/identity.d.ts.map +1 -0
  89. package/dist/identity.js +48 -0
  90. package/dist/identity.js.map +1 -0
  91. package/dist/index.d.ts +35 -5
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +19 -2
  94. package/dist/index.js.map +1 -1
  95. package/dist/inventory-adoption.d.ts +24 -5
  96. package/dist/inventory-adoption.d.ts.map +1 -1
  97. package/dist/inventory-adoption.js +70 -25
  98. package/dist/inventory-adoption.js.map +1 -1
  99. package/dist/inventory-choice.d.ts +40 -0
  100. package/dist/inventory-choice.d.ts.map +1 -0
  101. package/dist/inventory-choice.js +156 -0
  102. package/dist/inventory-choice.js.map +1 -0
  103. package/dist/inventory-contract.d.ts +89 -0
  104. package/dist/inventory-contract.d.ts.map +1 -0
  105. package/dist/inventory-contract.js +121 -0
  106. package/dist/inventory-contract.js.map +1 -0
  107. package/dist/key-editor.d.ts +30 -0
  108. package/dist/key-editor.d.ts.map +1 -0
  109. package/dist/key-editor.js +445 -0
  110. package/dist/key-editor.js.map +1 -0
  111. package/dist/ledger-contract.d.ts +190 -0
  112. package/dist/ledger-contract.d.ts.map +1 -0
  113. package/dist/ledger-contract.js +555 -0
  114. package/dist/ledger-contract.js.map +1 -0
  115. package/dist/ledger-trust.d.ts +90 -0
  116. package/dist/ledger-trust.d.ts.map +1 -0
  117. package/dist/ledger-trust.js +203 -0
  118. package/dist/ledger-trust.js.map +1 -0
  119. package/dist/lockfile-invariants.d.ts +48 -0
  120. package/dist/lockfile-invariants.d.ts.map +1 -0
  121. package/dist/lockfile-invariants.js +375 -0
  122. package/dist/lockfile-invariants.js.map +1 -0
  123. package/dist/lockfile-readers.d.ts +72 -0
  124. package/dist/lockfile-readers.d.ts.map +1 -0
  125. package/dist/lockfile-readers.js +713 -0
  126. package/dist/lockfile-readers.js.map +1 -0
  127. package/dist/lockfile-regen.d.ts +106 -0
  128. package/dist/lockfile-regen.d.ts.map +1 -0
  129. package/dist/lockfile-regen.js +760 -0
  130. package/dist/lockfile-regen.js.map +1 -0
  131. package/dist/lockfile-tool-env.d.ts +29 -0
  132. package/dist/lockfile-tool-env.d.ts.map +1 -0
  133. package/dist/lockfile-tool-env.js +111 -0
  134. package/dist/lockfile-tool-env.js.map +1 -0
  135. package/dist/materialize.d.ts +113 -0
  136. package/dist/materialize.d.ts.map +1 -0
  137. package/dist/materialize.js +881 -0
  138. package/dist/materialize.js.map +1 -0
  139. package/dist/observe-repository.d.ts +90 -0
  140. package/dist/observe-repository.d.ts.map +1 -0
  141. package/dist/observe-repository.js +1367 -0
  142. package/dist/observe-repository.js.map +1 -0
  143. package/dist/plan-bundle-setup-fixture.d.ts +68 -0
  144. package/dist/plan-bundle-setup-fixture.d.ts.map +1 -0
  145. package/dist/plan-bundle-setup-fixture.js +167 -0
  146. package/dist/plan-bundle-setup-fixture.js.map +1 -0
  147. package/dist/plan-bundle.d.ts +256 -0
  148. package/dist/plan-bundle.d.ts.map +1 -0
  149. package/dist/plan-bundle.js +882 -0
  150. package/dist/plan-bundle.js.map +1 -0
  151. package/dist/plan-command.d.ts +29 -0
  152. package/dist/plan-command.d.ts.map +1 -0
  153. package/dist/plan-command.js +523 -0
  154. package/dist/plan-command.js.map +1 -0
  155. package/dist/plan-contract.d.ts +153 -0
  156. package/dist/plan-contract.d.ts.map +1 -0
  157. package/dist/plan-contract.js +61 -0
  158. package/dist/plan-contract.js.map +1 -0
  159. package/dist/plan-digest.d.ts +25 -0
  160. package/dist/plan-digest.d.ts.map +1 -0
  161. package/dist/plan-digest.js +106 -0
  162. package/dist/plan-digest.js.map +1 -0
  163. package/dist/plan-rules.d.ts +23 -0
  164. package/dist/plan-rules.d.ts.map +1 -0
  165. package/dist/plan-rules.js +177 -0
  166. package/dist/plan-rules.js.map +1 -0
  167. package/dist/planned-bundle.d.ts +20 -0
  168. package/dist/planned-bundle.d.ts.map +1 -0
  169. package/dist/planned-bundle.js +191 -0
  170. package/dist/planned-bundle.js.map +1 -0
  171. package/dist/product-repository.d.ts +4 -0
  172. package/dist/product-repository.d.ts.map +1 -1
  173. package/dist/product-repository.js +9 -1
  174. package/dist/product-repository.js.map +1 -1
  175. package/dist/provenance-gate.d.ts +48 -0
  176. package/dist/provenance-gate.d.ts.map +1 -0
  177. package/dist/provenance-gate.js +324 -0
  178. package/dist/provenance-gate.js.map +1 -0
  179. package/dist/pull-request-body.d.ts +45 -0
  180. package/dist/pull-request-body.d.ts.map +1 -0
  181. package/dist/pull-request-body.js +232 -0
  182. package/dist/pull-request-body.js.map +1 -0
  183. package/dist/registry-snapshot.d.ts +141 -0
  184. package/dist/registry-snapshot.d.ts.map +1 -0
  185. package/dist/registry-snapshot.js +483 -0
  186. package/dist/registry-snapshot.js.map +1 -0
  187. package/dist/release-age-edit.d.ts +52 -0
  188. package/dist/release-age-edit.d.ts.map +1 -0
  189. package/dist/release-age-edit.js +413 -0
  190. package/dist/release-age-edit.js.map +1 -0
  191. package/dist/root-entries.d.ts +36 -0
  192. package/dist/root-entries.d.ts.map +1 -0
  193. package/dist/root-entries.js +80 -0
  194. package/dist/root-entries.js.map +1 -0
  195. package/dist/setup-template-scripts.d.ts +36 -0
  196. package/dist/setup-template-scripts.d.ts.map +1 -0
  197. package/dist/setup-template-scripts.js +568 -0
  198. package/dist/setup-template-scripts.js.map +1 -0
  199. package/dist/setup-templates.d.ts +55 -0
  200. package/dist/setup-templates.d.ts.map +1 -0
  201. package/dist/setup-templates.js +438 -0
  202. package/dist/setup-templates.js.map +1 -0
  203. package/dist/skills.d.ts +34 -1
  204. package/dist/skills.d.ts.map +1 -1
  205. package/dist/skills.js +129 -17
  206. package/dist/skills.js.map +1 -1
  207. package/dist/status.d.ts +63 -0
  208. package/dist/status.d.ts.map +1 -0
  209. package/dist/status.js +539 -0
  210. package/dist/status.js.map +1 -0
  211. package/dist/types.d.ts +151 -13
  212. package/dist/types.d.ts.map +1 -1
  213. package/package.json +4 -4
  214. package/skeleton/README.md +14 -9
  215. package/skeleton/package.json +2 -1
  216. package/skill/SKILL.md +23 -7
  217. package/skill-catalogue/advisor/SKILL.md +59 -6
  218. package/skill-catalogue/architect/SKILL.md +2 -2
  219. package/skill-catalogue/bouncer/SKILL.md +2 -2
  220. package/skill-catalogue/builder/SKILL.md +2 -2
  221. package/skill-catalogue/butler/SKILL.md +2 -2
  222. package/skill-catalogue/controller/SKILL.md +2 -2
  223. package/skill-catalogue/customer/SKILL.md +2 -2
  224. package/skill-catalogue/designer/SKILL.md +4 -2
  225. package/skill-catalogue/giver/SKILL.md +2 -2
  226. package/skill-catalogue/influencer/SKILL.md +2 -2
  227. package/skill-catalogue/inspector/SKILL.md +2 -2
  228. package/skill-catalogue/integrator/SKILL.md +2 -2
  229. package/skill-catalogue/keeper/SKILL.md +2 -2
  230. package/skill-catalogue/launcher/SKILL.md +23 -7
  231. package/skill-catalogue/locksmith/SKILL.md +2 -2
  232. package/skill-catalogue/messenger/SKILL.md +2 -2
  233. package/skill-catalogue/observer/SKILL.md +2 -2
  234. package/skill-catalogue/publisher/SKILL.md +2 -2
  235. package/skill-catalogue/starter/SKILL.md +3 -2
  236. package/skill-catalogue/strategist/SKILL.md +12 -4
  237. package/skill-catalogue/writer/SKILL.md +2 -2
  238. package/src/admission-fixture.ts +585 -0
  239. package/src/admission.ts +819 -0
  240. package/src/agents-guide.ts +29 -0
  241. package/src/apply-command-options.check.ts +27 -0
  242. package/src/apply-plan-cli.ts +454 -14
  243. package/src/apply-plan.ts +112 -124
  244. package/src/apply-step-fixture.ts +236 -0
  245. package/src/apply-store.ts +584 -0
  246. package/src/approval-sheet.ts +170 -0
  247. package/src/body-command.ts +162 -0
  248. package/src/change-set-contract.ts +987 -0
  249. package/src/change-set-digest.ts +70 -0
  250. package/src/check-cli.ts +14 -3
  251. package/src/cli.ts +90 -22
  252. package/src/core.ts +973 -275
  253. package/src/dry-materialize.ts +353 -0
  254. package/src/existing-declaration-adoption.check.ts +12 -0
  255. package/src/generated/contract-schema.generated.ts +520 -0
  256. package/src/generated/package-scope.generated.ts +10 -0
  257. package/src/generated/plan-contracts.generated.ts +3101 -0
  258. package/src/host.ts +10 -0
  259. package/src/identity.ts +51 -0
  260. package/src/index.ts +74 -3
  261. package/src/inventory-adoption.ts +107 -29
  262. package/src/inventory-choice.ts +172 -0
  263. package/src/inventory-contract.ts +166 -0
  264. package/src/key-editor.ts +446 -0
  265. package/src/ledger-contract.ts +660 -0
  266. package/src/ledger-trust.ts +272 -0
  267. package/src/lockfile-invariants.ts +421 -0
  268. package/src/lockfile-readers.ts +749 -0
  269. package/src/lockfile-regen.ts +851 -0
  270. package/src/lockfile-tool-env.ts +131 -0
  271. package/src/materialize.ts +915 -0
  272. package/src/observe-repository.ts +1365 -0
  273. package/src/plan-bundle-setup-fixture.ts +200 -0
  274. package/src/plan-bundle.ts +1014 -0
  275. package/src/plan-command.ts +532 -0
  276. package/src/plan-contract.ts +179 -0
  277. package/src/plan-digest.ts +102 -0
  278. package/src/plan-rules.ts +188 -0
  279. package/src/planned-bundle.ts +211 -0
  280. package/src/product-repository.ts +10 -1
  281. package/src/provenance-gate.ts +352 -0
  282. package/src/pull-request-body.ts +261 -0
  283. package/src/registry-snapshot.ts +534 -0
  284. package/src/release-age-edit.ts +430 -0
  285. package/src/root-entries.ts +81 -0
  286. package/src/setup-template-scripts.ts +580 -0
  287. package/src/setup-templates.ts +479 -0
  288. package/src/skills.ts +161 -18
  289. package/src/status.ts +557 -0
  290. package/src/types.ts +148 -13
@@ -0,0 +1,324 @@
1
+ // Provenance gate for a change set (RFC apply-approved-plan: V9, D20, D23, T12).
2
+ //
3
+ // Every package version a set installs or pins must have registry provenance
4
+ // that Integrator verifies. The hub pins `@clossys/integrator` (D23), so this
5
+ // runs the hub's own `integrator-provenance-check` bin against the
6
+ // materialized tree, reads its JSON report strictly, and turns it into one V9
7
+ // verdict for the set. The bin is found only inside the hub's own
8
+ // node_modules, never through npx or PATH, and runs under the same literal
9
+ // environment allow-list the lockfile step uses (lockfile-tool-env.ts): no
10
+ // parent credential, proxy, CA-trust or NODE_OPTIONS value reaches it.
11
+ //
12
+ // Every ambiguity refuses. A report that does not parse to exactly the shape
13
+ // the bin prints, or that disagrees with its own exit code, is indeterminate,
14
+ // never trusted as far as it goes. A package version the bin does not verify is
15
+ // never passed by any exception: it stays violated or indeterminate exactly as
16
+ // Integrator reports it.
17
+ //
18
+ // D20's first-identity-publication exception is deliberately not implemented.
19
+ // It needs evidence that a version is a package's first publication, and the
20
+ // registry snapshot cannot give it: a snapshot taken to resolve a plan records
21
+ // only the one version `latest` names, whatever else the package has published,
22
+ // and Integrator reports "attestation failed" (violated) the same way as "no
23
+ // attestation". Any condition built on the snapshot would therefore also pass a
24
+ // package with earlier releases whose latest release fails verification. Until
25
+ // the snapshot contract records evidence of a first publication, an unattested
26
+ // first publication blocks the apply: fail closed. Nothing here fetches,
27
+ // repairs or retries.
28
+ //
29
+ // The RFC and the snapshot contract are in the public repository, not shipped
30
+ // in this package: docs/rfcs/apply-approved-plan.md and
31
+ // docs/contracts/registry-snapshot.json.
32
+ import { lstat, realpath, stat } from "node:fs/promises";
33
+ import { delimiter, dirname, isAbsolute, join, relative, sep } from "node:path";
34
+ import { compareCodeUnits } from "./change-set-contract.js";
35
+ import { readContractDocument } from "./generated/contract-schema.generated.js";
36
+ import { PACKAGE_SCOPE } from "./generated/package-scope.generated.js";
37
+ import { spawnLockfileTool } from "./lockfile-regen.js";
38
+ import { lockfileToolEnv, prepareLockfileScratch } from "./lockfile-tool-env.js";
39
+ import { canonicalDigest } from "./plan-digest.js";
40
+ import { canonicalRegistrySnapshot, registrySnapshotViolations } from "./registry-snapshot.js";
41
+ /** The bin Integrator installs, found under the hub's node_modules/.bin. */
42
+ export const PROVENANCE_CHECK_BIN = "integrator-provenance-check";
43
+ /** How long one run of the bin may take. */
44
+ export const PROVENANCE_CHECK_TIMEOUT_MS = 120_000;
45
+ /** Bytes kept of the bin's stdout; a report at or past this is treated as truncated. */
46
+ export const PROVENANCE_CHECK_MAX_BUFFER = 1_000_000;
47
+ // ---------------------------------------------------------------------------
48
+ // The snapshot digest
49
+ // ---------------------------------------------------------------------------
50
+ /**
51
+ * The snapshot's digest, as docs/contracts/registry-snapshot.json defines it
52
+ * (in the public repository, not shipped in this package): `sha256:` and the
53
+ * SHA-256 of the canonical JSON of `{registry, packages}`, packages sorted by
54
+ * name, each reduced to name, status, latest and versions, versions sorted by
55
+ * version and kept whole. fetchedAt, fetchedBy, responseSha256, schemaVersion
56
+ * and kind are left out. Throws TypeError for a snapshot that does not validate
57
+ * against the contract: an invalid snapshot has no digest. The gate itself
58
+ * does not read a snapshot; this is exported for the plan binding that a later
59
+ * change wires in.
60
+ */
61
+ export function registrySnapshotDigest(snapshot) {
62
+ if (registrySnapshotViolations(snapshot).length > 0)
63
+ throw new TypeError("a snapshot that does not validate against the registry snapshot contract has no digest");
64
+ const canonical = canonicalRegistrySnapshot(snapshot);
65
+ return canonicalDigest({
66
+ registry: canonical.registry,
67
+ packages: canonical.packages.map((entry) => ({ name: entry.name, status: entry.status, latest: entry.latest, versions: entry.versions })),
68
+ });
69
+ }
70
+ // ---------------------------------------------------------------------------
71
+ // Verdict plumbing
72
+ // ---------------------------------------------------------------------------
73
+ const indeterminate = (rule) => ({ check: "V9", verdict: "indeterminate", rule });
74
+ const violated = (rule) => ({ check: "V9", verdict: "violated", rule });
75
+ /** The rules behind a failed V9, one entry per distinct rule, in code-unit order. */
76
+ function distinctRules(verdict, rules) {
77
+ return [...rules].sort(compareCodeUnits).map((rule) => (verdict === "indeterminate" ? indeterminate(rule) : violated(rule)));
78
+ }
79
+ /**
80
+ * The packages this set installs or pins that its base does not already hold,
81
+ * by exact name, each once. Only a `satisfiedInBase` of exactly `true` skips an
82
+ * item: a value other than `true` (`false`, missing, `null`, a string or a
83
+ * number) is gated. Undefined when one name is gated at two versions: the set
84
+ * then asks for something no single report can answer.
85
+ */
86
+ function gatedPackages(items) {
87
+ const versions = new Map();
88
+ for (const item of items) {
89
+ if ((item.act !== "install" && item.act !== "pin-starter") || item.satisfiedInBase === true)
90
+ continue;
91
+ const earlier = versions.get(item.package.name);
92
+ if (earlier !== undefined && earlier !== item.package.version)
93
+ return undefined;
94
+ versions.set(item.package.name, item.package.version);
95
+ }
96
+ return [...versions].map(([name, version]) => ({ name, version }));
97
+ }
98
+ // ---------------------------------------------------------------------------
99
+ // Roots and the bin
100
+ // ---------------------------------------------------------------------------
101
+ /** An absolute path that is its own realpath and names a directory. */
102
+ async function isCanonicalDirectory(path) {
103
+ if (typeof path !== "string" || !isAbsolute(path))
104
+ return false;
105
+ try {
106
+ if ((await realpath(path)) !== path)
107
+ return false;
108
+ return (await stat(path)).isDirectory();
109
+ }
110
+ catch {
111
+ return false;
112
+ }
113
+ }
114
+ /** Whether `child` lies strictly inside `parent`; both are already real paths. */
115
+ function isStrictlyInside(parent, child) {
116
+ const path = relative(parent, child);
117
+ return path !== "" && path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path);
118
+ }
119
+ /**
120
+ * The verified real path of the hub's Integrator bin, or undefined. The
121
+ * `.bin` entry must resolve to a regular file strictly inside the real path of
122
+ * `node_modules/@clossys/integrator`, and that directory's real path must
123
+ * itself lie strictly inside `<hubRoot>/node_modules`, so a link that leaves
124
+ * the hub, leaves node_modules, or points at some other package's file is
125
+ * refused. `hubRoot` is already its own realpath.
126
+ *
127
+ * The caller spawns the returned real path, not the `.bin` entry: the launch
128
+ * then goes to the very file that was checked, and a link swapped in after the
129
+ * check cannot redirect it.
130
+ */
131
+ async function verifiedBin(hubRoot) {
132
+ const nodeModules = join(hubRoot, "node_modules");
133
+ try {
134
+ const realBin = await realpath(join(nodeModules, ".bin", PROVENANCE_CHECK_BIN));
135
+ const realIntegrator = await realpath(join(nodeModules, "@clossys", "integrator"));
136
+ if (!(await lstat(realBin)).isFile() || !(await lstat(realIntegrator)).isDirectory())
137
+ return undefined;
138
+ if (!isStrictlyInside(nodeModules, realIntegrator) || !isStrictlyInside(realIntegrator, realBin))
139
+ return undefined;
140
+ return realBin;
141
+ }
142
+ catch {
143
+ return undefined;
144
+ }
145
+ }
146
+ const REPORT_KEYS = new Set(["state", "registryBaseUrl", "packages"]);
147
+ const PACKAGE_KEYS = new Set(["name", "installedVersion", "latestVersion", "currencyDistance", "state", "reasons"]);
148
+ const isPlainObject = (value) => {
149
+ if (typeof value !== "object" || value === null || Array.isArray(value))
150
+ return false;
151
+ const prototype = Object.getPrototypeOf(value);
152
+ return prototype === Object.prototype || prototype === null;
153
+ };
154
+ const isState = (value) => value === "verified" || value === "violated" || value === "indeterminate";
155
+ const isString = (value) => typeof value === "string";
156
+ /** Whether `value` is an own-keyed plain object whose keys all come from `allowed`. */
157
+ function hasOnlyKeys(value, allowed) {
158
+ return Object.keys(value).every((key) => allowed.has(key) && Object.hasOwn(value, key));
159
+ }
160
+ /**
161
+ * Reads the bin's stdout as the closed report it prints, or undefined. Output
162
+ * at or past the capture limit is refused as truncated. The text is read with
163
+ * the same strict reader the contracts use, so a repeated key or a byte order
164
+ * mark is refused as well as anything JSON.parse refuses; no key outside the
165
+ * closed sets is accepted, and each field must have the type the bin gives it.
166
+ * The report's reasons are checked for shape and never used.
167
+ */
168
+ function readReport(stdout) {
169
+ if (Buffer.byteLength(stdout, "utf8") >= PROVENANCE_CHECK_MAX_BUFFER)
170
+ return undefined;
171
+ let value;
172
+ try {
173
+ value = readContractDocument(Buffer.from(stdout, "utf8"));
174
+ }
175
+ catch {
176
+ return undefined;
177
+ }
178
+ if (!isPlainObject(value) || !hasOnlyKeys(value, REPORT_KEYS) || Object.keys(value).length !== REPORT_KEYS.size)
179
+ return undefined;
180
+ const { state, registryBaseUrl, packages } = value;
181
+ if (!isState(state) || registryBaseUrl !== PACKAGE_SCOPE.registry || !Array.isArray(packages))
182
+ return undefined;
183
+ const reported = [];
184
+ for (const entry of packages) {
185
+ if (!isPlainObject(entry) || !hasOnlyKeys(entry, PACKAGE_KEYS))
186
+ return undefined;
187
+ const { name, installedVersion, latestVersion, currencyDistance, state: packageState, reasons } = entry;
188
+ if (!isString(name) || !isString(installedVersion) || !isState(packageState))
189
+ return undefined;
190
+ if (!Array.isArray(reasons) || !reasons.every(isString))
191
+ return undefined;
192
+ if (Object.hasOwn(entry, "latestVersion") && !isString(latestVersion))
193
+ return undefined;
194
+ if (Object.hasOwn(entry, "currencyDistance") && !isString(currencyDistance))
195
+ return undefined;
196
+ reported.push({ name, installedVersion, state: packageState });
197
+ }
198
+ return { state, packages: reported };
199
+ }
200
+ /** Whether the report says what its exit code says: exit 0 all verified, exit 1 a violation and nothing indeterminate. */
201
+ function agreesWithExit(status, report) {
202
+ if (status === 0)
203
+ return report.state === "verified" && report.packages.length > 0 && report.packages.every((entry) => entry.state === "verified");
204
+ return report.state === "violated" && report.packages.some((entry) => entry.state === "violated") && report.packages.every((entry) => entry.state !== "indeterminate");
205
+ }
206
+ /** The rule an unsuccessful launch maps to, or undefined when the bin ran to exit 0 or 1 with no failure flag. */
207
+ function launchFailure(result) {
208
+ if (result.failure === "not-found")
209
+ return "engine-missing-bin";
210
+ if (result.failure === "timeout")
211
+ return "engine-timeout";
212
+ if (result.failure !== undefined)
213
+ return "engine-failed";
214
+ if (result.status === 2)
215
+ return "engine-indeterminate";
216
+ if (result.status !== 0 && result.status !== 1)
217
+ return "engine-failed";
218
+ return undefined;
219
+ }
220
+ // ---------------------------------------------------------------------------
221
+ // The gate
222
+ // ---------------------------------------------------------------------------
223
+ /**
224
+ * V9 for one change set. Satisfied without touching anything when the set
225
+ * installs or pins nothing its base lacks. Otherwise runs the hub's
226
+ * `integrator-provenance-check` against `tree` and judges each gated package
227
+ * by exact name and exact version: satisfied only when every one is verified
228
+ * at that version, with no exception for an unverified one. Other `@clossys`
229
+ * packages in the report never gate: an unrelated violated legacy pin passes
230
+ * when every gated package is verified. Indeterminate wins over violated; the
231
+ * result lists each distinct rule once, in code-unit order, and never mixes a
232
+ * satisfied entry with a refusal. Never throws for a bad input; everything it
233
+ * cannot rely on is an indeterminate entry. The input carries no ledger or
234
+ * snapshot: nothing here reads one.
235
+ */
236
+ export async function checkSetProvenance(input, ports = {}) {
237
+ // 1. The gated set.
238
+ const gated = gatedPackages(input.items);
239
+ if (gated === undefined)
240
+ return [indeterminate("gated-package-conflict")];
241
+ if (gated.length === 0)
242
+ return [{ check: "V9", verdict: "satisfied" }];
243
+ // 2 and 3. Roots, then the bin.
244
+ if (!(await isCanonicalDirectory(input.tree)) || !(await isCanonicalDirectory(input.hubRoot)))
245
+ return [indeterminate("root-not-canonical")];
246
+ const command = await verifiedBin(input.hubRoot);
247
+ if (command === undefined)
248
+ return [indeterminate("engine-missing-bin")];
249
+ // 4. Launch, in a scratch working directory, under the allow-list environment.
250
+ const run = ports.spawn ?? spawnLockfileTool;
251
+ let scratch;
252
+ try {
253
+ scratch = await prepareLockfileScratch();
254
+ }
255
+ catch {
256
+ return [indeterminate("engine-failed")];
257
+ }
258
+ let result;
259
+ try {
260
+ const env = lockfileToolEnv({ tool: "npm", scratch: scratch.path, registry: PACKAGE_SCOPE.registry, corepack: false, parent: process.env });
261
+ // The bin's `#!/usr/bin/env node` must find the node that is running this code.
262
+ env.PATH = `${dirname(process.execPath)}${delimiter}${env.PATH ?? ""}`;
263
+ result = await run({
264
+ command,
265
+ args: ["--cwd", input.tree, "--registry", PACKAGE_SCOPE.registry],
266
+ cwd: scratch.path,
267
+ env,
268
+ timeoutMs: PROVENANCE_CHECK_TIMEOUT_MS,
269
+ maxBuffer: PROVENANCE_CHECK_MAX_BUFFER,
270
+ });
271
+ }
272
+ catch {
273
+ result = undefined;
274
+ }
275
+ finally {
276
+ try {
277
+ await scratch.remove();
278
+ }
279
+ catch {
280
+ // The verdict does not depend on the scratch's removal, and it is under the OS temp root.
281
+ }
282
+ }
283
+ if (result === undefined)
284
+ return [indeterminate("engine-failed")];
285
+ const failure = launchFailure(result);
286
+ if (failure !== undefined)
287
+ return [indeterminate(failure)];
288
+ // 5 and 6. The report, and whether it agrees with its exit code.
289
+ const report = typeof result.stdout === "string" ? readReport(result.stdout) : undefined;
290
+ if (report === undefined)
291
+ return [indeterminate("report-unreadable")];
292
+ if (!agreesWithExit(result.status, report))
293
+ return [indeterminate("report-contradicts-exit")];
294
+ // 7 and 8. Each gated package, by exact name and exact version.
295
+ const indeterminateRules = new Set();
296
+ const violatedRules = new Set();
297
+ for (const pkg of gated) {
298
+ const entries = report.packages.filter((entry) => entry.name === pkg.name);
299
+ if (entries.length === 0) {
300
+ indeterminateRules.add("package-not-in-report");
301
+ continue;
302
+ }
303
+ if (entries.some((entry) => entry.installedVersion !== pkg.version)) {
304
+ violatedRules.add("version-mismatch");
305
+ continue;
306
+ }
307
+ // The report already agrees with its exit code, so no entry is indeterminate; this is the same refusal kept local.
308
+ if (entries.some((entry) => entry.state === "indeterminate")) {
309
+ indeterminateRules.add("package-indeterminate");
310
+ continue;
311
+ }
312
+ if (entries.every((entry) => entry.state === "verified"))
313
+ continue;
314
+ // Unverified at the act's own version: violated, with no exception.
315
+ violatedRules.add("provenance-unverified");
316
+ }
317
+ // 9. Indeterminate wins over violated; a satisfied entry never sits beside a refusal.
318
+ if (indeterminateRules.size > 0)
319
+ return distinctRules("indeterminate", indeterminateRules);
320
+ if (violatedRules.size > 0)
321
+ return distinctRules("violated", violatedRules);
322
+ return [{ check: "V9", verdict: "satisfied" }];
323
+ }
324
+ //# sourceMappingURL=provenance-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance-gate.js","sourceRoot":"","sources":["../src/provenance-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { ApprovalBinding, RepositoryChangeSet } from "./change-set-contract.js";
2
+ export type PullRequestRefusalReason = "change-set-invalid" | "title-mismatch" | "binding-invalid" | "binding-phase-mismatch" | "task-record-invalid" | "supersedes-invalid" | "value-unsafe" | "body-invalid" | "render-failed";
3
+ /** A refusal: a fixed token, never a value. */
4
+ export interface PullRequestRefusal {
5
+ readonly state: "refused";
6
+ readonly reason: PullRequestRefusalReason;
7
+ }
8
+ export interface RenderPullRequestInput {
9
+ readonly set: RepositoryChangeSet;
10
+ /** What admission decided for this set (decideSetBinding). It is shown, not checked here beyond its shape. */
11
+ readonly binding: ApprovalBinding;
12
+ /** The number of the task-record issue in the target repository: a positive safe integer. */
13
+ readonly taskRecord: number;
14
+ /**
15
+ * The numbers of the pull requests of older change sets this one replaces (RFC section 6, D6): each a positive safe integer,
16
+ * none repeated and none the task record. Written ascending under `## Supersedes`. Absent or empty: no such section, and the
17
+ * body is the one it was without this option.
18
+ */
19
+ readonly supersedes?: readonly number[];
20
+ }
21
+ export interface PullRequestText {
22
+ readonly state: "rendered";
23
+ readonly title: string;
24
+ readonly body: string;
25
+ /** `sha256:` and the hex SHA-256 of the body's UTF-8 bytes. */
26
+ readonly bodySha256: string;
27
+ }
28
+ /**
29
+ * The change-set digest a body carries, or null. Exactly one line must be
30
+ * exactly `<!-- clossys-change-set: sha256:<64 hex> -->`, and the marker's
31
+ * name must appear nowhere else in the body, so a second marker line, or a
32
+ * marker inside another line, reads as none.
33
+ */
34
+ export declare function readChangeSetMarker(body: string): string | null;
35
+ /**
36
+ * The pull request title and body for one stored change set. The title is
37
+ * exactly `set.pullRequest.title`, which must be `Clossys: apply plan ` and
38
+ * the first 12 hex digits of the set's own digest. Refuses a set that fails
39
+ * validateRepositoryChangeSet or whose digest does not recompute, a malformed
40
+ * binding, an `admitted` binding on a setup set, a task record that is not a
41
+ * positive safe integer, and any value it cannot prove safe to write. Never
42
+ * throws.
43
+ */
44
+ export declare function renderPullRequest(input: RenderPullRequestInput): PullRequestText | PullRequestRefusal;
45
+ //# sourceMappingURL=pull-request-body.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull-request-body.d.ts","sourceRoot":"","sources":["../src/pull-request-body.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,eAAe,EAAiB,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGpG,MAAM,MAAM,wBAAwB,GAChC,oBAAoB,GACpB,gBAAgB,GAChB,iBAAiB,GACjB,wBAAwB,GACxB,qBAAqB,GACrB,oBAAoB,GACpB,cAAc,GACd,cAAc,GACd,eAAe,CAAC;AAEpB,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC;CAC3C;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,EAAE,mBAAmB,CAAC;IAClC,8GAA8G;IAC9G,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,6FAA6F;IAC7F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAyCD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D;AA0BD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,eAAe,GAAG,kBAAkB,CAMrG"}
@@ -0,0 +1,232 @@
1
+ // The title and body of one repository's pull request (issue #1178; the apply
2
+ // RFC's section 6 body marker, threat T6, decision D31 and section 12.7, in the
3
+ // public repository, not shipped in this package). A pure function of three
4
+ // inputs: a stored change set, the ApprovalBinding admission decided for it,
5
+ // and the number of the task-record issue. It reads nothing, writes nothing and
6
+ // opens nothing; a later step decides the binding, passes it in, records
7
+ // `bodySha256` and hands the text to the agent that opens the pull request.
8
+ // The caller may also pass the numbers of the older pull requests this one
9
+ // replaces; they are written ascending under `## Supersedes` and nothing else.
10
+ //
11
+ // This is a public-safety surface, so every ambiguity refuses:
12
+ //
13
+ // - The text carries ids, act names, versions and digests only: never a brief,
14
+ // plan prose, `texts`, a key value, a path or a file's contents. Every value
15
+ // is matched against its own strict pattern before it is written, and any
16
+ // that holds `<`, `>`, a backtick, `|`, a carriage return or a line feed is
17
+ // refused, so no value can start a marker, close a code span or add a line.
18
+ // - Each value is written inside a code span, so a package name such as
19
+ // `@scope/name` is never read by GitHub as a mention.
20
+ // - The body opens with the one marker line and holds no other, and it is read
21
+ // back with readChangeSetMarker before it is returned.
22
+ // - The renderer does not decide the binding: it shows what the caller passes.
23
+ // It does not check that the task-record issue exists, and it cannot stop a
24
+ // pull request's body being edited after it is opened; `bodySha256` and the
25
+ // marker are how a later `status` sees that.
26
+ //
27
+ // A refusal carries a fixed token, never an id, a digest or any input text.
28
+ import { createHash } from "node:crypto";
29
+ import { validateRepositoryChangeSet } from "./change-set-contract.js";
30
+ import { changeSetDigest } from "./change-set-digest.js";
31
+ const DIGEST = /^sha256:[0-9a-f]{64}$/u;
32
+ const TITLE = /^Clossys: apply plan [0-9a-f]{12}$/u;
33
+ const MARKER_LINE = /^<!-- clossys-change-set: (sha256:[0-9a-f]{64}) -->$/u;
34
+ const REPOSITORY_ID = /^(?:[A-Za-z0-9][A-Za-z0-9-]{0,38}\/)?[A-Za-z0-9._-]{1,100}$/u;
35
+ // 355 characters: a repository id of up to 140, a colon and a package name of up to 214 (the planItem the contract derives, C16).
36
+ const ITEM_ID = /^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,354}$/u;
37
+ const ID_TOKEN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
38
+ const PACKAGE_NAME = /^(?=.{1,214}$)@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$/u;
39
+ const EXACT_VERSION = /^(?:0|[1-9][0-9]{0,15})\.(?:0|[1-9][0-9]{0,15})\.(?:0|[1-9][0-9]{0,15})$/u;
40
+ const UNSAFE = /[<>`|\r\n]/u;
41
+ const PRINTABLE_LINES = /^[\x20-\x7e\n]*$/u;
42
+ const MARKER_WORD = /clossys-change-set/giu;
43
+ const PHASES = new Set(["setup", "apply"]);
44
+ const ACTS = new Set([
45
+ "write-record",
46
+ "compose-skills",
47
+ "install",
48
+ "pin-starter",
49
+ "exempt-release-age",
50
+ "declare-root-entry",
51
+ "write-ledger",
52
+ "add-caller-workflow",
53
+ "write-starter-request",
54
+ "add-ci-template",
55
+ "add-path-scope-job",
56
+ ]);
57
+ const refuse = (reason) => ({ state: "refused", reason });
58
+ /** A value that may be interpolated: a string that matches its own pattern and holds nothing that could start a marker, close a code span or add a line. */
59
+ function safe(value, pattern) {
60
+ return typeof value === "string" && !UNSAFE.test(value) && pattern.test(value);
61
+ }
62
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
63
+ const sha256Of = (text) => `sha256:${createHash("sha256").update(Buffer.from(text, "utf8")).digest("hex")}`;
64
+ /**
65
+ * The change-set digest a body carries, or null. Exactly one line must be
66
+ * exactly `<!-- clossys-change-set: sha256:<64 hex> -->`, and the marker's
67
+ * name must appear nowhere else in the body, so a second marker line, or a
68
+ * marker inside another line, reads as none.
69
+ */
70
+ export function readChangeSetMarker(body) {
71
+ if (typeof body !== "string")
72
+ return null;
73
+ const found = body.split("\n").flatMap((line) => MARKER_LINE.exec(line)?.[1] ?? []);
74
+ if (found.length !== 1)
75
+ return null;
76
+ if ((body.match(MARKER_WORD) ?? []).length !== 1)
77
+ return null;
78
+ return found[0];
79
+ }
80
+ function itemLine(item) {
81
+ if (!safe(item.id, ITEM_ID) || typeof item.act !== "string" || !ACTS.has(item.act))
82
+ return null;
83
+ if (item.act !== "install" && item.act !== "pin-starter")
84
+ return `- \`${item.id}\` \`${item.act}\``;
85
+ if (!safe(item.package.name, PACKAGE_NAME) || !safe(item.package.version, EXACT_VERSION))
86
+ return null;
87
+ return `- \`${item.id}\` \`${item.act}\` \`${item.package.name}@${item.package.version}\``;
88
+ }
89
+ function bindingLines(binding, set) {
90
+ if (!isRecord(binding))
91
+ return refuse("binding-invalid");
92
+ const keys = Object.keys(binding).sort().join(",");
93
+ if (binding.kind === "approved") {
94
+ if (keys !== "kind,subjectDigest" || !safe(binding.subjectDigest, DIGEST))
95
+ return refuse("binding-invalid");
96
+ return ["- Binding: `approved`", `- Approved subject: \`${binding.subjectDigest}\``];
97
+ }
98
+ if (binding.kind === "admitted") {
99
+ if (keys !== "kind,setupChangeSet,subjectDigest" || !safe(binding.subjectDigest, DIGEST) || !safe(binding.setupChangeSet, DIGEST))
100
+ return refuse("binding-invalid");
101
+ if (set.phase !== "apply")
102
+ return refuse("binding-phase-mismatch");
103
+ // Admission never binds a set to itself, or to the bundle that holds the set.
104
+ if (binding.setupChangeSet === set.changeSetDigest || binding.subjectDigest === set.bundle)
105
+ return refuse("binding-invalid");
106
+ return ["- Binding: `admitted`", `- Admitted subject: \`${binding.subjectDigest}\``, `- Setup change set: \`${binding.setupChangeSet}\``];
107
+ }
108
+ return refuse("binding-invalid");
109
+ }
110
+ /**
111
+ * The pull request title and body for one stored change set. The title is
112
+ * exactly `set.pullRequest.title`, which must be `Clossys: apply plan ` and
113
+ * the first 12 hex digits of the set's own digest. Refuses a set that fails
114
+ * validateRepositoryChangeSet or whose digest does not recompute, a malformed
115
+ * binding, an `admitted` binding on a setup set, a task record that is not a
116
+ * positive safe integer, and any value it cannot prove safe to write. Never
117
+ * throws.
118
+ */
119
+ export function renderPullRequest(input) {
120
+ try {
121
+ return render(input);
122
+ }
123
+ catch {
124
+ return refuse("render-failed");
125
+ }
126
+ }
127
+ function render(input) {
128
+ const { taskRecord } = input;
129
+ if (typeof taskRecord !== "number" || !Number.isSafeInteger(taskRecord) || taskRecord < 1)
130
+ return refuse("task-record-invalid");
131
+ // Read once, like the set: what is checked is what is written. Only a list of distinct positive safe integers other than the task record passes.
132
+ let supersedes = [];
133
+ if (input.supersedes !== undefined) {
134
+ let read;
135
+ try {
136
+ read = JSON.parse(JSON.stringify(input.supersedes));
137
+ }
138
+ catch {
139
+ return refuse("supersedes-invalid");
140
+ }
141
+ if (!Array.isArray(read))
142
+ return refuse("supersedes-invalid");
143
+ for (const entry of read)
144
+ if (typeof entry !== "number" || !Number.isSafeInteger(entry) || entry < 1 || entry === taskRecord)
145
+ return refuse("supersedes-invalid");
146
+ supersedes = read.slice().sort((left, right) => left - right);
147
+ if (supersedes.some((entry, index) => index > 0 && entry === supersedes[index - 1]))
148
+ return refuse("supersedes-invalid");
149
+ }
150
+ // One snapshot, so a getter or a proxy cannot show the checks one value and the text another.
151
+ let set;
152
+ let binding;
153
+ try {
154
+ set = JSON.parse(JSON.stringify(input.set));
155
+ }
156
+ catch {
157
+ return refuse("change-set-invalid");
158
+ }
159
+ try {
160
+ binding = JSON.parse(JSON.stringify(input.binding));
161
+ }
162
+ catch {
163
+ return refuse("binding-invalid");
164
+ }
165
+ if (!validateRepositoryChangeSet(set).valid || changeSetDigest(set) !== set.changeSetDigest)
166
+ return refuse("change-set-invalid");
167
+ // The contract's rule C5 already ties the title to the digest; this is the same rule, held here too.
168
+ const digest = set.changeSetDigest;
169
+ if (!safe(digest, DIGEST))
170
+ return refuse("change-set-invalid");
171
+ if (!safe(set.pullRequest.title, TITLE) || set.pullRequest.title !== `Clossys: apply plan ${digest.slice("sha256:".length, "sha256:".length + 12)}`)
172
+ return refuse("title-mismatch");
173
+ const bound = bindingLines(binding, set);
174
+ if (!Array.isArray(bound))
175
+ return bound;
176
+ const repositoryId = set.repository.id;
177
+ if (!safe(repositoryId, REPOSITORY_ID) || repositoryId === "." || repositoryId === ".." || repositoryId.endsWith("/.") || repositoryId.endsWith("/.."))
178
+ return refuse("value-unsafe");
179
+ if (typeof set.phase !== "string" || !PHASES.has(set.phase))
180
+ return refuse("value-unsafe");
181
+ if (!safe(set.planDigest, DIGEST) || !safe(set.bundle, DIGEST))
182
+ return refuse("value-unsafe");
183
+ const items = [];
184
+ for (const item of set.items) {
185
+ const line = itemLine(item);
186
+ if (line === null)
187
+ return refuse("value-unsafe");
188
+ items.push(line);
189
+ }
190
+ const notes = [];
191
+ for (const deferral of set.deferred) {
192
+ if (!safe(deferral.planItem, ITEM_ID) || !safe(deferral.reason, ID_TOKEN))
193
+ return refuse("value-unsafe");
194
+ notes.push(`- Deferred: \`${deferral.planItem}\` \`${deferral.reason}\``);
195
+ }
196
+ for (const refusal of set.refused) {
197
+ if (!safe(refusal.item, ITEM_ID) || !safe(refusal.reason, ID_TOKEN))
198
+ return refuse("value-unsafe");
199
+ notes.push(`- Refused: \`${refusal.item}\` \`${refusal.reason}\``);
200
+ }
201
+ const lines = [
202
+ `<!-- clossys-change-set: ${digest} -->`,
203
+ "",
204
+ "## Change set",
205
+ "",
206
+ `- Repository: \`${repositoryId}\``,
207
+ `- Phase: \`${set.phase}\``,
208
+ `- Change set: \`${digest}\``,
209
+ `- Plan: \`${set.planDigest}\``,
210
+ `- Bundle: \`${set.bundle}\``,
211
+ "",
212
+ "## Binding",
213
+ "",
214
+ ...bound,
215
+ "",
216
+ "## Items",
217
+ "",
218
+ ...items,
219
+ ...(notes.length === 0 ? [] : ["", "## Not applied", "", ...notes]),
220
+ ...(supersedes.length === 0 ? [] : ["", "## Supersedes", "", ...supersedes.map((number) => `- #${number}`)]),
221
+ "",
222
+ "## Task record",
223
+ "",
224
+ `- #${taskRecord}`,
225
+ ];
226
+ const body = `${lines.join("\n")}\n`;
227
+ // The last word: printable ASCII and line feeds only, no line feed at the end but one, and the marker reads back as this set's digest.
228
+ if (!PRINTABLE_LINES.test(body) || body.endsWith("\n\n") || readChangeSetMarker(body) !== digest || !body.startsWith(`<!-- clossys-change-set: ${digest} -->\n`))
229
+ return refuse("body-invalid");
230
+ return { state: "rendered", title: set.pullRequest.title, body, bodySha256: sha256Of(body) };
231
+ }
232
+ //# sourceMappingURL=pull-request-body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull-request-body.js","sourceRoot":"","sources":["../src/pull-request-body.ts"],"names":[],"mappings":"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"}