@open-agent-toolkit/cli 0.2.29 → 0.2.31

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 (87) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/configuration.md +52 -1
  3. package/assets/docs/contributing/code.md +19 -7
  4. package/assets/docs/contributing/explainer-kit-verification.md +9 -1
  5. package/assets/docs/contributing/skills.md +9 -0
  6. package/assets/docs/workflows/projects/artifacts.md +5 -0
  7. package/assets/docs/workflows/projects/index.md +1 -0
  8. package/assets/docs/workflows/projects/lifecycle.md +23 -0
  9. package/assets/docs/workflows/projects/retro.md +261 -0
  10. package/assets/docs/workflows/skills/explainer-kit-providers.md +16 -5
  11. package/assets/docs/workflows/skills/explainer-kit.md +98 -38
  12. package/assets/docs/workflows/skills/index.md +6 -0
  13. package/assets/public-package-versions.json +4 -4
  14. package/assets/skills/explainer-kit/SKILL.md +33 -8
  15. package/assets/skills/explainer-kit/briefs/project-recap.md +25 -7
  16. package/assets/skills/explainer-kit/recipes/project-recap.v2.json +72 -0
  17. package/assets/skills/explainer-kit/references/contracts.md +49 -17
  18. package/assets/skills/explainer-kit/references/destination-contract.md +141 -25
  19. package/assets/skills/explainer-kit/references/extension-contract.md +19 -10
  20. package/assets/skills/explainer-kit/references/visual-authoring.md +24 -0
  21. package/assets/skills/explainer-kit/references/visual-review.md +19 -5
  22. package/assets/skills/explainer-kit/schemas/author-request.v3.schema.json +241 -0
  23. package/assets/skills/explainer-kit/schemas/publish-receipt.v2.schema.json +215 -0
  24. package/assets/skills/explainer-kit/schemas/publish-request.v2.schema.json +34 -0
  25. package/assets/skills/explainer-kit/schemas/run-request.schema.json +4 -1
  26. package/assets/skills/explainer-kit/schemas/terminal-evidence.v1.schema.json +81 -0
  27. package/assets/skills/explainer-kit/schemas/visual-review-evidence.v1.schema.json +66 -0
  28. package/assets/skills/explainer-kit/scripts/lib/catalog.mjs +109 -3
  29. package/assets/skills/explainer-kit/scripts/lib/contracts.mjs +550 -17
  30. package/assets/skills/explainer-kit/scripts/lib/durability.mjs +90 -8
  31. package/assets/skills/explainer-kit/scripts/lib/fs-safe.mjs +5 -0
  32. package/assets/skills/explainer-kit/scripts/lib/internal-references.mjs +538 -0
  33. package/assets/skills/explainer-kit/scripts/lib/package-coverage.mjs +129 -11
  34. package/assets/skills/explainer-kit/scripts/lib/publication-policy.mjs +54 -0
  35. package/assets/skills/explainer-kit/scripts/lib/recipes.mjs +2 -1
  36. package/assets/skills/explainer-kit/scripts/lib/records.mjs +139 -22
  37. package/assets/skills/explainer-kit/scripts/lib/s3-roots.mjs +353 -0
  38. package/assets/skills/explainer-kit/scripts/lib/s3-static.mjs +237 -107
  39. package/assets/skills/explainer-kit/scripts/lib/set-plan.mjs +1 -0
  40. package/assets/skills/explainer-kit/scripts/lib/terminal-evidence.mjs +157 -0
  41. package/assets/skills/explainer-kit/scripts/lib/visual-review.mjs +26 -6
  42. package/assets/skills/explainer-kit/scripts/run.mjs +1006 -144
  43. package/assets/skills/oat-explainer-kit/SKILL.md +16 -3
  44. package/assets/skills/oat-explainer-kit/references/config-contract.md +13 -8
  45. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +50 -6
  46. package/assets/skills/oat-explainer-kit/references/migration.md +2 -1
  47. package/assets/skills/oat-explainer-kit/references/visual-review-callback.md +11 -0
  48. package/assets/skills/oat-explainer-kit/scripts/bind-project-sources.mjs +37 -15
  49. package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +83 -0
  50. package/assets/skills/oat-explainer-kit/scripts/derive-destination.mjs +91 -0
  51. package/assets/skills/oat-explainer-kit/scripts/finalize-tracked-run.mjs +66 -10
  52. package/assets/skills/oat-explainer-kit/scripts/resolve-config.mjs +60 -21
  53. package/assets/skills/oat-explainer-kit/scripts/resolve-paths.mjs +52 -8
  54. package/assets/skills/oat-explainer-kit/scripts/run.mjs +271 -36
  55. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +38 -38
  56. package/assets/skills/oat-project-complete/SKILL.md +60 -8
  57. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +38 -38
  58. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  59. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +28 -6
  60. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +38 -38
  61. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +38 -38
  62. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +38 -38
  63. package/assets/skills/oat-project-retro/SKILL.md +310 -0
  64. package/assets/skills/oat-project-retro/references/apply-procedure.md +212 -0
  65. package/assets/skills/oat-project-retro/references/evidence-and-lanes.md +91 -0
  66. package/assets/skills/oat-project-retro/references/retro-quality-bar.md +100 -0
  67. package/assets/skills/oat-project-retro-file/SKILL.md +387 -0
  68. package/assets/templates/project-retro.md +236 -0
  69. package/dist/commands/config/index.d.ts.map +1 -1
  70. package/dist/commands/config/index.js +99 -0
  71. package/dist/commands/init/tools/shared/skill-manifest.d.ts +2 -2
  72. package/dist/commands/init/tools/shared/skill-manifest.d.ts.map +1 -1
  73. package/dist/commands/init/tools/shared/skill-manifest.js +3 -0
  74. package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
  75. package/dist/commands/project/archive/archive-utils.js +57 -7
  76. package/dist/commands/project/archive/explainer-terminal-evidence.d.ts +29 -0
  77. package/dist/commands/project/archive/explainer-terminal-evidence.d.ts.map +1 -0
  78. package/dist/commands/project/archive/explainer-terminal-evidence.js +37 -0
  79. package/dist/config/oat-config.d.ts +14 -1
  80. package/dist/config/oat-config.d.ts.map +1 -1
  81. package/dist/config/oat-config.js +42 -2
  82. package/dist/config/resolve.d.ts.map +1 -1
  83. package/dist/config/resolve.js +9 -0
  84. package/package.json +2 -2
  85. /package/assets/skills/explainer-kit/recipes/{project-recap.json → project-recap.v1.json} +0 -0
  86. /package/assets/skills/explainer-kit/schemas/{publish-receipt.schema.json → publish-receipt.v1.schema.json} +0 -0
  87. /package/assets/skills/explainer-kit/schemas/{publish-request.schema.json → publish-request.v1.schema.json} +0 -0
