@open-agent-toolkit/cli 0.2.72 → 0.2.74

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 (165) hide show
  1. package/assets/agents/oat-reviewer.md +4 -3
  2. package/assets/agents/recon-worker.md +42 -8
  3. package/assets/bundle-metadata.json +1 -1
  4. package/assets/docs/cli-utilities/configuration.md +3 -56
  5. package/assets/docs/cli-utilities/tool-packs.md +4 -4
  6. package/assets/docs/contributing/code.md +2 -2
  7. package/assets/docs/contributing/index.md +0 -1
  8. package/assets/docs/reference/cli-reference.md +1 -1
  9. package/assets/docs/reference/troubleshooting.md +17 -40
  10. package/assets/docs/workflows/projects/artifacts.md +16 -34
  11. package/assets/docs/workflows/projects/lifecycle.md +20 -19
  12. package/assets/docs/workflows/skills/explainer-kit.md +180 -440
  13. package/assets/docs/workflows/skills/index.md +1 -2
  14. package/assets/docs/workflows/skills/recon.md +114 -23
  15. package/assets/public-package-versions.json +4 -4
  16. package/assets/skills/explainer-kit/SKILL.md +96 -171
  17. package/assets/skills/explainer-kit/recipes/engineer-tour.json +1 -22
  18. package/assets/skills/explainer-kit/recipes/program-recap.json +1 -29
  19. package/assets/skills/explainer-kit/recipes/project-explainer.json +1 -22
  20. package/assets/skills/explainer-kit/recipes/project-recap.v2.json +1 -43
  21. package/assets/skills/explainer-kit/references/fact-base-contract.md +76 -101
  22. package/assets/skills/explainer-kit/references/recap-authoring.md +99 -0
  23. package/assets/skills/explainer-kit/schemas/manifest.schema.json +13 -135
  24. package/assets/skills/explainer-kit/scripts/bundle.mjs +712 -0
  25. package/assets/skills/explainer-kit/scripts/lib/claim-subject.mjs +40 -0
  26. package/assets/skills/explainer-kit/scripts/lib/contracts.mjs +113 -1504
  27. package/assets/skills/explainer-kit/scripts/lib/failure.mjs +13 -0
  28. package/assets/skills/explainer-kit/scripts/lib/package-coverage.mjs +57 -403
  29. package/assets/skills/explainer-kit/scripts/lib/qa-result.mjs +168 -0
  30. package/assets/skills/explainer-kit/scripts/lib/qa.mjs +90 -488
  31. package/assets/skills/explainer-kit/scripts/lib/recipes.mjs +28 -545
  32. package/assets/skills/explainer-kit/scripts/lib/run-package.mjs +120 -0
  33. package/assets/skills/explainer-kit/scripts/lib/sanitize.mjs +42 -0
  34. package/assets/skills/explainer-kit/scripts/record.mjs +335 -0
  35. package/assets/skills/explainer-kit/scripts/verify.mjs +648 -0
  36. package/assets/skills/oat-explainer-kit/SKILL.md +57 -117
  37. package/assets/skills/oat-explainer-kit/references/config-contract.md +20 -64
  38. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +85 -237
  39. package/assets/skills/oat-explainer-kit/scripts/bind-project-sources.mjs +18 -10
  40. package/assets/skills/oat-explainer-kit/scripts/check-core.mjs +1 -0
  41. package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +256 -21
  42. package/assets/skills/oat-explainer-kit/scripts/persist-intent.mjs +82 -3
  43. package/assets/skills/oat-explainer-kit/scripts/resolve-config.mjs +0 -162
  44. package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +57 -123
  45. package/assets/skills/oat-project-autonomous/SKILL.md +14 -13
  46. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +165 -165
  47. package/assets/skills/oat-project-complete/SKILL.md +168 -349
  48. package/assets/skills/oat-project-complete/scripts/consume-persisted-recap-intent.mjs +125 -0
  49. package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +0 -30
  50. package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +4 -37
  51. package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +17 -16
  52. package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +9 -174
  53. package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +1 -2
  54. package/assets/skills/oat-project-document/SKILL.md +1 -1
  55. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +165 -165
  56. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  57. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +44 -56
  58. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +165 -165
  59. package/assets/skills/oat-project-lite/SKILL.md +1 -1
  60. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +165 -165
  61. package/assets/skills/oat-project-plan/SKILL.md +5 -6
  62. package/assets/skills/oat-project-pr-final/SKILL.md +1 -1
  63. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +165 -165
  64. package/assets/skills/oat-project-quick-start/SKILL.md +1 -1
  65. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +165 -165
  66. package/assets/skills/oat-project-review-receive/SKILL.md +14 -1
  67. package/assets/skills/oat-project-summary/SKILL.md +35 -25
  68. package/assets/skills/oat-wave-execute/SKILL.md +11 -36
  69. package/assets/skills/oat-wave-program/SKILL.md +13 -37
  70. package/assets/skills/recon/SKILL.md +103 -40
  71. package/assets/skills/recon/references/packet-contract.md +150 -67
  72. package/assets/skills/recon/references/profiles.md +38 -16
  73. package/assets/skills/recon/references/worker-contract.md +39 -8
  74. package/assets/skills/recon/scripts/lib/contracts.mjs +703 -55
  75. package/assets/skills/recon/scripts/lib/routing.mjs +588 -0
  76. package/assets/skills/recon/scripts/lib/validated-run.mjs +2 -0
  77. package/assets/skills/recon/scripts/prepare-routing.mjs +88 -0
  78. package/assets/skills/recon/scripts/render-packet.mjs +69 -1
  79. package/assets/skills/recon/scripts/validate-packet.mjs +473 -56
  80. package/assets/skills/subagent-orchestration/SKILL.md +18 -9
  81. package/assets/skills/subagent-orchestration/references/model-selection-principles.md +17 -8
  82. package/dist/commands/config/index.d.ts.map +1 -1
  83. package/dist/commands/config/index.js +9 -113
  84. package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
  85. package/dist/commands/project/archive/archive-utils.js +32 -184
  86. package/dist/commands/project/archive/explainer-package-coverage.d.ts +3 -8
  87. package/dist/commands/project/archive/explainer-package-coverage.d.ts.map +1 -1
  88. package/dist/commands/project/archive/explainer-package-coverage.js +1 -1
  89. package/dist/config/oat-config.d.ts +0 -11
  90. package/dist/config/oat-config.d.ts.map +1 -1
  91. package/dist/config/oat-config.js +0 -34
  92. package/dist/config/resolve.d.ts.map +1 -1
  93. package/dist/config/resolve.js +0 -8
  94. package/dist/release/public-package-contract.d.ts.map +1 -1
  95. package/dist/release/public-package-contract.js +2 -10
  96. package/dist/validation/project-state.d.ts.map +1 -1
  97. package/dist/validation/project-state.js +42 -3
  98. package/package.json +2 -2
  99. package/assets/docs/contributing/explainer-kit-verification.md +0 -133
  100. package/assets/docs/workflows/skills/explainer-kit-providers.md +0 -155
  101. package/assets/skills/explainer-kit/briefs/deep-dive.md +0 -35
  102. package/assets/skills/explainer-kit/briefs/project-page.md +0 -38
  103. package/assets/skills/explainer-kit/briefs/supporting-diagram.md +0 -28
  104. package/assets/skills/explainer-kit/briefs/walkthrough-deck.md +0 -35
  105. package/assets/skills/explainer-kit/examples/project-explainer/content.md +0 -31
  106. package/assets/skills/explainer-kit/examples/project-explainer/fact-base.md +0 -22
  107. package/assets/skills/explainer-kit/examples/project-recap/content.md +0 -77
  108. package/assets/skills/explainer-kit/examples/project-recap/fact-base.json +0 -104
  109. package/assets/skills/explainer-kit/examples/project-recap/fact-base.md +0 -44
  110. package/assets/skills/explainer-kit/examples/theme-bundle.json +0 -87
  111. package/assets/skills/explainer-kit/recipes/project-recap.v1.json +0 -93
  112. package/assets/skills/explainer-kit/references/contracts.md +0 -324
  113. package/assets/skills/explainer-kit/references/destination-contract.md +0 -210
  114. package/assets/skills/explainer-kit/references/extension-contract.md +0 -80
  115. package/assets/skills/explainer-kit/references/golden-conformance.md +0 -80
  116. package/assets/skills/explainer-kit/references/visual-review.md +0 -71
  117. package/assets/skills/explainer-kit/schemas/author-request.v2.schema.json +0 -212
  118. package/assets/skills/explainer-kit/schemas/author-request.v3.schema.json +0 -241
  119. package/assets/skills/explainer-kit/schemas/author-result.v2.schema.json +0 -52
  120. package/assets/skills/explainer-kit/schemas/build-record.schema.json +0 -98
  121. package/assets/skills/explainer-kit/schemas/durability-evidence.schema.json +0 -53
  122. package/assets/skills/explainer-kit/schemas/publish-receipt.v1.schema.json +0 -97
  123. package/assets/skills/explainer-kit/schemas/publish-receipt.v2.schema.json +0 -215
  124. package/assets/skills/explainer-kit/schemas/publish-request.v1.schema.json +0 -32
  125. package/assets/skills/explainer-kit/schemas/publish-request.v2.schema.json +0 -34
  126. package/assets/skills/explainer-kit/schemas/run-request.schema.json +0 -147
  127. package/assets/skills/explainer-kit/schemas/set-plan.v1.schema.json +0 -149
  128. package/assets/skills/explainer-kit/schemas/terminal-evidence.v1.schema.json +0 -81
  129. package/assets/skills/explainer-kit/schemas/visual-review-evidence.v1.schema.json +0 -66
  130. package/assets/skills/explainer-kit/schemas/visual-review-request.v1.schema.json +0 -117
  131. package/assets/skills/explainer-kit/schemas/visual-review-result.v1.schema.json +0 -80
  132. package/assets/skills/explainer-kit/scripts/lib/catalog.mjs +0 -349
  133. package/assets/skills/explainer-kit/scripts/lib/content-approval.mjs +0 -418
  134. package/assets/skills/explainer-kit/scripts/lib/diagram.mjs +0 -514
  135. package/assets/skills/explainer-kit/scripts/lib/durability.mjs +0 -643
  136. package/assets/skills/explainer-kit/scripts/lib/fact-base.mjs +0 -664
  137. package/assets/skills/explainer-kit/scripts/lib/internal-references.mjs +0 -538
  138. package/assets/skills/explainer-kit/scripts/lib/markdown.mjs +0 -414
  139. package/assets/skills/explainer-kit/scripts/lib/publication-policy.mjs +0 -54
  140. package/assets/skills/explainer-kit/scripts/lib/records.mjs +0 -954
  141. package/assets/skills/explainer-kit/scripts/lib/render.mjs +0 -703
  142. package/assets/skills/explainer-kit/scripts/lib/s3-roots.mjs +0 -353
  143. package/assets/skills/explainer-kit/scripts/lib/s3-static.mjs +0 -759
  144. package/assets/skills/explainer-kit/scripts/lib/set-plan.mjs +0 -209
  145. package/assets/skills/explainer-kit/scripts/lib/source-backlinks.mjs +0 -218
  146. package/assets/skills/explainer-kit/scripts/lib/terminal-evidence.mjs +0 -157
  147. package/assets/skills/explainer-kit/scripts/lib/visual-review.mjs +0 -400
  148. package/assets/skills/explainer-kit/scripts/publish.mjs +0 -76
  149. package/assets/skills/explainer-kit/scripts/record-durability.mjs +0 -64
  150. package/assets/skills/explainer-kit/scripts/render-qa.mjs +0 -461
  151. package/assets/skills/explainer-kit/scripts/run.mjs +0 -3038
  152. package/assets/skills/explainer-kit/scripts/validate.mjs +0 -59
  153. package/assets/skills/oat-explainer-kit/references/author-callback.md +0 -53
  154. package/assets/skills/oat-explainer-kit/references/migration.md +0 -144
  155. package/assets/skills/oat-explainer-kit/references/visual-review-callback.md +0 -83
  156. package/assets/skills/oat-explainer-kit/scripts/derive-destination.mjs +0 -91
  157. package/assets/skills/oat-explainer-kit/scripts/finalize-tracked-run.mjs +0 -531
  158. package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +0 -371
  159. package/assets/skills/oat-explainer-kit/scripts/run.mjs +0 -905
  160. package/dist/commands/project/archive/explainer-source-backlinks.d.ts +0 -18
  161. package/dist/commands/project/archive/explainer-source-backlinks.d.ts.map +0 -1
  162. package/dist/commands/project/archive/explainer-source-backlinks.js +0 -27
  163. package/dist/commands/project/archive/explainer-terminal-evidence.d.ts +0 -29
  164. package/dist/commands/project/archive/explainer-terminal-evidence.d.ts.map +0 -1
  165. package/dist/commands/project/archive/explainer-terminal-evidence.js +0 -37
