@open-agent-toolkit/cli 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/assets/docs/cli-utilities/configuration.md +40 -0
  2. package/assets/docs/cli-utilities/tool-packs.md +31 -0
  3. package/assets/docs/workflows/projects/artifacts.md +27 -0
  4. package/assets/docs/workflows/skills/explainer-kit.md +125 -0
  5. package/assets/docs/workflows/skills/index.md +4 -0
  6. package/assets/migration/pjm-restructure.md +12 -0
  7. package/assets/public-package-versions.json +4 -4
  8. package/assets/skills/explainer-kit/SKILL.md +95 -0
  9. package/assets/skills/explainer-kit/examples/project-explainer/content.md +31 -0
  10. package/assets/skills/explainer-kit/examples/project-explainer/fact-base.md +22 -0
  11. package/assets/skills/explainer-kit/examples/project-recap/content.md +34 -0
  12. package/assets/skills/explainer-kit/examples/project-recap/fact-base.md +22 -0
  13. package/assets/skills/explainer-kit/examples/theme-bundle.json +87 -0
  14. package/assets/skills/explainer-kit/palettes/ember.json +37 -0
  15. package/assets/skills/explainer-kit/palettes/forest.json +37 -0
  16. package/assets/skills/explainer-kit/palettes/neutral.json +37 -0
  17. package/assets/skills/explainer-kit/palettes/ocean.json +37 -0
  18. package/assets/skills/explainer-kit/palettes/violet.json +37 -0
  19. package/assets/skills/explainer-kit/profiles/clean.json +42 -0
  20. package/assets/skills/explainer-kit/profiles/editorial.json +42 -0
  21. package/assets/skills/explainer-kit/profiles/technical.json +39 -0
  22. package/assets/skills/explainer-kit/recipes/engineer-tour.json +33 -0
  23. package/assets/skills/explainer-kit/recipes/project-explainer.json +33 -0
  24. package/assets/skills/explainer-kit/recipes/project-recap.json +34 -0
  25. package/assets/skills/explainer-kit/references/contracts.md +101 -0
  26. package/assets/skills/explainer-kit/references/destination-contract.md +94 -0
  27. package/assets/skills/explainer-kit/references/extension-contract.md +71 -0
  28. package/assets/skills/explainer-kit/references/fact-base-contract.md +119 -0
  29. package/assets/skills/explainer-kit/schemas/build-record.schema.json +92 -0
  30. package/assets/skills/explainer-kit/schemas/durability-evidence.schema.json +53 -0
  31. package/assets/skills/explainer-kit/schemas/fact-base.schema.json +129 -0
  32. package/assets/skills/explainer-kit/schemas/manifest.schema.json +193 -0
  33. package/assets/skills/explainer-kit/schemas/publish-receipt.schema.json +97 -0
  34. package/assets/skills/explainer-kit/schemas/publish-request.schema.json +32 -0
  35. package/assets/skills/explainer-kit/schemas/run-request.schema.json +132 -0
  36. package/assets/skills/explainer-kit/schemas/theme.schema.json +217 -0
  37. package/assets/skills/explainer-kit/scripts/lib/content-approval.mjs +180 -0
  38. package/assets/skills/explainer-kit/scripts/lib/contracts.mjs +680 -0
  39. package/assets/skills/explainer-kit/scripts/lib/durability.mjs +538 -0
  40. package/assets/skills/explainer-kit/scripts/lib/fact-base.mjs +494 -0
  41. package/assets/skills/explainer-kit/scripts/lib/fs-safe.mjs +218 -0
  42. package/assets/skills/explainer-kit/scripts/lib/qa.mjs +512 -0
  43. package/assets/skills/explainer-kit/scripts/lib/recipes.mjs +289 -0
  44. package/assets/skills/explainer-kit/scripts/lib/records.mjs +231 -0
  45. package/assets/skills/explainer-kit/scripts/lib/render.mjs +485 -0
  46. package/assets/skills/explainer-kit/scripts/lib/s3-static.mjs +587 -0
  47. package/assets/skills/explainer-kit/scripts/lib/safe-paths.mjs +96 -0
  48. package/assets/skills/explainer-kit/scripts/lib/theme.mjs +332 -0
  49. package/assets/skills/explainer-kit/scripts/publish.mjs +76 -0
  50. package/assets/skills/explainer-kit/scripts/record-durability.mjs +64 -0
  51. package/assets/skills/explainer-kit/scripts/render-qa.mjs +273 -0
  52. package/assets/skills/explainer-kit/scripts/run.mjs +778 -0
  53. package/assets/skills/explainer-kit/scripts/validate.mjs +59 -0
  54. package/assets/skills/explainer-kit/templates/deck-shell.html +233 -0
  55. package/assets/skills/explainer-kit/templates/diagram-shell.html +219 -0
  56. package/assets/skills/explainer-kit/templates/engineer-tour.html +273 -0
  57. package/assets/skills/explainer-kit/templates/house-style.html +218 -0
  58. package/assets/skills/oat-explainer-kit/SKILL.md +92 -0
  59. package/assets/skills/oat-explainer-kit/references/config-contract.md +67 -0
  60. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +143 -0
  61. package/assets/skills/oat-explainer-kit/references/migration.md +143 -0
  62. package/assets/skills/oat-explainer-kit/scripts/bind-project-sources.mjs +103 -0
  63. package/assets/skills/oat-explainer-kit/scripts/check-core.mjs +127 -0
  64. package/assets/skills/oat-explainer-kit/scripts/finalize-tracked-run.mjs +392 -0
  65. package/assets/skills/oat-explainer-kit/scripts/persist-intent.mjs +130 -0
  66. package/assets/skills/oat-explainer-kit/scripts/resolve-config.mjs +293 -0
  67. package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +209 -0
  68. package/assets/skills/oat-explainer-kit/scripts/resolve-paths.mjs +166 -0
  69. package/assets/skills/oat-explainer-kit/scripts/run.mjs +284 -0
  70. package/assets/skills/oat-project-autonomous/SKILL.md +22 -1
  71. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +5 -1
  72. package/assets/skills/oat-project-complete/SKILL.md +166 -28
  73. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +5 -1
  74. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  75. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +17 -0
  76. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +5 -1
  77. package/assets/skills/oat-project-plan/SKILL.md +30 -1
  78. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +5 -1
  79. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +5 -1
  80. package/assets/skills/oat-project-summary/SKILL.md +20 -1
  81. package/assets/skills/oat-wave-execute/SKILL.md +1 -1
  82. package/dist/commands/config/index.d.ts.map +1 -1
  83. package/dist/commands/config/index.js +232 -1
  84. package/dist/commands/init/tools/shared/skill-manifest.d.ts +2 -2
  85. package/dist/commands/init/tools/shared/skill-manifest.d.ts.map +1 -1
  86. package/dist/commands/init/tools/shared/skill-manifest.js +2 -0
  87. package/dist/commands/project/archive/archive-utils.d.ts +12 -0
  88. package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
  89. package/dist/commands/project/archive/archive-utils.js +320 -8
  90. package/dist/commands/project/archive/index.d.ts.map +1 -1
  91. package/dist/commands/project/archive/index.js +1 -0
  92. package/dist/commands/project/archive/push-runner.d.ts +1 -0
  93. package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
  94. package/dist/commands/project/archive/push-runner.js +9 -2
  95. package/dist/config/oat-config.d.ts +26 -0
  96. package/dist/config/oat-config.d.ts.map +1 -1
  97. package/dist/config/oat-config.js +88 -0
  98. package/dist/config/resolve.d.ts.map +1 -1
  99. package/dist/config/resolve.js +18 -0
  100. package/dist/validation/project-state.d.ts +3 -0
  101. package/dist/validation/project-state.d.ts.map +1 -1
  102. package/dist/validation/project-state.js +91 -0
  103. package/package.json +2 -2
  104. /package/assets/skills/{oat-wave-execute/assets/program-recap.recipe.json → explainer-kit/recipes/program-recap.json} +0 -0
