@duypham93/openkit 0.2.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.
- package/.opencode/README.md +47 -0
- package/.opencode/install-manifest.json +41 -0
- package/.opencode/lib/artifact-scaffolder.js +111 -0
- package/.opencode/lib/contract-consistency.js +218 -0
- package/.opencode/lib/parallel-execution-rules.js +261 -0
- package/.opencode/lib/runtime-paths.js +95 -0
- package/.opencode/lib/runtime-summary.js +82 -0
- package/.opencode/lib/state-guard.js +99 -0
- package/.opencode/lib/task-board-rules.js +375 -0
- package/.opencode/lib/work-item-store.js +280 -0
- package/.opencode/lib/workflow-state-controller.js +1739 -0
- package/.opencode/lib/workflow-state-rules.js +331 -0
- package/.opencode/opencode.json +93 -0
- package/.opencode/package.json +3 -0
- package/.opencode/tests/artifact-scaffolder.test.js +733 -0
- package/.opencode/tests/multi-work-item-runtime.test.js +369 -0
- package/.opencode/tests/parallel-execution-runtime.test.js +259 -0
- package/.opencode/tests/session-start-hook.test.js +357 -0
- package/.opencode/tests/state-guard.test.js +124 -0
- package/.opencode/tests/task-board-rules.test.js +204 -0
- package/.opencode/tests/work-item-store.test.js +380 -0
- package/.opencode/tests/workflow-behavior.test.js +149 -0
- package/.opencode/tests/workflow-contract-consistency.test.js +387 -0
- package/.opencode/tests/workflow-state-cli.test.js +1275 -0
- package/.opencode/tests/workflow-state-controller.test.js +1038 -0
- package/.opencode/work-items/feature-001/state.json +70 -0
- package/.opencode/work-items/index.json +13 -0
- package/.opencode/workflow-state.js +489 -0
- package/.opencode/workflow-state.json +70 -0
- package/AGENTS.md +265 -0
- package/README.md +401 -0
- package/agents/architect-agent.md +63 -0
- package/agents/ba-agent.md +56 -0
- package/agents/code-reviewer.md +77 -0
- package/agents/fullstack-agent.md +115 -0
- package/agents/master-orchestrator.md +60 -0
- package/agents/pm-agent.md +56 -0
- package/agents/qa-agent.md +124 -0
- package/agents/tech-lead-agent.md +60 -0
- package/assets/install-bundle/README.md +7 -0
- package/assets/install-bundle/opencode/README.md +11 -0
- package/assets/install-bundle/opencode/agents/ArchitectAgent.md +63 -0
- package/assets/install-bundle/opencode/agents/BAAgent.md +56 -0
- package/assets/install-bundle/opencode/agents/CodeReviewer.md +77 -0
- package/assets/install-bundle/opencode/agents/FullstackAgent.md +115 -0
- package/assets/install-bundle/opencode/agents/MasterOrchestrator.md +60 -0
- package/assets/install-bundle/opencode/agents/PMAgent.md +56 -0
- package/assets/install-bundle/opencode/agents/QAAgent.md +124 -0
- package/assets/install-bundle/opencode/agents/TechLeadAgent.md +60 -0
- package/assets/install-bundle/opencode/commands/brainstorm.md +44 -0
- package/assets/install-bundle/opencode/commands/delivery.md +45 -0
- package/assets/install-bundle/opencode/commands/execute-plan.md +44 -0
- package/assets/install-bundle/opencode/commands/migrate.md +61 -0
- package/assets/install-bundle/opencode/commands/quick-task.md +45 -0
- package/assets/install-bundle/opencode/commands/task.md +46 -0
- package/assets/install-bundle/opencode/commands/write-plan.md +50 -0
- package/assets/install-bundle/opencode/context/core/lane-selection.md +54 -0
- package/assets/install-bundle/opencode/skills/brainstorming/SKILL.md +51 -0
- package/assets/install-bundle/opencode/skills/code-review/SKILL.md +48 -0
- package/assets/install-bundle/opencode/skills/subagent-driven-development/SKILL.md +79 -0
- package/assets/install-bundle/opencode/skills/systematic-debugging/SKILL.md +61 -0
- package/assets/install-bundle/opencode/skills/test-driven-development/SKILL.md +48 -0
- package/assets/install-bundle/opencode/skills/using-skills/SKILL.md +39 -0
- package/assets/install-bundle/opencode/skills/verification-before-completion/SKILL.md +137 -0
- package/assets/install-bundle/opencode/skills/writing-plans/SKILL.md +68 -0
- package/assets/install-bundle/opencode/skills/writing-specs/SKILL.md +47 -0
- package/assets/opencode.json.template +11 -0
- package/assets/openkit-install.json.template +19 -0
- package/bin/openkit.js +9 -0
- package/commands/brainstorm.md +44 -0
- package/commands/delivery.md +45 -0
- package/commands/execute-plan.md +44 -0
- package/commands/migrate.md +61 -0
- package/commands/quick-task.md +45 -0
- package/commands/task.md +46 -0
- package/commands/write-plan.md +50 -0
- package/context/core/approval-gates.md +146 -0
- package/context/core/code-quality.md +42 -0
- package/context/core/issue-routing.md +85 -0
- package/context/core/lane-selection.md +54 -0
- package/context/core/project-config.md +143 -0
- package/context/core/session-resume.md +85 -0
- package/context/core/workflow-state-schema.md +224 -0
- package/context/core/workflow.md +442 -0
- package/context/navigation.md +94 -0
- package/docs/adr/README.md +6 -0
- package/docs/architecture/2026-03-20-task-intake-dashboard.md +54 -0
- package/docs/architecture/README.md +7 -0
- package/docs/briefs/2026-03-20-task-intake-dashboard.md +48 -0
- package/docs/briefs/README.md +7 -0
- package/docs/governance/README.md +25 -0
- package/docs/governance/adr-policy.md +27 -0
- package/docs/governance/definition-of-done.md +17 -0
- package/docs/governance/naming-conventions.md +21 -0
- package/docs/governance/severity-levels.md +12 -0
- package/docs/maintainer/README.md +51 -0
- package/docs/operations/README.md +79 -0
- package/docs/operations/internal-records/2026-03-24-release-checklist.md +79 -0
- package/docs/operations/internal-records/2026-03-24-simplified-install-ux.md +36 -0
- package/docs/operations/internal-records/README.md +18 -0
- package/docs/operations/runbooks/README.md +23 -0
- package/docs/operations/runbooks/openkit-daily-usage.md +288 -0
- package/docs/operations/runbooks/workflow-state-smoke-tests.md +302 -0
- package/docs/operator/README.md +50 -0
- package/docs/plans/2026-03-20-task-intake-dashboard.md +49 -0
- package/docs/plans/2026-03-21-openkit-full-delivery-multi-task-runtime.md +521 -0
- package/docs/plans/2026-03-23-openkit-global-install-runtime.md +157 -0
- package/docs/plans/README.md +7 -0
- package/docs/qa/2026-03-20-task-intake-dashboard.md +41 -0
- package/docs/qa/README.md +7 -0
- package/docs/specs/2026-03-20-task-intake-dashboard.md +50 -0
- package/docs/specs/2026-03-21-openkit-full-delivery-multi-task-runtime.md +462 -0
- package/docs/specs/README.md +7 -0
- package/docs/templates/README.md +36 -0
- package/docs/templates/adr-template.md +18 -0
- package/docs/templates/architecture-template.md +31 -0
- package/docs/templates/implementation-plan-template.md +32 -0
- package/docs/templates/migration-baseline-checklist.md +48 -0
- package/docs/templates/migration-plan-template.md +52 -0
- package/docs/templates/migration-report-template.md +74 -0
- package/docs/templates/migration-verify-checklist.md +39 -0
- package/docs/templates/product-brief-template.md +32 -0
- package/docs/templates/qa-report-template.md +37 -0
- package/docs/templates/quick-task-template.md +36 -0
- package/docs/templates/spec-template.md +31 -0
- package/hooks/hooks.json +16 -0
- package/hooks/session-start +162 -0
- package/package.json +24 -0
- package/registry.json +328 -0
- package/skills/brainstorming/SKILL.md +51 -0
- package/skills/code-review/SKILL.md +48 -0
- package/skills/subagent-driven-development/SKILL.md +79 -0
- package/skills/systematic-debugging/SKILL.md +61 -0
- package/skills/test-driven-development/SKILL.md +48 -0
- package/skills/using-skills/SKILL.md +39 -0
- package/skills/verification-before-completion/SKILL.md +137 -0
- package/skills/writing-plans/SKILL.md +68 -0
- package/skills/writing-specs/SKILL.md +47 -0
- package/src/audit/vietnamese-detection.js +259 -0
- package/src/cli/commands/detect-vietnamese.js +24 -0
- package/src/cli/commands/doctor.js +33 -0
- package/src/cli/commands/help.js +33 -0
- package/src/cli/commands/init.js +25 -0
- package/src/cli/commands/install-global.js +26 -0
- package/src/cli/commands/install.js +25 -0
- package/src/cli/commands/run.js +63 -0
- package/src/cli/commands/uninstall.js +32 -0
- package/src/cli/commands/upgrade.js +25 -0
- package/src/cli/conflict-output.js +19 -0
- package/src/cli/index.js +56 -0
- package/src/global/doctor.js +101 -0
- package/src/global/ensure-install.js +32 -0
- package/src/global/install-state.js +73 -0
- package/src/global/launcher.js +51 -0
- package/src/global/materialize.js +123 -0
- package/src/global/paths.js +85 -0
- package/src/global/uninstall.js +25 -0
- package/src/global/workspace-state.js +63 -0
- package/src/install/asset-manifest.js +284 -0
- package/src/install/conflicts.js +43 -0
- package/src/install/discovery.js +138 -0
- package/src/install/install-state.js +136 -0
- package/src/install/materialize.js +158 -0
- package/src/install/merge-policy.js +145 -0
- package/src/runtime/doctor.js +281 -0
- package/src/runtime/launcher.js +49 -0
- package/src/runtime/opencode-layering.js +135 -0
- package/src/runtime/openkit-managed-summary.js +27 -0
- package/tests/cli/openkit-cli.test.js +417 -0
- package/tests/global/doctor.test.js +130 -0
- package/tests/global/ensure-install.test.js +105 -0
- package/tests/install/discovery.test.js +124 -0
- package/tests/install/install-state.test.js +346 -0
- package/tests/install/materialize.test.js +244 -0
- package/tests/install/merge-policy.test.js +177 -0
- package/tests/runtime/doctor.test.js +430 -0
- package/tests/runtime/launcher.test.js +230 -0
- package/tests/runtime/module-boundary.test.js +16 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import test from "node:test"
|
|
2
|
+
import assert from "node:assert/strict"
|
|
3
|
+
import fs from "node:fs"
|
|
4
|
+
import os from "node:os"
|
|
5
|
+
import path from "node:path"
|
|
6
|
+
|
|
7
|
+
import { validateInstallState } from "../../src/install/install-state.js"
|
|
8
|
+
import { materializeInstall } from "../../src/install/materialize.js"
|
|
9
|
+
|
|
10
|
+
function makeTempDir() {
|
|
11
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), "openkit-materialize-"))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function writeJson(filePath, value) {
|
|
15
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
16
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8")
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function readJson(filePath) {
|
|
20
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test("materializeInstall creates managed install files without mutating the checked-in runtime surface", () => {
|
|
24
|
+
const projectRoot = makeTempDir()
|
|
25
|
+
const runtimeManifestPath = path.join(projectRoot, ".opencode", "opencode.json")
|
|
26
|
+
|
|
27
|
+
writeJson(runtimeManifestPath, {
|
|
28
|
+
$schema: "https://example.com/opencode.schema.json",
|
|
29
|
+
runtime: true,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const beforeRuntimeManifest = fs.readFileSync(runtimeManifestPath, "utf8")
|
|
33
|
+
const result = materializeInstall(projectRoot, {
|
|
34
|
+
kitVersion: "0.2.0",
|
|
35
|
+
now: new Date("2026-03-22T12:00:00.000Z"),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const rootManifestPath = path.join(projectRoot, "opencode.json")
|
|
39
|
+
const installStatePath = path.join(projectRoot, ".openkit", "openkit-install.json")
|
|
40
|
+
const rootManifest = readJson(rootManifestPath)
|
|
41
|
+
const installState = readJson(installStatePath)
|
|
42
|
+
|
|
43
|
+
assert.equal(result.conflicts.length, 0)
|
|
44
|
+
assert.equal(result.warnings.length, 0)
|
|
45
|
+
assert.deepEqual(rootManifest, {
|
|
46
|
+
installState: {
|
|
47
|
+
path: ".openkit/openkit-install.json",
|
|
48
|
+
schema: "openkit/install-state@1",
|
|
49
|
+
},
|
|
50
|
+
productSurface: {
|
|
51
|
+
current: "global-openkit-install",
|
|
52
|
+
installReadiness: "managed",
|
|
53
|
+
installationMode: "openkit-managed",
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
assert.equal(fs.readFileSync(runtimeManifestPath, "utf8"), beforeRuntimeManifest)
|
|
57
|
+
assert.deepEqual(validateInstallState(installState), [])
|
|
58
|
+
assert.equal(installState.kit.version, "0.2.0")
|
|
59
|
+
assert.equal(installState.installation.installedAt, "2026-03-22T12:00:00.000Z")
|
|
60
|
+
assert.deepEqual(installState.assets.managed, [
|
|
61
|
+
{
|
|
62
|
+
assetId: "runtime.opencode-manifest",
|
|
63
|
+
path: "opencode.json",
|
|
64
|
+
status: "materialized",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
assetId: "runtime.install-state",
|
|
68
|
+
path: ".openkit/openkit-install.json",
|
|
69
|
+
status: "managed",
|
|
70
|
+
},
|
|
71
|
+
])
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test("materializeInstall additively inserts allowed managed-install keys into an existing root manifest", () => {
|
|
75
|
+
const projectRoot = makeTempDir()
|
|
76
|
+
const rootManifestPath = path.join(projectRoot, "opencode.json")
|
|
77
|
+
|
|
78
|
+
writeJson(path.join(projectRoot, ".opencode", "opencode.json"), {
|
|
79
|
+
runtime: true,
|
|
80
|
+
})
|
|
81
|
+
writeJson(rootManifestPath, {
|
|
82
|
+
plugin: ["existing-plugin"],
|
|
83
|
+
instructions: ["LOCAL.md"],
|
|
84
|
+
theme: "light",
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const result = materializeInstall(projectRoot, {
|
|
88
|
+
now: new Date("2026-03-22T12:00:00.000Z"),
|
|
89
|
+
})
|
|
90
|
+
const afterRootManifest = readJson(rootManifestPath)
|
|
91
|
+
const installState = readJson(path.join(projectRoot, ".openkit", "openkit-install.json"))
|
|
92
|
+
|
|
93
|
+
assert.deepEqual(afterRootManifest, {
|
|
94
|
+
plugin: ["existing-plugin"],
|
|
95
|
+
instructions: ["LOCAL.md"],
|
|
96
|
+
theme: "light",
|
|
97
|
+
installState: {
|
|
98
|
+
path: ".openkit/openkit-install.json",
|
|
99
|
+
schema: "openkit/install-state@1",
|
|
100
|
+
},
|
|
101
|
+
productSurface: {
|
|
102
|
+
current: "global-openkit-install",
|
|
103
|
+
installReadiness: "managed",
|
|
104
|
+
installationMode: "openkit-managed",
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
assert.deepEqual(result.conflicts, [])
|
|
108
|
+
assert.deepEqual(result.managedAssets, [
|
|
109
|
+
{
|
|
110
|
+
assetId: "runtime.opencode-manifest",
|
|
111
|
+
path: "opencode.json",
|
|
112
|
+
status: "materialized",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
assetId: "runtime.install-state",
|
|
116
|
+
path: ".openkit/openkit-install.json",
|
|
117
|
+
status: "managed",
|
|
118
|
+
},
|
|
119
|
+
])
|
|
120
|
+
assert.deepEqual(validateInstallState(installState), [])
|
|
121
|
+
assert.deepEqual(installState.assets.adopted, [])
|
|
122
|
+
assert.deepEqual(installState.conflicts, result.conflicts)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test("materializeInstall fails closed for unsupported root manifest rewrites", () => {
|
|
126
|
+
const projectRoot = makeTempDir()
|
|
127
|
+
const rootManifestPath = path.join(projectRoot, "opencode.json")
|
|
128
|
+
|
|
129
|
+
writeJson(path.join(projectRoot, ".opencode", "opencode.json"), {
|
|
130
|
+
runtime: true,
|
|
131
|
+
})
|
|
132
|
+
writeJson(rootManifestPath, {
|
|
133
|
+
theme: "light",
|
|
134
|
+
productSurface: {
|
|
135
|
+
current: "custom-surface",
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
const beforeRootManifest = fs.readFileSync(rootManifestPath, "utf8")
|
|
140
|
+
const result = materializeInstall(projectRoot, {
|
|
141
|
+
now: new Date("2026-03-22T12:00:00.000Z"),
|
|
142
|
+
})
|
|
143
|
+
const afterRootManifest = readJson(rootManifestPath)
|
|
144
|
+
const installStatePath = path.join(projectRoot, ".openkit", "openkit-install.json")
|
|
145
|
+
|
|
146
|
+
assert.deepEqual(afterRootManifest, JSON.parse(beforeRootManifest))
|
|
147
|
+
assert.deepEqual(result.conflicts, [
|
|
148
|
+
{
|
|
149
|
+
assetId: "runtime.opencode-manifest",
|
|
150
|
+
path: "opencode.json",
|
|
151
|
+
field: "productSurface",
|
|
152
|
+
reason: "unsupported-top-level-key",
|
|
153
|
+
currentValue: {
|
|
154
|
+
current: "custom-surface",
|
|
155
|
+
},
|
|
156
|
+
desiredValue: {
|
|
157
|
+
current: "global-openkit-install",
|
|
158
|
+
installReadiness: "managed",
|
|
159
|
+
installationMode: "openkit-managed",
|
|
160
|
+
},
|
|
161
|
+
resolution: "manual-review-required",
|
|
162
|
+
},
|
|
163
|
+
])
|
|
164
|
+
assert.equal(fs.existsSync(installStatePath), false)
|
|
165
|
+
assert.deepEqual(result.managedAssets, [])
|
|
166
|
+
assert.deepEqual(result.adoptedAssets, [
|
|
167
|
+
{
|
|
168
|
+
assetId: "runtime.opencode-manifest",
|
|
169
|
+
path: "opencode.json",
|
|
170
|
+
adoptedFrom: "user-existing",
|
|
171
|
+
status: "adopted",
|
|
172
|
+
},
|
|
173
|
+
])
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test("materializeInstall reports pre-existing user-owned install-state without mutating managed install assets", () => {
|
|
177
|
+
const projectRoot = makeTempDir()
|
|
178
|
+
const rootManifestPath = path.join(projectRoot, "opencode.json")
|
|
179
|
+
const installStatePath = path.join(projectRoot, ".openkit", "openkit-install.json")
|
|
180
|
+
|
|
181
|
+
writeJson(rootManifestPath, {
|
|
182
|
+
plugin: ["existing-plugin"],
|
|
183
|
+
})
|
|
184
|
+
writeJson(installStatePath, {
|
|
185
|
+
schema: "custom/install-state",
|
|
186
|
+
owner: "user",
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const beforeRootManifest = fs.readFileSync(rootManifestPath, "utf8")
|
|
190
|
+
const beforeInstallState = fs.readFileSync(installStatePath, "utf8")
|
|
191
|
+
const result = materializeInstall(projectRoot, {
|
|
192
|
+
now: new Date("2026-03-22T12:00:00.000Z"),
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
assert.equal(result.conflicts.length, 1)
|
|
196
|
+
assert.deepEqual(result.conflicts, [
|
|
197
|
+
{
|
|
198
|
+
assetId: "runtime.install-state",
|
|
199
|
+
path: ".openkit/openkit-install.json",
|
|
200
|
+
reason: "existing-managed-asset",
|
|
201
|
+
resolution: "manual-review-required",
|
|
202
|
+
},
|
|
203
|
+
])
|
|
204
|
+
assert.equal(fs.readFileSync(rootManifestPath, "utf8"), beforeRootManifest)
|
|
205
|
+
assert.equal(fs.readFileSync(installStatePath, "utf8"), beforeInstallState)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
test("materializeInstall does not leave a dangling root manifest when install-state write fails", () => {
|
|
209
|
+
const projectRoot = makeTempDir()
|
|
210
|
+
const runtimeManifestPath = path.join(projectRoot, ".opencode", "opencode.json")
|
|
211
|
+
const rootManifestPath = path.join(projectRoot, "opencode.json")
|
|
212
|
+
const openkitDirPath = path.join(projectRoot, ".openkit")
|
|
213
|
+
const originalWriteFileSync = fs.writeFileSync
|
|
214
|
+
|
|
215
|
+
writeJson(runtimeManifestPath, {
|
|
216
|
+
runtime: true,
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
fs.mkdirSync(openkitDirPath, { recursive: true })
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
fs.writeFileSync = (filePath, data, options) => {
|
|
223
|
+
if (filePath === path.join(projectRoot, ".openkit", "openkit-install.json")) {
|
|
224
|
+
throw new Error("simulated install-state write failure")
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return originalWriteFileSync(filePath, data, options)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
assert.throws(
|
|
231
|
+
() => {
|
|
232
|
+
materializeInstall(projectRoot, {
|
|
233
|
+
now: new Date("2026-03-22T12:00:00.000Z"),
|
|
234
|
+
})
|
|
235
|
+
},
|
|
236
|
+
/simulated install-state write failure/,
|
|
237
|
+
)
|
|
238
|
+
} finally {
|
|
239
|
+
fs.writeFileSync = originalWriteFileSync
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
assert.equal(fs.existsSync(rootManifestPath), false)
|
|
243
|
+
assert.equal(fs.existsSync(path.join(projectRoot, ".openkit", "openkit-install.json")), false)
|
|
244
|
+
})
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import test from "node:test"
|
|
2
|
+
import assert from "node:assert/strict"
|
|
3
|
+
|
|
4
|
+
import { applyOpenKitMergePolicy } from "../../src/install/merge-policy.js"
|
|
5
|
+
|
|
6
|
+
test("applyOpenKitMergePolicy additively merges supported plugin and instructions fields", () => {
|
|
7
|
+
const currentConfig = {
|
|
8
|
+
$schema: "https://example.com/schema.json",
|
|
9
|
+
plugin: ["existing-plugin"],
|
|
10
|
+
instructions: ["local-note.md"],
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const desiredConfig = {
|
|
14
|
+
plugin: ["existing-plugin", "openkit-plugin"],
|
|
15
|
+
instructions: ["local-note.md", "OPENKIT.md"],
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const result = applyOpenKitMergePolicy({ currentConfig, desiredConfig })
|
|
19
|
+
|
|
20
|
+
assert.deepEqual(result.config, {
|
|
21
|
+
$schema: "https://example.com/schema.json",
|
|
22
|
+
plugin: ["existing-plugin", "openkit-plugin"],
|
|
23
|
+
instructions: ["local-note.md", "OPENKIT.md"],
|
|
24
|
+
})
|
|
25
|
+
assert.deepEqual(result.conflicts, [])
|
|
26
|
+
assert.deepEqual(result.appliedFields, ["plugin", "instructions"])
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test("applyOpenKitMergePolicy rejects unsupported top-level rewrites", () => {
|
|
30
|
+
const result = applyOpenKitMergePolicy({
|
|
31
|
+
currentConfig: {
|
|
32
|
+
plugin: ["existing-plugin"],
|
|
33
|
+
theme: "light",
|
|
34
|
+
},
|
|
35
|
+
desiredConfig: {
|
|
36
|
+
plugin: ["existing-plugin", "openkit-plugin"],
|
|
37
|
+
theme: "dark",
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
assert.deepEqual(result.config, {
|
|
42
|
+
plugin: ["existing-plugin", "openkit-plugin"],
|
|
43
|
+
theme: "light",
|
|
44
|
+
})
|
|
45
|
+
assert.equal(result.conflicts.length, 1)
|
|
46
|
+
assert.deepEqual(result.conflicts[0], {
|
|
47
|
+
field: "theme",
|
|
48
|
+
reason: "unsupported-top-level-key",
|
|
49
|
+
currentValue: "light",
|
|
50
|
+
desiredValue: "dark",
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test("applyOpenKitMergePolicy rejects unclassified key additions", () => {
|
|
55
|
+
const result = applyOpenKitMergePolicy({
|
|
56
|
+
currentConfig: {
|
|
57
|
+
plugin: [],
|
|
58
|
+
},
|
|
59
|
+
desiredConfig: {
|
|
60
|
+
plugin: ["openkit-plugin"],
|
|
61
|
+
wrappers: {
|
|
62
|
+
managed: true,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
assert.deepEqual(result.config, {
|
|
68
|
+
plugin: ["openkit-plugin"],
|
|
69
|
+
})
|
|
70
|
+
assert.equal(result.conflicts.length, 1)
|
|
71
|
+
assert.deepEqual(result.conflicts[0], {
|
|
72
|
+
field: "wrappers",
|
|
73
|
+
reason: "unclassified-top-level-key",
|
|
74
|
+
currentValue: undefined,
|
|
75
|
+
desiredValue: {
|
|
76
|
+
managed: true,
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test("applyOpenKitMergePolicy preserves existing order while appending unique allowlisted values", () => {
|
|
82
|
+
const result = applyOpenKitMergePolicy({
|
|
83
|
+
currentConfig: {
|
|
84
|
+
plugin: ["alpha", "beta"],
|
|
85
|
+
instructions: ["LOCAL.md"],
|
|
86
|
+
},
|
|
87
|
+
desiredConfig: {
|
|
88
|
+
plugin: ["beta", "gamma", "alpha"],
|
|
89
|
+
instructions: ["OPENKIT.md", "LOCAL.md", "GUIDE.md"],
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
assert.deepEqual(result.config, {
|
|
94
|
+
plugin: ["alpha", "beta", "gamma"],
|
|
95
|
+
instructions: ["LOCAL.md", "OPENKIT.md", "GUIDE.md"],
|
|
96
|
+
})
|
|
97
|
+
assert.deepEqual(result.conflicts, [])
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test("applyOpenKitMergePolicy additively inserts allowed managed-install top-level keys", () => {
|
|
101
|
+
const result = applyOpenKitMergePolicy({
|
|
102
|
+
currentConfig: {
|
|
103
|
+
plugin: ["existing-plugin"],
|
|
104
|
+
instructions: ["LOCAL.md"],
|
|
105
|
+
theme: "light",
|
|
106
|
+
},
|
|
107
|
+
desiredConfig: {
|
|
108
|
+
installState: {
|
|
109
|
+
path: ".openkit/openkit-install.json",
|
|
110
|
+
schema: "openkit/install-state@1",
|
|
111
|
+
},
|
|
112
|
+
productSurface: {
|
|
113
|
+
current: "global-openkit-install",
|
|
114
|
+
installReadiness: "managed",
|
|
115
|
+
installationMode: "openkit-managed",
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
assert.deepEqual(result.config, {
|
|
121
|
+
plugin: ["existing-plugin"],
|
|
122
|
+
instructions: ["LOCAL.md"],
|
|
123
|
+
theme: "light",
|
|
124
|
+
installState: {
|
|
125
|
+
path: ".openkit/openkit-install.json",
|
|
126
|
+
schema: "openkit/install-state@1",
|
|
127
|
+
},
|
|
128
|
+
productSurface: {
|
|
129
|
+
current: "global-openkit-install",
|
|
130
|
+
installReadiness: "managed",
|
|
131
|
+
installationMode: "openkit-managed",
|
|
132
|
+
},
|
|
133
|
+
})
|
|
134
|
+
assert.deepEqual(result.conflicts, [])
|
|
135
|
+
assert.deepEqual(result.appliedFields, ["installState", "productSurface"])
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
test("applyOpenKitMergePolicy treats allowlisted object fields as equal regardless of key order", () => {
|
|
139
|
+
const result = applyOpenKitMergePolicy({
|
|
140
|
+
currentConfig: {
|
|
141
|
+
installState: {
|
|
142
|
+
schema: "openkit/install-state@1",
|
|
143
|
+
path: ".openkit/openkit-install.json",
|
|
144
|
+
},
|
|
145
|
+
productSurface: {
|
|
146
|
+
installationMode: "openkit-managed",
|
|
147
|
+
installReadiness: "managed",
|
|
148
|
+
current: "global-openkit-install",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
desiredConfig: {
|
|
152
|
+
installState: {
|
|
153
|
+
path: ".openkit/openkit-install.json",
|
|
154
|
+
schema: "openkit/install-state@1",
|
|
155
|
+
},
|
|
156
|
+
productSurface: {
|
|
157
|
+
current: "global-openkit-install",
|
|
158
|
+
installReadiness: "managed",
|
|
159
|
+
installationMode: "openkit-managed",
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
assert.deepEqual(result.config, {
|
|
165
|
+
installState: {
|
|
166
|
+
schema: "openkit/install-state@1",
|
|
167
|
+
path: ".openkit/openkit-install.json",
|
|
168
|
+
},
|
|
169
|
+
productSurface: {
|
|
170
|
+
installationMode: "openkit-managed",
|
|
171
|
+
installReadiness: "managed",
|
|
172
|
+
current: "global-openkit-install",
|
|
173
|
+
},
|
|
174
|
+
})
|
|
175
|
+
assert.deepEqual(result.conflicts, [])
|
|
176
|
+
assert.deepEqual(result.appliedFields, ["installState", "productSurface"])
|
|
177
|
+
})
|