@@ -0,0 +1,215 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "explainer-kit.publish-receipt/v2",
4
+ "title": "PublishReceiptV2",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "provider",
10
+ "publishedAt",
11
+ "publicAccess",
12
+ "roots",
13
+ "sentinel",
14
+ "artifacts"
15
+ ],
16
+ "properties": {
17
+ "schemaVersion": {
18
+ "const": "explainer-kit.publish-receipt/v2"
19
+ },
20
+ "provider": {
21
+ "const": "s3-static"
22
+ },
23
+ "publishedAt": {
24
+ "type": "string",
25
+ "format": "date-time"
26
+ },
27
+ "publicAccess": {
28
+ "enum": ["public", "protected"]
29
+ },
30
+ "roots": {
31
+ "type": "object",
32
+ "additionalProperties": false,
33
+ "required": ["s3Uri", "publicBaseUrl"],
34
+ "properties": {
35
+ "s3Uri": {
36
+ "type": "string",
37
+ "pattern": "^s3://[a-z0-9][a-z0-9.-]{1,61}[a-z0-9](?:/[^\\s/?#\\\\]+(?:/[^\\s/?#\\\\]+)*)?$"
38
+ },
39
+ "publicBaseUrl": {
40
+ "type": "string",
41
+ "pattern": "^https://[^\\s/?#@]+(?::[0-9]+)?(?:/[^\\s/?#\\\\]+(?:/[^\\s/?#\\\\]+)*)?$"
42
+ }
43
+ }
44
+ },
45
+ "sentinel": {
46
+ "type": "object",
47
+ "additionalProperties": false,
48
+ "required": [
49
+ "relativePath",
50
+ "objectVerification",
51
+ "publicVerification",
52
+ "deleted"
53
+ ],
54
+ "properties": {
55
+ "relativePath": {
56
+ "$ref": "#/$defs/safeRelativePath"
57
+ },
58
+ "objectVerification": {
59
+ "$ref": "#/$defs/objectVerification"
60
+ },
61
+ "publicVerification": {
62
+ "$ref": "#/$defs/publicVerification"
63
+ },
64
+ "deleted": {
65
+ "type": "boolean"
66
+ }
67
+ }
68
+ },
69
+ "artifacts": {
70
+ "type": "array",
71
+ "items": {
72
+ "$ref": "#/$defs/artifact"
73
+ },
74
+ "uniqueItems": true
75
+ },
76
+ "publicRootPolicy": {
77
+ "description": "Present only when EXPLAINER_KIT_ALLOW_PRIVATE_PUBLIC_ROOT was set and the public root was in fact a non-public address, so a publication made with the anti-SSRF control disabled is distinguishable in durable evidence.",
78
+ "enum": ["private-allowed"]
79
+ }
80
+ },
81
+ "$defs": {
82
+ "hash": {
83
+ "type": "string",
84
+ "pattern": "^sha256:[a-f0-9]{64}$"
85
+ },
86
+ "safeRelativePath": {
87
+ "type": "string",
88
+ "minLength": 1,
89
+ "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"
90
+ },
91
+ "source": {
92
+ "oneOf": [
93
+ {
94
+ "type": "object",
95
+ "additionalProperties": false,
96
+ "required": ["kind", "artifactId"],
97
+ "properties": {
98
+ "kind": {
99
+ "const": "manifest"
100
+ },
101
+ "artifactId": {
102
+ "type": "string",
103
+ "minLength": 1
104
+ }
105
+ }
106
+ },
107
+ {
108
+ "type": "object",
109
+ "additionalProperties": false,
110
+ "required": ["kind", "name"],
111
+ "properties": {
112
+ "kind": {
113
+ "const": "auxiliary"
114
+ },
115
+ "name": {
116
+ "type": "string",
117
+ "minLength": 1
118
+ }
119
+ }
120
+ }
121
+ ]
122
+ },
123
+ "objectVerification": {
124
+ "type": "object",
125
+ "additionalProperties": false,
126
+ "required": ["status", "method", "hash"],
127
+ "properties": {
128
+ "status": {
129
+ "const": "verified"
130
+ },
131
+ "method": {
132
+ "enum": ["service-checksum", "authenticated-download"]
133
+ },
134
+ "hash": {
135
+ "$ref": "#/$defs/hash"
136
+ }
137
+ }
138
+ },
139
+ "publicVerification": {
140
+ "oneOf": [
141
+ {
142
+ "type": "object",
143
+ "additionalProperties": false,
144
+ "required": ["status", "httpStatus", "hash"],
145
+ "properties": {
146
+ "status": {
147
+ "const": "verified"
148
+ },
149
+ "httpStatus": {
150
+ "type": "integer",
151
+ "minimum": 200,
152
+ "maximum": 299
153
+ },
154
+ "hash": {
155
+ "$ref": "#/$defs/hash"
156
+ }
157
+ }
158
+ },
159
+ {
160
+ "type": "object",
161
+ "additionalProperties": false,
162
+ "required": ["status"],
163
+ "properties": {
164
+ "status": {
165
+ "const": "skipped-protected"
166
+ }
167
+ }
168
+ }
169
+ ]
170
+ },
171
+ "artifact": {
172
+ "type": "object",
173
+ "additionalProperties": false,
174
+ "required": [
175
+ "source",
176
+ "relativePath",
177
+ "hash",
178
+ "s3Uri",
179
+ "publicUrl",
180
+ "contentType",
181
+ "objectVerification",
182
+ "publicVerification"
183
+ ],
184
+ "properties": {
185
+ "source": {
186
+ "$ref": "#/$defs/source"
187
+ },
188
+ "relativePath": {
189
+ "$ref": "#/$defs/safeRelativePath"
190
+ },
191
+ "hash": {
192
+ "$ref": "#/$defs/hash"
193
+ },
194
+ "s3Uri": {
195
+ "type": "string",
196
+ "pattern": "^s3://[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]/[^\\s?#\\\\]+$"
197
+ },
198
+ "publicUrl": {
199
+ "type": "string",
200
+ "pattern": "^https://[^\\s/?#@]+(?::[0-9]+)?/[^\\s?#\\\\]+$"
201
+ },
202
+ "contentType": {
203
+ "type": "string",
204
+ "minLength": 1
205
+ },
206
+ "objectVerification": {
207
+ "$ref": "#/$defs/objectVerification"
208
+ },
209
+ "publicVerification": {
210
+ "$ref": "#/$defs/publicVerification"
211
+ }
212
+ }
213
+ }
214
+ }
215
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "explainer-kit.publish-request/v2",
4
+ "title": "S3StaticPublishRequestV2",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "provider",
10
+ "s3Uri",
11
+ "publicBaseUrl",
12
+ "awsRegion",
13
+ "publicAccess",
14
+ "siteRoot",
15
+ "manifestPath"
16
+ ],
17
+ "properties": {
18
+ "schemaVersion": { "const": "explainer-kit.publish-request/v2" },
19
+ "provider": { "const": "s3-static" },
20
+ "s3Uri": {
21
+ "type": "string",
22
+ "pattern": "^s3://[a-z0-9][a-z0-9.-]{1,61}[a-z0-9](?:/[^\\s/?#\\\\]+(?:/[^\\s/?#\\\\]+)*)?/?$"
23
+ },
24
+ "publicBaseUrl": {
25
+ "type": "string",
26
+ "pattern": "^https://[^\\s/?#@]+(?::[0-9]+)?(?:/[^\\s/?#\\\\]+(?:/[^\\s/?#\\\\]+)*)?/?$"
27
+ },
28
+ "awsRegion": { "type": "string", "minLength": 1 },
29
+ "awsProfile": { "type": "string", "minLength": 1 },
30
+ "publicAccess": { "enum": ["public", "protected"] },
31
+ "siteRoot": { "type": "string", "minLength": 1 },
32
+ "manifestPath": { "type": "string", "minLength": 1 }
33
+ }
34
+ }
@@ -46,7 +46,10 @@
46
46
  "enum": ["none", "commit", "publish"]