@@ -0,0 +1,97 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "explainer-kit.publish-receipt/v1",
4
+ "title": "PublishReceiptV1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "provider",
10
+ "publishedAt",
11
+ "roots",
12
+ "sentinel",
13
+ "artifacts"
14
+ ],
15
+ "properties": {
16
+ "schemaVersion": { "const": "explainer-kit.publish-receipt/v1" },
17
+ "provider": { "const": "s3-static" },
18
+ "publishedAt": { "type": "string", "format": "date-time" },
19
+ "roots": {
20
+ "type": "object",
21
+ "additionalProperties": false,
22
+ "required": ["s3Uri", "publicBaseUrl"],
23
+ "properties": {
24
+ "s3Uri": {
25
+ "type": "string",
26
+ "pattern": "^s3://[^/\\s]+(?:/[^\\s]*)?$"
27
+ },
28
+ "publicBaseUrl": {
29
+ "type": "string",
30
+ "pattern": "^https://[^\\s]+$"
31
+ }
32
+ }
33
+ },
34
+ "sentinel": {
35
+ "type": "object",
36
+ "additionalProperties": false,
37
+ "required": [
38
+ "relativePath",
39
+ "uploadVerified",
40
+ "publicVerified",
41
+ "deleted"
42
+ ],
43
+ "properties": {
44
+ "relativePath": { "$ref": "#/$defs/safeRelativePath" },
45
+ "uploadVerified": { "type": "boolean" },
46
+ "publicVerified": { "type": "boolean" },
47
+ "deleted": { "type": "boolean" }
48
+ }
49
+ },
50
+ "artifacts": {
51
+ "type": "array",
52
+ "items": { "$ref": "#/$defs/artifact" },
53
+ "uniqueItems": true
54
+ }
55
+ },
56
+ "$defs": {
57
+ "hash": {
58
+ "type": "string",
59
+ "pattern": "^sha256:[a-f0-9]{64}$"
60
+ },
61
+ "safeRelativePath": {
62
+ "type": "string",
63
+ "minLength": 1,
64
+ "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"
65
+ },
66
+ "artifact": {
67
+ "type": "object",
68
+ "additionalProperties": false,
69
+ "required": [
70
+ "relativePath",
71
+ "hash",
72
+ "s3Uri",
73
+ "publicUrl",
74
+ "httpStatus",
75
+ "contentType"
76
+ ],
77
+ "properties": {
78
+ "relativePath": { "$ref": "#/$defs/safeRelativePath" },
79
+ "hash": { "$ref": "#/$defs/hash" },
80
+ "s3Uri": {
81
+ "type": "string",
82
+ "pattern": "^s3://[^/\\s]+/.+$"
83
+ },
84
+ "publicUrl": {
85
+ "type": "string",
86
+ "pattern": "^https://[^\\s]+$"
87
+ },
88
+ "httpStatus": {
89
+ "type": "integer",
90
+ "minimum": 200,
91
+ "maximum": 299
92
+ },
93
+ "contentType": { "type": "string", "minLength": 1 }
94
+ }
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "explainer-kit.publish-request/v1",
4
+ "title": "S3StaticPublishRequestV1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "provider",
10
+ "s3Uri",
11
+ "publicBaseUrl",
12
+ "awsRegion",
13
+ "siteRoot",
14
+ "manifestPath"
15
+ ],
16
+ "properties": {
17
+ "schemaVersion": { "const": "explainer-kit.publish-request/v1" },
18
+ "provider": { "const": "s3-static" },
19
+ "s3Uri": {
20
+ "type": "string",
21
+ "pattern": "^s3://[^/\\s]+(?:/[^\\s]*)?$"
22
+ },
23
+ "publicBaseUrl": {
24
+ "type": "string",
25
+ "pattern": "^https://[^\\s]+$"
26
+ },
27
+ "awsRegion": { "type": "string", "minLength": 1 },
28
+ "awsProfile": { "type": "string", "minLength": 1 },
29
+ "siteRoot": { "type": "string", "minLength": 1 },
30
+ "manifestPath": { "type": "string", "minLength": 1 }
31
+ }
32
+ }
@@ -0,0 +1,132 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "explainer-kit.run-request/v1",
4
+ "title": "ExplainerRunRequestV1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "recipe",
10
+ "slug",
11
+ "outputRoot",
12
+ "factBase",
13
+ "mode"
14
+ ],
15
+ "properties": {
16
+ "schemaVersion": {
17
+ "const": "explainer-kit.run-request/v1"
18
+ },
19
+ "recipe": {
20
+ "type": "object",
21
+ "additionalProperties": false,
22
+ "required": ["id", "version"],
23
+ "properties": {
24
+ "id": { "type": "string", "minLength": 1 },
25
+ "version": { "type": "string", "minLength": 1 }
26
+ }
27
+ },
28
+ "slug": {
29
+ "type": "string",
30
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
31
+ },
32
+ "outputRoot": { "$ref": "#/$defs/relativeOrAbsolutePath" },
33
+ "factBase": { "$ref": "#/$defs/factBaseBinding" },
34
+ "theme": { "$ref": "#/$defs/themeSelection" },
35
+ "publicBaseUrl": { "type": "string", "format": "uri" },
36
+ "durability": {
37
+ "type": "object",
38
+ "additionalProperties": false,
39
+ "required": ["strategy"],
40
+ "properties": {
41
+ "strategy": {
42
+ "enum": ["none", "commit", "publish"]
43
+ },
44
+ "publish": {
45
+ "$ref": "explainer-kit.publish-request/v1"
46
+ }
47
+ }
48
+ },
49
+ "privacy": {
50
+ "type": "object",
51
+ "additionalProperties": false,
52
+ "required": ["retainRawArtDirection"],
53
+ "properties": {
54
+ "retainRawArtDirection": { "type": "boolean" }
55
+ }
56
+ },
57
+ "mode": { "enum": ["interactive", "unattended"] }
58
+ },
59
+ "$defs": {
60
+ "relativeOrAbsolutePath": {
61
+ "type": "string",
62
+ "minLength": 1
63
+ },
64
+ "safeRelativePath": {
65
+ "type": "string",
66
+ "minLength": 1,
67
+ "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$"
68
+ },
69
+ "sourceBinding": {
70
+ "type": "object",
71
+ "additionalProperties": false,
72
+ "required": ["id", "kind", "locator", "role", "sourceSetId"],
73
+ "properties": {
74
+ "id": { "type": "string", "minLength": 1 },
75
+ "kind": {
76
+ "enum": ["file", "directory", "git", "github", "session", "other"]
77
+ },
78
+ "locator": { "type": "string", "minLength": 1 },
79
+ "role": { "type": "string", "minLength": 1 },
80
+ "sourceSetId": { "type": "string", "minLength": 1 },
81
+ "revision": { "type": "string", "minLength": 1 },
82
+ "authoritativeFor": {
83
+ "type": "array",
84
+ "items": { "type": "string", "minLength": 1 },
85
+ "uniqueItems": true
86
+ }
87
+ }
88
+ },
89
+ "factBaseBinding": {
90
+ "type": "object",
91
+ "additionalProperties": false,
92
+ "required": ["mode", "freshnessPolicy"],
93
+ "properties": {
94
+ "mode": { "enum": ["supplied", "federated"] },
95
+ "path": { "$ref": "#/$defs/relativeOrAbsolutePath" },
96
+ "sources": {
97
+ "type": "array",
98
+ "items": { "$ref": "#/$defs/sourceBinding" },
99
+ "minItems": 1
100
+ },
101
+ "freshnessPolicy": { "const": "live-wins" }
102
+ },
103
+ "oneOf": [
104
+ {
105
+ "required": ["path"],
106
+ "properties": { "mode": { "const": "supplied" } }
107
+ },
108
+ {
109
+ "required": ["sources"],
110
+ "properties": { "mode": { "const": "federated" } }
111
+ }
112
+ ]
113
+ },
114
+ "themeSelection": {
115
+ "type": "object",
116
+ "additionalProperties": false,
117
+ "properties": {
118
+ "palette": { "type": "string", "minLength": 1 },
119
+ "visualProfile": { "type": "string", "minLength": 1 },
120
+ "suppliedBundlePath": {
121
+ "$ref": "#/$defs/relativeOrAbsolutePath"
122
+ },
123
+ "artDirection": { "type": "string", "minLength": 1 },
124
+ "defaultMode": { "enum": ["light", "dark"] },
125
+ "renderStrategy": {
126
+ "enum": ["default-only", "user-switchable"],
127
+ "default": "default-only"
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
@@ -0,0 +1,217 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "explainer-kit.theme/v1",
4
+ "title": "ResolvedThemeV1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "name",
10
+ "defaultMode",
11
+ "modes",
12
+ "provenance",
13
+ "typography",
14
+ "spacing",
15
+ "geometry",
16
+ "elevation",
17
+ "density",
18
+ "motion",
19
+ "diagrams",
20
+ "bundleHash"
21
+ ],
22
+ "properties": {
23
+ "schemaVersion": { "const": "explainer-kit.theme/v1" },
24
+ "name": { "type": "string", "minLength": 1 },
25
+ "defaultMode": { "enum": ["light", "dark"] },
26
+ "modes": {
27
+ "type": "object",
28
+ "additionalProperties": false,
29
+ "required": ["light", "dark"],
30
+ "properties": {
31
+ "light": { "$ref": "#/$defs/colors" },
32
+ "dark": { "$ref": "#/$defs/colors" }
33
+ }
34
+ },
35
+ "provenance": { "$ref": "#/$defs/provenance" },
36
+ "typography": {
37
+ "type": "object",
38
+ "additionalProperties": false,
39
+ "required": ["sans", "serif", "mono", "scale", "lineHeight"],
40
+ "properties": {
41
+ "sans": { "$ref": "#/$defs/fontList" },
42
+ "serif": { "$ref": "#/$defs/fontList" },
43
+ "mono": { "$ref": "#/$defs/fontList" },
44
+ "scale": { "$ref": "#/$defs/stringMap" },
45
+ "lineHeight": { "$ref": "#/$defs/positiveNumberMap" }
46
+ }
47
+ },
48
+ "spacing": {
49
+ "type": "object",
50
+ "additionalProperties": false,
51
+ "required": ["unit", "scale"],
52
+ "properties": {
53
+ "unit": { "type": "number", "exclusiveMinimum": 0, "maximum": 64 },
54
+ "scale": { "$ref": "#/$defs/positiveNumberMap" }
55
+ }
56
+ },
57
+ "geometry": {
58
+ "type": "object",
59
+ "additionalProperties": false,
60
+ "required": ["radius", "borderWidth"],
61
+ "properties": {
62
+ "radius": { "$ref": "#/$defs/nonNegativeNumberMap" },
63
+ "borderWidth": {
64
+ "type": "number",
65
+ "minimum": 0,
66
+ "maximum": 16
67
+ }
68
+ }
69
+ },
70
+ "elevation": {
71
+ "type": "object",
72
+ "additionalProperties": false,
73
+ "required": ["shadows"],
74
+ "properties": {
75
+ "shadows": { "$ref": "#/$defs/stringMap" }
76
+ }
77
+ },
78
+ "density": { "enum": ["compact", "comfortable", "spacious"] },
79
+ "motion": {
80
+ "type": "object",
81
+ "additionalProperties": false,
82
+ "required": ["enabled", "durationMs", "easing", "reducedMotion"],
83
+ "properties": {
84
+ "enabled": { "type": "boolean" },
85
+ "durationMs": { "$ref": "#/$defs/nonNegativeNumberMap" },
86
+ "easing": { "$ref": "#/$defs/stringMap" },
87
+ "reducedMotion": { "const": "disable-nonessential" }
88
+ }
89
+ },
90
+ "diagrams": {
91
+ "type": "object",
92
+ "additionalProperties": false,
93
+ "required": ["lineWidth", "nodeGap", "arrowStyle", "labelTreatment"],
94
+ "properties": {
95
+ "lineWidth": {
96
+ "type": "number",
97
+ "exclusiveMinimum": 0,
98
+ "maximum": 16
99
+ },
100
+ "nodeGap": {
101
+ "type": "number",
102
+ "exclusiveMinimum": 0,
103
+ "maximum": 512
104
+ },
105
+ "arrowStyle": { "enum": ["straight", "curved"] },
106
+ "labelTreatment": { "enum": ["inline", "boxed"] }
107
+ }
108
+ },
109
+ "bundleHash": {
110
+ "$ref": "#/$defs/hash",
111
+ "pattern": "^sha256:[a-f0-9]{64}$"
112
+ }
113
+ },
114
+ "$defs": {
115
+ "hash": {
116
+ "type": "string",
117
+ "pattern": "^sha256:[a-f0-9]{64}$"
118
+ },
119
+ "color": {
120
+ "type": "string",
121
+ "pattern": "^#[0-9a-fA-F]{6}$"
122
+ },
123
+ "colors": {
124
+ "type": "object",
125
+ "additionalProperties": false,
126
+ "required": ["surface", "ink", "accent", "status", "diagramSeries"],
127
+ "properties": {
128
+ "surface": {
129
+ "type": "object",
130
+ "additionalProperties": false,
131
+ "required": ["canvas", "panel", "elevated"],
132
+ "properties": {
133
+ "canvas": { "$ref": "#/$defs/color" },
134
+ "panel": { "$ref": "#/$defs/color" },
135
+ "elevated": { "$ref": "#/$defs/color" }
136
+ }
137
+ },
138
+ "ink": {
139
+ "type": "object",
140
+ "additionalProperties": false,
141
+ "required": ["primary", "muted", "inverse"],
142
+ "properties": {
143
+ "primary": { "$ref": "#/$defs/color" },
144
+ "muted": { "$ref": "#/$defs/color" },
145
+ "inverse": { "$ref": "#/$defs/color" }
146
+ }
147
+ },
148
+ "accent": {
149
+ "type": "object",
150
+ "additionalProperties": false,
151
+ "required": ["primary", "secondary"],
152
+ "properties": {
153
+ "primary": { "$ref": "#/$defs/color" },
154
+ "secondary": { "$ref": "#/$defs/color" }
155
+ }
156
+ },
157
+ "status": {
158
+ "type": "object",
159
+ "additionalProperties": false,
160
+ "required": ["success", "warning", "danger", "info"],
161
+ "properties": {
162
+ "success": { "$ref": "#/$defs/color" },
163
+ "warning": { "$ref": "#/$defs/color" },
164
+ "danger": { "$ref": "#/$defs/color" },
165
+ "info": { "$ref": "#/$defs/color" }
166
+ }
167
+ },
168
+ "diagramSeries": {
169
+ "type": "array",
170
+ "items": { "$ref": "#/$defs/color" },
171
+ "minItems": 1,
172
+ "uniqueItems": true
173
+ }
174
+ }
175
+ },
176
+ "provenance": {
177
+ "type": "object",
178
+ "additionalProperties": false,
179
+ "required": ["derived"],
180
+ "properties": {
181
+ "palette": { "type": "string", "minLength": 1 },
182
+ "visualProfile": { "type": "string", "minLength": 1 },
183
+ "derived": { "type": "boolean" },
184
+ "instructionHash": { "$ref": "#/$defs/hash" },
185
+ "suppliedBundleHash": { "$ref": "#/$defs/hash" }
186
+ }
187
+ },
188
+ "fontList": {
189
+ "type": "array",
190
+ "items": { "type": "string", "minLength": 1 },
191
+ "minItems": 1
192
+ },
193
+ "stringMap": {
194
+ "type": "object",
195
+ "propertyNames": { "minLength": 1 },
196
+ "additionalProperties": { "type": "string", "minLength": 1 }
197
+ },
198
+ "positiveNumberMap": {
199
+ "type": "object",
200
+ "propertyNames": { "minLength": 1 },
201
+ "additionalProperties": {
202
+ "type": "number",
203
+ "exclusiveMinimum": 0,
204
+ "maximum": 1024
205
+ }
206
+ },
207
+ "nonNegativeNumberMap": {
208
+ "type": "object",
209
+ "propertyNames": { "minLength": 1 },
210
+ "additionalProperties": {
211
+ "type": "number",
212
+ "minimum": 0,
213
+ "maximum": 10000
214
+ }
215
+ }
216
+ }
217
+ }
@@ -0,0 +1,180 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+
4
+ import { writeJsonAtomic } from './fs-safe.mjs';
5
+
6
+ const APPROVAL_PATH = 'source/content-approval.json';
7
+ const MODES = new Set(['interactive', 'unattended']);
8
+ const DECISIONS = new Map([
9
+ ['approve', 'approved'],
10
+ ['approved', 'approved'],
11
+ ['reject', 'rejected'],
12
+ ['rejected', 'rejected'],
13
+ ]);
14
+
15
+ export async function resolveContentApproval(run, mode, reviewedSource) {
16
+ assertRun(run);
17
+ if (!MODES.has(mode)) {
18
+ throw new Error('Content approval mode must be interactive or unattended.');
19
+ }
20
+
21
+ const previous = await readPrevious(run);
22
+ let record;
23
+ if (mode === 'unattended') {
24
+ const provenance =
25
+ reviewedSource ?? approvedSourceProvenance(run.request.factBase);
26
+ assertProvenance(provenance);
27
+ record = {
28
+ schemaVersion: 'explainer-kit.content-approval/v1',
29
+ runId: run.runId,
30
+ mode,
31
+ status: 'approved',
32
+ reviewedSource: structuredClone(provenance),
33
+ attempts: previous?.attempts ?? [],
34
+ };
35
+ } else if (reviewedSource === undefined) {
36
+ record = previous ?? {
37
+ schemaVersion: 'explainer-kit.content-approval/v1',
38
+ runId: run.runId,
39
+ mode,
40
+ status: 'pending',
41
+ attempts: [],
42
+ };
43
+ } else {
44
+ const decision = normalizeDecision(reviewedSource.decision);
45
+ const attempt = interactiveAttempt(reviewedSource, decision);
46
+ record = {
47
+ schemaVersion: 'explainer-kit.content-approval/v1',
48
+ runId: run.runId,
49
+ mode,
50
+ status: decision,
51
+ attempts: [...(previous?.attempts ?? []), attempt],
52
+ ...(decision === 'approved' && {
53
+ reviewedSource: structuredClone(
54
+ reviewedSource.source ?? {
55
+ kind: 'human-review',
56
+ locator: 'source/content',
57
+ },
58
+ ),
59
+ }),
60
+ };
61
+ if (record.reviewedSource) {
62
+ assertProvenance(record.reviewedSource);
63
+ }
64
+ }
65
+
66
+ await writeJsonAtomic(run.runRoot, APPROVAL_PATH, record);
67
+ return {
68
+ status: record.status,
69
+ canResume: record.status === 'approved',
70
+ path: APPROVAL_PATH,
71
+ record,
72
+ };
73
+ }
74
+
75
+ async function readPrevious(run) {
76
+ try {
77
+ const previous = JSON.parse(
78
+ await readFile(join(run.runRoot, APPROVAL_PATH), 'utf8'),
79
+ );
80
+ if (
81
+ previous.schemaVersion !== 'explainer-kit.content-approval/v1' ||
82
+ previous.runId !== run.runId
83
+ ) {
84
+ throw new Error('Content approval record does not belong to this run.');
85
+ }
86
+ return previous;
87
+ } catch (error) {
88
+ if (error?.code === 'ENOENT') return null;
89
+ throw error;
90
+ }
91
+ }
92
+
93
+ function interactiveAttempt(source, status) {
94
+ if (!isObject(source)) {
95
+ throw new TypeError('Interactive approval requires a review decision.');
96
+ }
97
+ if (
98
+ typeof source.reviewedAt !== 'string' ||
99
+ Number.isNaN(Date.parse(source.reviewedAt))
100
+ ) {
101
+ throw new Error('Interactive approval requires a valid reviewedAt value.');
102
+ }
103
+ if (
104
+ source.reviewer !== undefined &&
105
+ (typeof source.reviewer !== 'string' || source.reviewer.length === 0)
106
+ ) {
107
+ throw new Error(
108
+ 'Interactive approval reviewer must be a non-empty string.',
109
+ );
110
+ }
111
+ const corrections = source.corrections ?? [];
112
+ if (
113
+ !Array.isArray(corrections) ||
114
+ corrections.some(
115
+ (correction) =>
116
+ typeof correction !== 'string' || correction.trim().length === 0,
117
+ )
118
+ ) {
119
+ throw new Error('Approval corrections must be non-empty strings.');
120
+ }
121
+ if (status === 'rejected' && corrections.length === 0) {
122
+ throw new Error('Rejected content approval requires corrections.');
123
+ }
124
+ return {
125
+ decision: status === 'approved' ? 'approve' : 'reject',
126
+ reviewedAt: source.reviewedAt,
127
+ ...(source.reviewer && { reviewer: source.reviewer }),
128
+ corrections: [...corrections],
129
+ };
130
+ }
131
+
132
+ function normalizeDecision(decision) {
133
+ const normalized = DECISIONS.get(decision);
134
+ if (!normalized) {
135
+ throw new Error('Review decision must be approve or reject.');
136
+ }
137
+ return normalized;
138
+ }
139
+
140
+ function approvedSourceProvenance(factBase) {
141
+ if (factBase?.mode === 'supplied') {
142
+ return {
143
+ kind: 'approved-fact-base',
144
+ locator: factBase.path,
145
+ };
146
+ }
147
+ return {
148
+ kind: 'approved-source-set',
149
+ locator: `federated:${factBase?.sources?.length ?? 0}`,
150
+ };
151
+ }
152
+
153
+ function assertProvenance(value) {
154
+ if (
155
+ !isObject(value) ||
156
+ typeof value.kind !== 'string' ||
157
+ value.kind.length === 0 ||
158
+ typeof value.locator !== 'string' ||
159
+ value.locator.length === 0
160
+ ) {
161
+ throw new Error(
162
+ 'Reviewed source provenance requires non-empty kind and locator values.',
163
+ );
164
+ }
165
+ }
166
+
167
+ function assertRun(run) {
168
+ if (
169
+ !isObject(run) ||
170
+ typeof run.runId !== 'string' ||
171
+ typeof run.runRoot !== 'string' ||
172
+ !isObject(run.request)
173
+ ) {
174
+ throw new TypeError('Run must be returned by initializeRun().');
175
+ }
176
+ }
177
+
178
+ function isObject(value) {
179
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
180
+ }