@@ -1,66 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "explainer-kit.visual-review-evidence/v1",
4
- "type": "object",
5
- "additionalProperties": false,
6
- "required": [
7
- "schemaVersion",
8
- "requestHash",
9
- "attempt",
10
- "disposition",
11
- "reasons"
12
- ],
13
- "properties": {
14
- "schemaVersion": {
15
- "const": "explainer-kit.visual-review-evidence/v1"
16
- },
17
- "requestHash": { "$ref": "#/$defs/hash" },
18
- "attempt": { "enum": [1, 2] },
19
- "disposition": { "enum": ["pass", "correct", "failed"] },
20
- "reasons": {
21
- "type": "array",
22
- "minItems": 0,
23
- "maxItems": 50,
24
- "items": { "$ref": "#/$defs/reason" }
25
- }
26
- },
27
- "$defs": {
28
- "hash": {
29
- "type": "string",
30
- "pattern": "^sha256:[a-f0-9]{64}$"
31
- },
32
- "reason": {
33
- "type": "object",
34
- "additionalProperties": false,
35
- "required": ["stage", "kind", "count"],
36
- "properties": {
37
- "stage": {
38
- "enum": [
39
- "planning",
40
- "authoring",
41
- "rendering",
42
- "link-validation",
43
- "browser-review",
44
- "visual-review",
45
- "durability",
46
- "finalization"
47
- ]
48
- },
49
- "kind": {
50
- "enum": [
51
- "finding",
52
- "provider-failure",
53
- "pipeline-failure",
54
- "superseded"
55
- ]
56
- },
57
- "artifactId": {
58
- "type": "string",
59
- "minLength": 1,
60
- "maxLength": 2000
61
- },
62
- "count": { "type": "integer", "minimum": 1, "maximum": 50 }
63
- }
64
- }
65
- }
66
- }
@@ -1,117 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "explainer-kit.visual-review-request/v1",
4
- "title": "VisualReviewRequestV1",
5
- "type": "object",
6
- "additionalProperties": false,
7
- "required": [
8
- "schemaVersion",
9
- "requestId",
10
- "requestHash",
11
- "browserRuntime",
12
- "captureIdentity",
13
- "plan",
14
- "renderedArtifacts"
15
- ],
16
- "properties": {
17
- "schemaVersion": {
18
- "const": "explainer-kit.visual-review-request/v1"
19
- },
20
- "requestId": { "$ref": "#/$defs/safeId" },
21
- "requestHash": { "$ref": "#/$defs/hash" },
22
- "browserRuntime": { "$ref": "#/$defs/browserRuntime" },
23
- "captureIdentity": { "$ref": "#/$defs/hash" },
24
- "plan": { "$ref": "explainer-kit.set-plan/v1" },
25
- "renderedArtifacts": {
26
- "type": "array",
27
- "items": { "$ref": "#/$defs/renderedArtifact" },
28
- "minItems": 1,
29
- "uniqueItems": true
30
- }
31
- },
32
- "$defs": {
33
- "safeRelativePath": {
34
- "type": "string",
35
- "minLength": 1,
36
- "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*\\\\).+$"
37
- },
38
- "safeId": {
39
- "type": "string",
40
- "pattern": "^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
41
- },
42
- "hash": {
43
- "type": "string",
44
- "pattern": "^sha256:[a-f0-9]{64}$"
45
- },
46
- "browserRuntime": {
47
- "type": "object",
48
- "additionalProperties": false,
49
- "required": ["kind", "name", "version"],
50
- "properties": {
51
- "kind": { "enum": ["launched", "fixture"] },
52
- "name": { "const": "chromium" },
53
- "version": { "type": "string", "minLength": 1 }
54
- }
55
- },
56
- "renderedArtifact": {
57
- "type": "object",
58
- "additionalProperties": false,
59
- "required": ["artifactId", "renderedPath", "renderedHash", "evidence"],
60
- "properties": {
61
- "artifactId": {
62
- "type": "string",
63
- "pattern": "^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
64
- },
65
- "renderedPath": { "$ref": "#/$defs/safeRelativePath" },
66
- "renderedHash": { "$ref": "#/$defs/hash" },
67
- "cohesionObservations": {
68
- "type": "array",
69
- "items": { "$ref": "#/$defs/cohesionObservation" },
70
- "uniqueItems": true
71
- },
72
- "evidence": {
73
- "type": "array",
74
- "items": { "$ref": "#/$defs/evidence" },
75
- "minItems": 1,
76
- "uniqueItems": true
77
- }
78
- }
79
- },
80
- "cohesionObservation": {
81
- "type": "object",
82
- "additionalProperties": false,
83
- "required": ["artifactId", "contentHash", "group", "claim", "value"],
84
- "properties": {
85
- "artifactId": { "$ref": "#/$defs/safeId" },
86
- "contentHash": { "$ref": "#/$defs/hash" },
87
- "group": {
88
- "enum": ["terminology", "statuses", "numericClaims"]
89
- },
90
- "claim": { "type": "string", "minLength": 1 },
91
- "value": {
92
- "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "number" }]
93
- }
94
- }
95
- },
96
- "evidence": {
97
- "type": "object",
98
- "additionalProperties": false,
99
- "required": [
100
- "viewport",
101
- "screenshotPath",
102
- "screenshotHash",
103
- "metricsPath",
104
- "metricsHash",
105
- "captureIdentity"
106
- ],
107
- "properties": {
108
- "viewport": { "enum": ["desktop", "tablet", "mobile"] },
109
- "screenshotPath": { "$ref": "#/$defs/safeRelativePath" },
110
- "screenshotHash": { "$ref": "#/$defs/hash" },
111
- "metricsPath": { "$ref": "#/$defs/safeRelativePath" },
112
- "metricsHash": { "$ref": "#/$defs/hash" },
113
- "captureIdentity": { "$ref": "#/$defs/hash" }
114
- }
115
- }
116
- }
117
- }
@@ -1,80 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "explainer-kit.visual-review-result/v1",
4
- "title": "VisualReviewResultV1",
5
- "type": "object",
6
- "additionalProperties": false,
7
- "required": [
8
- "schemaVersion",
9
- "reviewId",
10
- "requestId",
11
- "requestHash",
12
- "reviewedAt",
13
- "disposition",
14
- "artifactIds",
15
- "findings"
16
- ],
17
- "properties": {
18
- "schemaVersion": {
19
- "const": "explainer-kit.visual-review-result/v1"
20
- },
21
- "reviewId": { "$ref": "#/$defs/safeId" },
22
- "requestId": { "$ref": "#/$defs/safeId" },
23
- "requestHash": { "$ref": "#/$defs/hash" },
24
- "reviewedAt": { "type": "string", "format": "date-time" },
25
- "disposition": { "enum": ["pass", "correct", "fail"] },
26
- "artifactIds": {
27
- "type": "array",
28
- "items": { "$ref": "#/$defs/safeId" },
29
- "minItems": 1,
30
- "uniqueItems": true
31
- },
32
- "findings": {
33
- "type": "array",
34
- "items": { "$ref": "#/$defs/finding" },
35
- "uniqueItems": true
36
- }
37
- },
38
- "$defs": {
39
- "safeId": {
40
- "type": "string",
41
- "pattern": "^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
42
- },
43
- "hash": {
44
- "type": "string",
45
- "pattern": "^sha256:[a-f0-9]{64}$"
46
- },
47
- "finding": {
48
- "type": "object",
49
- "additionalProperties": false,
50
- "required": [
51
- "artifactId",
52
- "rubric",
53
- "severity",
54
- "evidence",
55
- "correction"
56
- ],
57
- "properties": {
58
- "artifactId": { "$ref": "#/$defs/safeId" },
59
- "rubric": {
60
- "enum": [
61
- "first-viewport",
62
- "hierarchy",
63
- "representation",
64
- "legibility",
65
- "medium-fit",
66
- "cohesion",
67
- "coverage",
68
- "redundancy",
69
- "interaction"
70
- ]
71
- },
72
- "severity": {
73
- "enum": ["critical", "important", "medium", "minor"]
74
- },
75
- "evidence": { "type": "string", "minLength": 1 },
76
- "correction": { "type": "string", "minLength": 1 }
77
- }
78
- }
79
- }
80
- }
@@ -1,349 +0,0 @@
1
- import { canonicalStringify, validateContract } from './contracts.mjs';
2
-
3
- /**
4
- * Bumped to v2 when the root `publicVerification` marker was added below.
5
- *
6
- * The catalog is serialized, hashed, and recorded as an auxiliary artifact in
7
- * the publish receipt, so adding a root key changes the bytes. Released
8
- * `0.2.30` emits the v1 shape, which has no such key: leaving both shapes under
9
- * one version string would put two different wire formats behind one identifier
10
- * for anyone parsing `catalog.json` by declared version.
11
- *
12
- * There is deliberately no v1 read path. Unlike `publish-request/v1` and
13
- * `publish-receipt/v1`, which are retained because a v1 record can still be
14
- * replayed, the catalog is regenerate-only and never reconstructed from a v1
15
- * receipt: every rebuild site short-circuits on a non-v2 receipt
16
- * (`durability.mjs` verifyPublishEvidence, `run.mjs` publicationValidationContext,
17
- * and the normative `private-wrapper.mjs` example), and `0.2.30` emits
18
- * `publish-receipt/v1` only. Accepting v1 here would be unreachable code. If a
19
- * future reader ever does replay a v1-era catalog, it must reconstruct that
20
- * shape explicitly rather than relaxing this constant.
21
- */
22
- const CATALOG_SCHEMA_VERSION = 'explainer-kit.initiative-catalog/v2';
23
-
24
- export const PUBLIC_VERIFICATION_REQUIRED = 'required';
25
- export const PUBLIC_VERIFICATION_SKIPPED_BY_POLICY = 'skipped-by-policy';
26
-
27
- /**
28
- * Single source of truth for public-verification state.
29
- *
30
- * The catalog is built, serialized and hashed *before* the first upload and long
31
- * before any per-artifact public verification runs, so it can never carry a
32
- * verification *outcome*: writing one would either require re-uploading the
33
- * catalog or invalidate the hash the receipt records for it. It therefore
34
- * carries verification *policy* only, and the authoritative outcome stays in the
35
- * run's publish receipt, which the catalog's `runId` identifies.
36
- *
37
- * Both the catalog marker and the receipt's skipped status are derived from this
38
- * one result so the two cannot drift apart.
39
- */
40
- export function resolvePublicVerificationPolicy(publicAccess) {
41
- return publicAccess === 'protected'
42
- ? {
43
- publicAccess: 'protected',
44
- catalogMarker: PUBLIC_VERIFICATION_SKIPPED_BY_POLICY,
45
- verifyPublicly: false,
46
- receiptSkipStatus: 'skipped-protected',
47
- }
48
- : {
49
- publicAccess: 'public',
50
- catalogMarker: PUBLIC_VERIFICATION_REQUIRED,
51
- verifyPublicly: true,
52
- receiptSkipStatus: 'skipped-protected',
53
- };
54
- }
55
-
56
- /**
57
- * The public-access policy must be stated explicitly, never defaulted.
58
- *
59
- * It selects the catalog's `publicVerification` marker, which is part of the
60
- * serialized bytes and therefore of the catalog hash the receipt records. An
61
- * options bag that silently defaulted to the permissive `public` branch is what
62
- * let four call sites omit it and still produce a plausible-looking catalog:
63
- * the connector published `skipped-by-policy` for a `protected` run while the
64
- * durability verifier rebuilt `required`, so the hashes diverged and no
65
- * `protected` publication could ever be recorded durable.
66
- *
67
- * Passing `{ publicAccess: undefined }` is allowed and means `public` — that is
68
- * the correct reading for `publish-request/v1` and `publish-receipt/v1`, which
69
- * have no such field. Omitting the key entirely is a programming error.
70
- */
71
- function requiredPublicAccess(options, caller) {
72
- if (
73
- options === null ||
74
- typeof options !== 'object' ||
75
- !('publicAccess' in options)
76
- ) {
77
- throw new TypeError(
78
- `${caller} requires an explicit { publicAccess } policy: it selects the catalog's publicVerification marker and therefore its hash. Pass { publicAccess: undefined } for v1 records, which are public by definition.`,
79
- );
80
- }
81
- return options.publicAccess;
82
- }
83
-
84
- export function initiativeCatalogPath(slug) {
85
- if (typeof slug !== 'string' || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) {
86
- throw new TypeError('Initiative catalog requires a safe initiative slug.');
87
- }
88
- return `site/initiatives/${slug}/catalog.json`;
89
- }
90
-
91
- export function catalogFromManifest(manifest, publicBaseUrl, options) {
92
- const publicAccess = requiredPublicAccess(options, 'catalogFromManifest');
93
- const validation = validateContract('manifest', manifest);
94
- if (!validation.valid) {
95
- throw new TypeError(
96
- `Initiative catalog requires a valid finalized manifest: ${validation.errors[0].message}`,
97
- );
98
- }
99
- const baseUrl = normalizePublicBaseUrl(publicBaseUrl);
100
- const artifacts = manifest.artifacts.map((artifact) => {
101
- if (
102
- artifact.status !== 'built' ||
103
- typeof artifact.renderedPath !== 'string' ||
104
- !artifact.renderedPath.startsWith('site/') ||
105
- typeof artifact.hash !== 'string'
106
- ) {
107
- throw new TypeError(
108
- `Initiative catalog cannot publish non-built artifact ${artifact.id}.`,
109
- );
110
- }
111
- return {
112
- id: artifact.id,
113
- type: artifact.type,
114
- status: artifact.status,
115
- renderedPath: artifact.renderedPath,
116
- ...(artifact.mediaType && { mediaType: artifact.mediaType }),
117
- hash: artifact.hash,
118
- url: absoluteArtifactUrl(baseUrl, artifact.renderedPath),
119
- };
120
- });
121
-
122
- return {
123
- schemaVersion: CATALOG_SCHEMA_VERSION,
124
- runId: manifest.runId,
125
- slug: manifest.slug,
126
- recipe: structuredClone(manifest.recipe),
127
- createdAt: manifest.createdAt,
128
- // Policy, never outcome. See resolvePublicVerificationPolicy.
129
- publicVerification:
130
- resolvePublicVerificationPolicy(publicAccess).catalogMarker,
131
- artifacts,
132
- sourceBacklinks: structuredClone(manifest.source.backlinks ?? []),
133
- };
134
- }
135
-
136
- export function validateInitiativeCatalog(
137
- catalog,
138
- manifest,
139
- publicBaseUrl,
140
- options,
141
- ) {
142
- const publicAccess = requiredPublicAccess(
143
- options,
144
- 'validateInitiativeCatalog',
145
- );
146
- const errors = [];
147
- let normalizedPublicBaseUrl;
148
- try {
149
- normalizedPublicBaseUrl = normalizePublicBaseUrl(publicBaseUrl);
150
- } catch (error) {
151
- add(
152
- errors,
153
- '$.artifacts',
154
- 'catalog-public-root',
155
- error instanceof Error
156
- ? error.message
157
- : 'Initiative catalog requires an HTTPS public base URL.',
158
- );
159
- }
160
- if (!catalog || typeof catalog !== 'object' || Array.isArray(catalog)) {
161
- return {
162
- valid: false,
163
- errors: [
164
- {
165
- path: '$',
166
- code: 'catalog-shape',
167
- message: 'Initiative catalog must be an object.',
168
- },
169
- ],
170
- };
171
- }
172
- const expectedRootKeys = new Set([
173
- 'schemaVersion',
174
- 'runId',
175
- 'slug',
176
- 'recipe',
177
- 'createdAt',
178
- 'publicVerification',
179
- 'artifacts',
180
- 'sourceBacklinks',
181
- ]);
182
- if (Object.keys(catalog).some((key) => !expectedRootKeys.has(key))) {
183
- add(
184
- errors,
185
- '$',
186
- 'catalog-shape',
187
- 'Initiative catalog contains an unknown property.',
188
- );
189
- }
190
-
191
- for (const [field, expected] of [
192
- ['schemaVersion', CATALOG_SCHEMA_VERSION],
193
- ['runId', manifest?.runId],
194
- ['slug', manifest?.slug],
195
- ['createdAt', manifest?.createdAt],
196
- ]) {
197
- if (catalog[field] !== expected) {
198
- add(
199
- errors,
200
- `$.${field}`,
201
- 'catalog-manifest-mismatch',
202
- `Catalog ${field} does not match the finalized manifest.`,
203
- );
204
- }
205
- }
206
- // Derived from the same resolver the receipt status comes from, so a catalog
207
- // that disagrees with the run's resolved verification policy is rejected.
208
- const expectedPublicVerification =
209
- resolvePublicVerificationPolicy(publicAccess).catalogMarker;
210
- if (catalog.publicVerification !== expectedPublicVerification) {
211
- add(
212
- errors,
213
- '$.publicVerification',
214
- 'catalog-verification-policy',
215
- `Catalog publicVerification must be ${expectedPublicVerification} for this run's public access policy.`,
216
- );
217
- }
218
-
219
- if (
220
- canonicalStringify(catalog.recipe) !== canonicalStringify(manifest?.recipe)
221
- ) {
222
- add(
223
- errors,
224
- '$.recipe',
225
- 'catalog-manifest-mismatch',
226
- 'Catalog recipe does not match the finalized manifest.',
227
- );
228
- }
229
-
230
- const catalogArtifacts = Array.isArray(catalog.artifacts)
231
- ? catalog.artifacts
232
- : [];
233
- const manifestArtifacts = Array.isArray(manifest?.artifacts)
234
- ? manifest.artifacts
235
- : [];
236
- if (
237
- !Array.isArray(catalog.artifacts) ||
238
- catalogArtifacts.length !== manifestArtifacts.length
239
- ) {
240
- add(
241
- errors,
242
- '$.artifacts',
243
- 'catalog-artifact-parity',
244
- 'Catalog artifacts must exactly cover the finalized manifest artifacts.',
245
- );
246
- }
247
- for (const [index, artifact] of manifestArtifacts.entries()) {
248
- const entry = catalogArtifacts[index];
249
- const expected = {
250
- id: artifact.id,
251
- type: artifact.type,
252
- status: artifact.status,
253
- renderedPath: artifact.renderedPath,
254
- ...(artifact.mediaType && { mediaType: artifact.mediaType }),
255
- hash: artifact.hash,
256
- url:
257
- normalizedPublicBaseUrl === undefined
258
- ? undefined
259
- : absoluteArtifactUrl(normalizedPublicBaseUrl, artifact.renderedPath),
260
- };
261
- const actual = entry && typeof entry === 'object' ? entry : undefined;
262
- if (canonicalStringify(actual) !== canonicalStringify(expected)) {
263
- add(
264
- errors,
265
- `$.artifacts[${index}]`,
266
- 'catalog-artifact-mismatch',
267
- `Catalog artifact ${artifact.id} is stale or does not match the finalized manifest.`,
268
- );
269
- }
270
- }
271
-
272
- if (
273
- canonicalStringify(catalog.sourceBacklinks) !==
274
- canonicalStringify(manifest?.source?.backlinks ?? [])
275
- ) {
276
- add(
277
- errors,
278
- '$.sourceBacklinks',
279
- 'catalog-source-mismatch',
280
- 'Catalog source backlinks do not match the finalized manifest.',
281
- );
282
- }
283
- for (const [index, backlink] of (Array.isArray(catalog.sourceBacklinks)
284
- ? catalog.sourceBacklinks
285
- : []
286
- ).entries()) {
287
- if (
288
- typeof backlink?.sourceId !== 'string' ||
289
- !isImmutableGithubUrl(backlink?.url)
290
- ) {
291
- add(
292
- errors,
293
- `$.sourceBacklinks[${index}]`,
294
- 'catalog-source-url',
295
- 'Catalog source backlinks must be immutable absolute GitHub URLs.',
296
- );
297
- }
298
- }
299
-
300
- return { valid: errors.length === 0, errors };
301
- }
302
-
303
- export function serializeInitiativeCatalog(catalog) {
304
- return `${JSON.stringify(catalog, null, 2)}\n`;
305
- }
306
-
307
- function normalizePublicBaseUrl(value) {
308
- let url;
309
- try {
310
- url = new URL(value);
311
- } catch {
312
- throw new TypeError(
313
- 'Initiative catalog requires an HTTPS public base URL.',
314
- );
315
- }
316
- if (
317
- url.protocol !== 'https:' ||
318
- url.username ||
319
- url.password ||
320
- url.search ||
321
- url.hash
322
- ) {
323
- throw new TypeError(
324
- 'Initiative catalog requires credential-free HTTPS without query or fragment.',
325
- );
326
- }
327
- return url.href.replace(/\/+$/, '');
328
- }
329
-
330
- function absoluteArtifactUrl(baseUrl, renderedPath) {
331
- return `${baseUrl}/${renderedPath
332
- .slice('site/'.length)
333
- .split('/')
334
- .map((part) => encodeURIComponent(part))
335
- .join('/')}`;
336
- }
337
-
338
- function isImmutableGithubUrl(value) {
339
- return (
340
- typeof value === 'string' &&
341
- /^https:\/\/github\.com\/[^/]+\/[^/]+\/blob\/[a-f0-9]{40}\/.+#L[1-9][0-9]*(?:-L[1-9][0-9]*)?$/.test(
342
- value,
343
- )
344
- );
345
- }
346
-
347
- function add(errors, path, code, message) {
348
- errors.push({ path, code, message });
349
- }