47
47
  },
48
48
  "publish": {
49
- "$ref": "explainer-kit.publish-request/v1"
49
+ "oneOf": [
50
+ { "$ref": "explainer-kit.publish-request/v1" },
51
+ { "$ref": "explainer-kit.publish-request/v2" }
52
+ ]
50
53
  }
51
54
  }
52
55
  },
@@ -0,0 +1,81 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "explainer-kit.terminal-evidence/v1",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": [
7
+ "schemaVersion",
8
+ "runId",
9
+ "outcome",
10
+ "reasons",
11
+ "evidenceDisposition"
12
+ ],
13
+ "properties": {
14
+ "schemaVersion": {
15
+ "const": "explainer-kit.terminal-evidence/v1"
16
+ },
17
+ "runId": { "type": "string", "minLength": 1, "maxLength": 2000 },
18
+ "outcome": {
19
+ "enum": ["built-needs-review", "failed"]
20
+ },
21
+ "manifestHash": { "$ref": "#/$defs/hash" },
22
+ "reasons": {
23
+ "type": "array",
24
+ "minItems": 1,
25
+ "maxItems": 50,
26
+ "items": { "$ref": "#/$defs/reason" }
27
+ },
28
+ "evidenceDisposition": {
29
+ "enum": ["retained", "partial", "unavailable", "superseded"]
30
+ },
31
+ "supersededBy": { "$ref": "#/$defs/supersededBy" }
32
+ },
33
+ "$defs": {
34
+ "hash": {
35
+ "type": "string",
36
+ "pattern": "^sha256:[a-f0-9]{64}$"
37
+ },
38
+ "reason": {
39
+ "type": "object",
40
+ "additionalProperties": false,
41
+ "required": ["stage", "kind", "count"],
42
+ "properties": {
43
+ "stage": {
44
+ "enum": [
45
+ "planning",
46
+ "authoring",
47
+ "rendering",
48
+ "link-validation",
49
+ "browser-review",
50
+ "visual-review",
51
+ "durability",
52
+ "finalization"
53
+ ]
54
+ },
55
+ "kind": {
56
+ "enum": [
57
+ "finding",
58
+ "provider-failure",
59
+ "pipeline-failure",
60
+ "superseded"
61
+ ]
62
+ },
63
+ "artifactId": {
64
+ "type": "string",
65
+ "minLength": 1,
66
+ "maxLength": 2000
67
+ },
68
+ "count": { "type": "integer", "minimum": 1, "maximum": 50 }
69
+ }
70
+ },
71
+ "supersededBy": {
72
+ "type": "object",
73
+ "additionalProperties": false,
74
+ "required": ["runId", "manifestHash"],
75
+ "properties": {
76
+ "runId": { "type": "string", "minLength": 1, "maxLength": 2000 },
77
+ "manifestHash": { "$ref": "#/$defs/hash" }
78
+ }
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,66 @@
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,6 +1,85 @@
1
1
  import { canonicalStringify, validateContract } from './contracts.mjs';
2
2
 
3
- const CATALOG_SCHEMA_VERSION = 'explainer-kit.initiative-catalog/v1';
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
+ }
4
83
 
5
84
  export function initiativeCatalogPath(slug) {
6
85
  if (typeof slug !== 'string' || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) {
@@ -9,7 +88,8 @@ export function initiativeCatalogPath(slug) {
9
88
  return `site/initiatives/${slug}/catalog.json`;
10
89
  }
11
90
 
12
- export function catalogFromManifest(manifest, publicBaseUrl) {
91
+ export function catalogFromManifest(manifest, publicBaseUrl, options) {
92
+ const publicAccess = requiredPublicAccess(options, 'catalogFromManifest');
13
93
  const validation = validateContract('manifest', manifest);
14
94
  if (!validation.valid) {
15
95
  throw new TypeError(
@@ -45,12 +125,24 @@ export function catalogFromManifest(manifest, publicBaseUrl) {
45
125
  slug: manifest.slug,
46
126
  recipe: structuredClone(manifest.recipe),
47
127
  createdAt: manifest.createdAt,
128
+ // Policy, never outcome. See resolvePublicVerificationPolicy.
129
+ publicVerification:
130
+ resolvePublicVerificationPolicy(publicAccess).catalogMarker,
48
131
  artifacts,
49
132
  sourceBacklinks: structuredClone(manifest.source.backlinks ?? []),
50
133
  };
51
134
  }
52
135
 
53
- export function validateInitiativeCatalog(catalog, manifest, publicBaseUrl) {
136
+ export function validateInitiativeCatalog(
137
+ catalog,
138
+ manifest,
139
+ publicBaseUrl,
140
+ options,
141
+ ) {
142
+ const publicAccess = requiredPublicAccess(
143
+ options,
144
+ 'validateInitiativeCatalog',
145
+ );
54
146
  const errors = [];
55
147
  let normalizedPublicBaseUrl;
56
148
  try {
@@ -83,6 +175,7 @@ export function validateInitiativeCatalog(catalog, manifest, publicBaseUrl) {
83
175
  'slug',
84
176
  'recipe',
85
177
  'createdAt',
178
+ 'publicVerification',
86
179
  'artifacts',
87
180
  'sourceBacklinks',
88
181
  ]);
@@ -110,6 +203,19 @@ export function validateInitiativeCatalog(catalog, manifest, publicBaseUrl) {
110
203
  );
111
204
  }
112
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
+
113
219
  if (
114
220
  canonicalStringify(catalog.recipe) !== canonicalStringify(manifest?.recipe)
115
221
  ) {