@zaip-ai/cli 4.8.0 → 5.0.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/dist/services/adapter-hook-sources.d.ts +3 -0
- package/dist/services/adapter-hook-sources.d.ts.map +1 -1
- package/dist/services/adapter-hook-sources.js +3 -0
- package/dist/services/adapter-hook-sources.js.map +1 -1
- package/dist/services/adapters.d.ts +1 -1
- package/dist/services/adapters.d.ts.map +1 -1
- package/dist/services/adapters.js +5 -2
- package/dist/services/adapters.js.map +1 -1
- package/dist/services/adapters.test.js +1 -1
- package/dist/services/adapters.test.js.map +1 -1
- package/dist/services/codex-conversation-lifecycle-hook.d.ts.map +1 -1
- package/dist/services/codex-conversation-lifecycle-hook.js +16 -6
- package/dist/services/codex-conversation-lifecycle-hook.js.map +1 -1
- package/dist/services/codex-hook-activation.d.ts +1 -1
- package/dist/services/codex-hook-activation.d.ts.map +1 -1
- package/dist/services/codex-hook-activation.js +116 -10
- package/dist/services/codex-hook-activation.js.map +1 -1
- package/dist/services/codex-pre-tool-cli-capture-template.d.ts +2 -0
- package/dist/services/codex-pre-tool-cli-capture-template.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-cli-capture-template.js +104 -0
- package/dist/services/codex-pre-tool-cli-capture-template.js.map +1 -0
- package/dist/services/codex-pre-tool-cloud-template.d.ts +2 -0
- package/dist/services/codex-pre-tool-cloud-template.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-cloud-template.js +190 -0
- package/dist/services/codex-pre-tool-cloud-template.js.map +1 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.d.ts +2 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.js +9 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.js.map +1 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts +5 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.js +19 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.js.map +1 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts +2 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js +107 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js.map +1 -0
- package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.js +152 -190
- package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.test.js +63 -75
- package/dist/services/codex-pre-tool-hook-template.test.js.map +1 -1
- package/dist/services/codex-pre-tool-host-context-template.d.ts +2 -0
- package/dist/services/codex-pre-tool-host-context-template.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-host-context-template.js +139 -0
- package/dist/services/codex-pre-tool-host-context-template.js.map +1 -0
- package/dist/services/codex-pre-tool-support-template.d.ts.map +1 -1
- package/dist/services/codex-pre-tool-support-template.js +77 -100
- package/dist/services/codex-pre-tool-support-template.js.map +1 -1
- package/dist/services/codex-subagent-hook-template.d.ts +1 -1
- package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
- package/dist/services/codex-subagent-hook-template.js +59 -15
- package/dist/services/codex-subagent-hook-template.js.map +1 -1
- package/dist/services/codex-validation-runtime-template.d.ts.map +1 -1
- package/dist/services/codex-validation-runtime-template.js +37 -10
- package/dist/services/codex-validation-runtime-template.js.map +1 -1
- package/dist/services/runtime-template-test-support.d.ts +1 -0
- package/dist/services/runtime-template-test-support.d.ts.map +1 -1
- package/dist/services/runtime-template-test-support.js +23 -2
- package/dist/services/runtime-template-test-support.js.map +1 -1
- package/dist/services/runtime-template.test.js +7 -24
- package/dist/services/runtime-template.test.js.map +1 -1
- package/package.json +1 -1
- package/templates/.agents/README-ENG.md +49 -7
- package/templates/.agents/README.md +41 -10
- package/templates/.agents/config/agents/discovery.json +1 -1
- package/templates/.agents/dashboard/.zaip-managed.json +125 -73
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-BW7Vy1cp.js → CostCalculationGuidePage-w6pjt4OM.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-BN8KQPrr.js → DashboardHelpDialog-DhlntQcR.js} +117 -17
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Prcs-ydy.js → PricingSettingsDialog-BHoZT2an.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-HqGUImIG.js → ProjectSettingsPage-IcncIZ9Y.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CNpmQMO4.js → TraceabilityWorkspace-C6rORjpr.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-uPnOBShc.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-CF7B1Hjn.js → dashboard-localization-DWXjEIgc.js} +49 -7
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DOhy6D2-.js → icons-vendor-Hc9fp_gs.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{index-DX65Ufuu.js → index-BqeOEZW5.js} +2 -2
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CkPC-yMz.js → react-vendor-C9ta_O10.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-BEdGoIDm.js → traceability-IcefoTOv.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
- package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +10 -3
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +5 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostGroupsTable.period-values.test.tsx +2 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables-keeps-legacy-codex-identity-fields-blank.test.ts +5 -5
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +36 -43
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-loaders.ts +1 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-formatters.ts +6 -4
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.ts +2 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.ts +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +11 -25
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/cost-summary-defaults.ts +26 -0
- package/templates/.agents/dashboard/modules/operations/package.json +1 -1
- package/templates/.agents/dashboard/modules/operations/src/contracts.ts +11 -16
- package/templates/.agents/dashboard/modules/operations/src/planning-contracts.ts +14 -0
- package/templates/.agents/dashboard/modules/operations/src/react/DecisionTable.tsx +65 -0
- package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +5 -71
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +10 -36
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +7 -2
- package/templates/.agents/dashboard/modules/operations/src/react/use-operations-filter-sync.ts +66 -0
- package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
- package/templates/.agents/dashboard/packages/branding/package.json +1 -1
- package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
- package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +229 -222
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +167 -167
- package/templates/.agents/dashboard/packages/local-runtime/package.json +3 -3
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +229 -222
- package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.test.ts +38 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.ts +35 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.test.ts +69 -6
- package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.ts +62 -15
- package/templates/.agents/dashboard/packages/local-runtime/src/manifest-runs.ts +8 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/server-capture-projection-order.test.ts +118 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.test.ts +90 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.ts +86 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-routes.ts +8 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/server-projection-loaders.ts +13 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-projections.ts +30 -74
- package/templates/.agents/dashboard/packages/local-runtime/src/server-route-handlers.ts +42 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-routes.test.ts +60 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +12 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-command.test.ts +29 -4
- package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-refresh.ts +7 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +51 -54
- package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +37 -48
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-filter-matrix.test.ts +5 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +49 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +44 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-cached-cost-page.ts +11 -10
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-summary.ts +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-work-item-index.test.ts +18 -1
- package/templates/.agents/dashboard/packages/local-runtime/vitest.config.ts +12 -0
- package/templates/.agents/dashboard/packages/ui/package.json +1 -1
- package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
- package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +13 -6
- package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +1 -1
- package/templates/.agents/design/.zaip-managed.json +3 -3
- package/templates/.agents/design/apps/catalog/package.json +1 -1
- package/templates/.agents/design/packages/local-runtime/package.json +1 -1
- package/templates/.agents/requirements/autonomous-assurance.md +39 -3
- package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
- package/templates/.agents/requirements/comparative-audit.md +1 -1
- package/templates/.agents/requirements/context-engine.md +3 -3
- package/templates/.agents/requirements/cost-observability.md +1 -1
- package/templates/.agents/requirements/dashboard-launch.md +1 -1
- package/templates/.agents/requirements/data-export.md +1 -1
- package/templates/.agents/requirements/design-system.md +1 -1
- package/templates/.agents/requirements/execution-engine.md +11 -2
- package/templates/.agents/requirements/kanban-dashboard.md +1 -1
- package/templates/.agents/requirements/model-orchestration.md +1 -1
- package/templates/.agents/requirements/public-distribution.md +1 -1
- package/templates/.agents/requirements/software-architecture.md +1 -1
- package/templates/.agents/requirements/strategic-improvements.md +1 -1
- package/templates/.agents/requirements/work-lifecycle.md +2 -1
- package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
- package/templates/.agents/rules/cloud-hostinger-autonomy.md +1 -1
- package/templates/.agents/rules/production-readiness.md +24 -0
- package/templates/.agents/rules/run-lifecycle-closure.md +2 -1
- package/templates/.agents/rules/security.md +2 -2
- package/templates/.agents/rules/workflow.md +4 -3
- package/templates/.agents/runtime/VERSION +1 -1
- package/templates/.agents/runtime/bin/zaip.mjs +1185 -610
- package/templates/.agents/runtime/checksums.sha256 +6 -6
- package/templates/.agents/runtime/manifest.json +1 -1
- package/templates/.agents/runtime/pnpm-lock.sha256 +1 -1
- package/templates/.agents/runtime/provenance.json +5 -5
- package/templates/.agents/runtime/sbom.cdx.json +71 -71
- package/templates/.agents/skills/zaip-skill-execution/SKILL.md +4 -2
- package/templates/.agents/skills/zaip-skill-execution/references/controlled-release-recovery.md +44 -0
- package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +1 -1
- package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +7 -1
- package/templates/.agents/skills/zaip-skill-github-deploy/references/release-gate-matrix.md +15 -20
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.mjs +38 -2
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.test.mjs +62 -4
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +8 -6
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/guarded-commits.mjs +5 -3
- package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +7 -0
- package/templates/.agents/skills/zaip-skill-maintenance-enter/SKILL.md +23 -0
- package/templates/.agents/skills/zaip-skill-maintenance-enter/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.mjs +213 -0
- package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.test.mjs +207 -0
- package/templates/.agents/skills/zaip-skill-maintenance-exit/SKILL.md +21 -0
- package/templates/.agents/skills/zaip-skill-maintenance-exit/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-prd/SKILL.md +6 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +26 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +46 -11
- package/templates/.agents/skills/zaip-skill-spec/SKILL.md +9 -0
- package/templates/.agents/skills/zaip-skill-testing-strategy/SKILL.md +15 -0
- package/templates/.agents/zaip-aios/VERSION +1 -1
- package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +22 -0
- package/templates/.agents/zaip-aios/guides/14-troubleshooting.md +1 -1
- package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +52 -5
- package/templates/template-manifest.json +210 -126
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Co2Jiuzh.js +0 -68
|
@@ -29,6 +29,31 @@ const input = (request, identity = consumer, policy = DEFAULT_POLICY) => ({
|
|
|
29
29
|
policy,
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
test("literal SPEC continuation binds only its explicit version and optional release suffix", () => {
|
|
33
|
+
const base =
|
|
34
|
+
"pode retomar a execução da SPEC até finalizar todas as tasks e fazer publicação deploy da v5.0";
|
|
35
|
+
for (const request of [base, `${base} com release`, `${base} com release e npm publicado`]) {
|
|
36
|
+
const plan = resolveDeployment(input(request, builder));
|
|
37
|
+
assert.equal(plan.mode, "release", request);
|
|
38
|
+
assert.equal(plan.phase, "publish", request);
|
|
39
|
+
assert.equal(plan.scope, "current-work", request);
|
|
40
|
+
assert.equal(plan.version, "5.0", request);
|
|
41
|
+
assert.equal(plan.npm_publication, true, request);
|
|
42
|
+
}
|
|
43
|
+
assert.equal(resolveDeployment(input(base.replace("v5.0", "v6.2"), builder)).version, "6.2");
|
|
44
|
+
for (const request of [
|
|
45
|
+
`"${base}"`,
|
|
46
|
+
`o usuário disse '${base}'`,
|
|
47
|
+
`${base}?`,
|
|
48
|
+
`${base} se eu confirmar`,
|
|
49
|
+
`${base} não publique`,
|
|
50
|
+
`${base} e faça outra coisa`,
|
|
51
|
+
`${base} com npm publicado`,
|
|
52
|
+
base.replace("v5.0", "v05.0"),
|
|
53
|
+
])
|
|
54
|
+
assert.equal(resolveDeployment(input(request, builder)).mode, "none", request);
|
|
55
|
+
});
|
|
56
|
+
|
|
32
57
|
test("fixed defaults, explicit intent and preparation: Portuguese/English decision matrix", () => {
|
|
33
58
|
const cases = [
|
|
34
59
|
["faça deploy", null, "publish"],
|
|
@@ -127,6 +152,28 @@ test("final adjustments with an explicit publication goal preserve the actual hu
|
|
|
127
152
|
assert.equal(prepare.phase, "prepare");
|
|
128
153
|
assert.equal(prepare.npm_publication, false);
|
|
129
154
|
});
|
|
155
|
+
test("short positive publication statements bind their stated version without promoting questions or conditions", () => {
|
|
156
|
+
const request = "Boa, boa. Eu quero publicar quatro ponto nove, sim";
|
|
157
|
+
for (const [utterance, version] of [
|
|
158
|
+
[request, "4.9"],
|
|
159
|
+
["Eu quero publicar quatro ponto oito, sim", "4.8"],
|
|
160
|
+
["Quero publicar a versão v5.0", "5.0"],
|
|
161
|
+
]) {
|
|
162
|
+
const plan = resolveDeployment(input(utterance, builder));
|
|
163
|
+
assert.equal(plan.mode, "release", utterance);
|
|
164
|
+
assert.equal(plan.phase, "publish", utterance);
|
|
165
|
+
assert.equal(plan.scope, "current-work", utterance);
|
|
166
|
+
assert.equal(plan.version, version, utterance);
|
|
167
|
+
}
|
|
168
|
+
for (const unsafe of [
|
|
169
|
+
`${request}?`,
|
|
170
|
+
"Boa, boa. Eu não quero publicar quatro ponto nove, sim",
|
|
171
|
+
`Se estiver pronto, ${request}`,
|
|
172
|
+
`O usuário disse '${request}'`,
|
|
173
|
+
"Eu quero publicar quatro ponto nove se vocês aprovarem",
|
|
174
|
+
])
|
|
175
|
+
assert.equal(resolveDeployment(input(unsafe, builder)).mode, "none", unsafe);
|
|
176
|
+
});
|
|
130
177
|
test("preferences are project-owned; one-shot overrides, scope and invalid config are explicit", () => {
|
|
131
178
|
const preference = { ...DEFAULT_POLICY, default_mode: "release" };
|
|
132
179
|
assert.equal(resolveDeployment(input("faça deploy", consumer, preference)).mode, "release");
|
|
@@ -220,19 +267,21 @@ test("commits route checks before push, sends the exact SHA and never creates re
|
|
|
220
267
|
assert.equal((await guardedCommits({ ...value, dryRun: true }, operations)).pushed, false);
|
|
221
268
|
assert.ok(!events.includes("preflight"));
|
|
222
269
|
});
|
|
223
|
-
test("critical builder commit
|
|
270
|
+
test("critical builder commit uses portable injected gates and fails closed if either is missing", async (t) => {
|
|
271
|
+
const root = await mkdtemp(join(tmpdir(), "zaip-critical-portable-"));
|
|
272
|
+
t.after(() => rm(root, { recursive: true, force: true }));
|
|
224
273
|
const current = input("faça deploy apenas commits", builder);
|
|
225
274
|
const sha = "a".repeat(40);
|
|
226
275
|
const events = [];
|
|
227
276
|
const value = {
|
|
228
|
-
repository:
|
|
277
|
+
repository: root,
|
|
229
278
|
request: current.request,
|
|
230
279
|
plan: resolveDeployment(current),
|
|
231
280
|
sha,
|
|
232
281
|
branch: "main",
|
|
233
282
|
checks: [{ command: "node", args: ["--test", "fixture.test.mjs"] }],
|
|
234
283
|
};
|
|
235
|
-
|
|
284
|
+
const operations = {
|
|
236
285
|
readInput: async () => current,
|
|
237
286
|
identity: () => {},
|
|
238
287
|
branchFor: async () => "main",
|
|
@@ -243,8 +292,17 @@ test("critical builder commit qualifies locally and waits for exact main CI", as
|
|
|
243
292
|
push: async () => events.push("main"),
|
|
244
293
|
remoteSha: async () => sha,
|
|
245
294
|
verifyMainCi: async () => events.push("main-ci"),
|
|
246
|
-
}
|
|
295
|
+
};
|
|
296
|
+
await guardedCommits(value, operations);
|
|
247
297
|
assert.deepEqual(events, ["checks", "preflight", "qualify", "main", "main-ci"]);
|
|
298
|
+
for (const missing of ["qualifyPreMain", "verifyMainCi"]) {
|
|
299
|
+
events.length = 0;
|
|
300
|
+
await assert.rejects(
|
|
301
|
+
guardedCommits(value, { ...operations, [missing]: undefined }),
|
|
302
|
+
/remote-candidate-gate\.mjs/,
|
|
303
|
+
);
|
|
304
|
+
assert.ok(!events.includes("main"), `${missing} must fail before push`);
|
|
305
|
+
}
|
|
248
306
|
});
|
|
249
307
|
test("critical Task in an earlier candidate commit requires candidate-bound proof", async (t) => {
|
|
250
308
|
const root = await mkdtemp(join(tmpdir(), "zaip-critical-range-"));
|
package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs
CHANGED
|
@@ -67,10 +67,7 @@ test("deploy guidance preserves canonical Task identity and builder-versus-consu
|
|
|
67
67
|
skill,
|
|
68
68
|
/verified builder means Release plus npm through Actions; a consumer means commits plus push/,
|
|
69
69
|
);
|
|
70
|
-
assert.match(
|
|
71
|
-
workflow,
|
|
72
|
-
/Preserve trabalho\/index concorrentes e causalidade de todos os commits publicados/,
|
|
73
|
-
);
|
|
70
|
+
assert.match(workflow, /Preserve trabalho, index, causalidade, SHA, integridade e autorização/);
|
|
74
71
|
});
|
|
75
72
|
|
|
76
73
|
test("framework deploy follows Release-triggered npm publication without claiming partial success", async () => {
|
|
@@ -124,10 +121,15 @@ test("deploy permission and reconciliation remain explicit and recoverable", asy
|
|
|
124
121
|
});
|
|
125
122
|
|
|
126
123
|
test("workflow retains the explicit publication routing contract", async () => {
|
|
127
|
-
const workflow = await
|
|
124
|
+
const [workflow, core, skill] = await Promise.all([
|
|
125
|
+
readFile(resolve(root, "rules", "workflow.md"), "utf8"),
|
|
126
|
+
readFile(resolve(root, "rules", "core.md"), "utf8"),
|
|
127
|
+
readFile(resolve(skillRoot, "SKILL.md"), "utf8"),
|
|
128
|
+
]);
|
|
128
129
|
assert.match(workflow, /\$zaip-skill-github-deploy/);
|
|
129
130
|
assert.match(workflow, /deploy-policy\.json/);
|
|
130
|
-
assert.match(
|
|
131
|
+
assert.match(core, /Escopo `current-work`/);
|
|
132
|
+
assert.match(skill, /Default scope is `current-work`/);
|
|
131
133
|
});
|
|
132
134
|
|
|
133
135
|
test("release guidance requires reviewed categorized notes and published-body readback", async () => {
|
|
@@ -134,9 +134,11 @@ export async function guardedCommits(input, operations = {}) {
|
|
|
134
134
|
input.sha,
|
|
135
135
|
input.remote ?? "origin",
|
|
136
136
|
));
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
// Portable callers can supply both gates; otherwise load the real gate before push.
|
|
138
|
+
const candidateGate =
|
|
139
|
+
critical && (!operations.qualifyPreMain || !operations.verifyMainCi)
|
|
140
|
+
? await import(pathToFileURL(resolve(root, "scripts/remote-candidate-gate.mjs")).href)
|
|
141
|
+
: null;
|
|
140
142
|
if (critical) {
|
|
141
143
|
await (operations.qualifyPreMain ?? candidateGate.qualifyLocalCandidate)({
|
|
142
144
|
repository: root,
|
|
@@ -42,6 +42,13 @@ the supplied ID and canonical record; never scan ordinary history to guess a tar
|
|
|
42
42
|
while the responsible root is active. A finalizer stop may close that root, so do not
|
|
43
43
|
send it an unfinished framework lock, failed local check, or outdated SHA evidence.
|
|
44
44
|
Rebind validation and independent review after each candidate-changing commit.
|
|
45
|
+
10. For a confirmed residual Git `index.lock`, use the official `git-lock recover --task TASK
|
|
46
|
+
--run RUN --decision DEC-HUMAN-ID --json` command before claiming a Task workspace.
|
|
47
|
+
The Decision must explicitly authorize this lock recovery. The command verifies the real
|
|
48
|
+
Git dir (including linked worktrees), a regular empty lock older than ten minutes,
|
|
49
|
+
no active Git process or operation marker, and a stable second stat; it moves the lock
|
|
50
|
+
to a verified backup. An inconclusive check refuses recovery. Preserve that backup
|
|
51
|
+
and report its path; use the existing Task workspace flow afterwards.
|
|
45
52
|
|
|
46
53
|
Use `$zaip-skill-reconcile-telemetry` only for missing observed usage after lifecycle
|
|
47
54
|
identity is correct; it is not a substitute for closure.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zaip-skill-maintenance-enter
|
|
3
|
+
description: Enter explicitly authorized ZAIP framework maintenance with governance disabled and canonical telemetry enabled.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Entrar em manutenção
|
|
7
|
+
|
|
8
|
+
Use quando o usuário pedir manutenção direta do framework ou autorizar desligar sua governança para uma correção delimitada. Um bloqueio de ferramenta, sozinho, não autoriza manutenção. Reutilize a autorização vigente; não peça outra confirmação para o mesmo escopo.
|
|
9
|
+
|
|
10
|
+
1. No agente principal, confirme a raiz e o escopo autorizado. O toggle afeta todos os agentes e chats deste projeto: não entre enquanto houver trabalho governado concorrente sem coordenação. Um subagente solicita a transição ao principal; não a executa por conta própria.
|
|
11
|
+
2. Execute na raiz, com o Node disponível no host:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
node .agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.mjs enter
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Para outra raiz explícita, acrescente `--root <diretório>`. Use hooks atualizados com os dois controles independentes. A leitura estática do script detecta a presença da interface; não atesta integridade, habilitação no cliente ou coleta real. A confirmação vem da etapa 4. Usa o [parser YAML distribuído](../zaip-skill-github-deploy/scripts/yaml-parser.mjs); não requer instalação de dependências. Os controles devem estar no formato canônico de blocos YAML com chaves sem aspas e booleanos literais; formatos alternativos são recusados sem alteração.
|
|
18
|
+
3. Confira a saída JSON: `zaip_enabled: false`, `tracking_enabled: true`. A entrada liga tracking caso estivesse desligado; isso faz parte deste modo com telemetria. Somente os dois booleanos mudam; comentários e demais configurações ficam preservados. Repetir a entrada é idempotente. O recibo de recuperação fica em `.local/zaip-maintenance/state.json`.
|
|
19
|
+
4. Faça uma chamada real de leitura e confira o registro observado em `.local/hooks/observations`. Os quatro hooks permanecem instalados e habilitados no cliente; não altere `.codex/config.toml`. O script não liga um hook desabilitado na interface. Configuração ligada não prova captura: confira o recibo e o processamento canônico sem inventar IDs ou métricas. Se o cliente não disparar o hook, informe a pendência; não declare telemetria comprovada.
|
|
20
|
+
5. Trabalhe diretamente com as ferramentas nativas do Codex no escopo autorizado, sem materializar Task/SPEC/PRD, claims ou transições ZAIP apenas para satisfazer sua governança suspensa. Preserve registros existentes. A manutenção não amplia permissões do host, acesso a segredos, publicação ou ações destrutivas. Nunca reinicie, desligue, suspenda, hiberne ou encerre a sessão do computador.
|
|
21
|
+
6. Ao terminar a manutenção, execute `$zaip-skill-maintenance-exit`, salvo pedido explícito para permanecer neste modo. Ao retomar após interrupção, use o comando com `status`, leia o estado atual e prossiga; não restaure uma cópia inteira de `project.yaml` sobre edições feitas durante a manutenção.
|
|
22
|
+
|
|
23
|
+
O recibo local registra apenas a transição, não substitui a telemetria. Os hooks existentes continuam observando a sessão e salvando dados canônicos conforme `tracking.storage_mode`; valores indisponíveis continuam nulos. Encerrar manutenção não encerra automaticamente Tasks ou Runs históricas.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
closeSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
lstatSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
openSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
realpathSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
unlinkSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from "node:fs";
|
|
14
|
+
import { dirname, resolve } from "node:path";
|
|
15
|
+
import { pathToFileURL } from "node:url";
|
|
16
|
+
import { parseDocument } from "../../zaip-skill-github-deploy/scripts/yaml-parser.mjs";
|
|
17
|
+
|
|
18
|
+
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
19
|
+
|
|
20
|
+
function physical(root, parts, create = false) {
|
|
21
|
+
let current = root;
|
|
22
|
+
for (const part of parts) {
|
|
23
|
+
current = resolve(current, part);
|
|
24
|
+
if (create && !existsSync(current)) mkdirSync(current);
|
|
25
|
+
if (lstatSync(current).isSymbolicLink()) throw Error(`Linked maintenance path: ${current}`);
|
|
26
|
+
}
|
|
27
|
+
return current;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function atomic(path, content, expected) {
|
|
31
|
+
if (existsSync(path) && lstatSync(path).isSymbolicLink()) throw Error("Linked output rejected");
|
|
32
|
+
const temporary = resolve(dirname(path), `.maintenance-${randomUUID()}.tmp`);
|
|
33
|
+
try {
|
|
34
|
+
writeFileSync(temporary, content, {
|
|
35
|
+
flag: "wx",
|
|
36
|
+
mode: existsSync(path) ? lstatSync(path).mode & 0o777 : 0o600,
|
|
37
|
+
});
|
|
38
|
+
if (expected !== undefined && readFileSync(path, "utf8") !== expected)
|
|
39
|
+
throw Error("Configuration changed concurrently; retry after coordinating writers");
|
|
40
|
+
renameSync(temporary, path);
|
|
41
|
+
} finally {
|
|
42
|
+
if (existsSync(temporary)) unlinkSync(temporary);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function inspect(source) {
|
|
47
|
+
if (Buffer.byteLength(source) > 65536) throw Error("Project policy exceeds the hook input limit");
|
|
48
|
+
const doc = parseDocument(source, { strict: true, uniqueKeys: true });
|
|
49
|
+
if (doc.errors.length || doc.warnings.length) throw Error("Invalid or unsupported project YAML");
|
|
50
|
+
// Accept the canonical block form only. Do not execute JavaScript from --root.
|
|
51
|
+
for (const name of ["zaip", "tracking"]) {
|
|
52
|
+
const pair = doc.contents?.items?.find((item) => item.key?.value === name);
|
|
53
|
+
const section = pair?.value;
|
|
54
|
+
const enabled = section?.items?.find((item) => item.key?.value === "enabled");
|
|
55
|
+
const key = pair?.key;
|
|
56
|
+
if (
|
|
57
|
+
!key?.range ||
|
|
58
|
+
!enabled?.key?.range ||
|
|
59
|
+
section.flow ||
|
|
60
|
+
section.anchor ||
|
|
61
|
+
section.tag ||
|
|
62
|
+
source.slice(key.range[0], key.range[1]) !== name ||
|
|
63
|
+
(key.range[0] > 0 && source[key.range[0] - 1] !== "\n") ||
|
|
64
|
+
!new RegExp("^" + name + ":[ \\t]*(?:#[^\\r\\n]*)?\\r?\\n").test(
|
|
65
|
+
source.slice(key.range[0]),
|
|
66
|
+
) ||
|
|
67
|
+
source.slice(enabled.key.range[0], enabled.key.range[1]) !== "enabled"
|
|
68
|
+
) {
|
|
69
|
+
throw Error("Use canonical unquoted zaip/tracking blocks with literal enabled keys");
|
|
70
|
+
}
|
|
71
|
+
const lineStart = source.lastIndexOf("\n", enabled.key.range[0] - 1) + 1;
|
|
72
|
+
if (
|
|
73
|
+
!/^ +$/.test(source.slice(lineStart, enabled.key.range[0])) ||
|
|
74
|
+
!enabled.value?.range ||
|
|
75
|
+
!/^: +$/.test(source.slice(enabled.key.range[1], enabled.value.range[0]))
|
|
76
|
+
) {
|
|
77
|
+
throw Error("Use canonical enabled: true/false on one indented line");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const nodes = ["zaip", "tracking"].map((key) => doc.getIn([key, "enabled"], true));
|
|
81
|
+
for (const node of nodes) {
|
|
82
|
+
if (
|
|
83
|
+
!node ||
|
|
84
|
+
typeof node.value !== "boolean" ||
|
|
85
|
+
node.anchor ||
|
|
86
|
+
node.tag ||
|
|
87
|
+
!node.range ||
|
|
88
|
+
!/^(true|false)$/.test(source.slice(node.range[0], node.range[1]))
|
|
89
|
+
) {
|
|
90
|
+
throw Error("zaip.enabled and tracking.enabled must be explicit literal booleans");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const policy = { governance: nodes[0].value, tracking: nodes[1].value };
|
|
94
|
+
return { nodes, policy };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function maintenance(action, directory = process.cwd()) {
|
|
98
|
+
if (!["enter", "exit", "status"].includes(action))
|
|
99
|
+
throw Error("Use enter, exit or status [--root <directory>]");
|
|
100
|
+
const root = realpathSync(resolve(directory));
|
|
101
|
+
const config = physical(root, [".agents", "config", "project.yaml"]);
|
|
102
|
+
const hook = physical(root, [".codex", "hooks", "zaip-hook-activation.mjs"]);
|
|
103
|
+
const hookSource = readFileSync(hook, "utf8");
|
|
104
|
+
if (
|
|
105
|
+
!hookSource.includes("export function parseHookPolicies(") ||
|
|
106
|
+
!hookSource.includes("export async function hookPolicies(")
|
|
107
|
+
)
|
|
108
|
+
throw Error(
|
|
109
|
+
"Update the four hooks to support independent governance and tracking before maintenance",
|
|
110
|
+
);
|
|
111
|
+
let source = readFileSync(config, "utf8");
|
|
112
|
+
let parsed = inspect(source);
|
|
113
|
+
const result = (state, changed) => ({
|
|
114
|
+
action,
|
|
115
|
+
root,
|
|
116
|
+
zaip_enabled: parsed.policy.governance,
|
|
117
|
+
tracking_enabled: parsed.policy.tracking,
|
|
118
|
+
phase: state?.phase ?? null,
|
|
119
|
+
changed,
|
|
120
|
+
config_sha256: hash(source),
|
|
121
|
+
telemetry: "verify_native_hook_observation",
|
|
122
|
+
hook_check: "interface_presence_only",
|
|
123
|
+
});
|
|
124
|
+
if (action === "status") {
|
|
125
|
+
const path = resolve(root, ".local/zaip-maintenance/state.json");
|
|
126
|
+
if (!existsSync(path)) return result(null, false);
|
|
127
|
+
physical(root, [".local", "zaip-maintenance", "state.json"]);
|
|
128
|
+
const state = JSON.parse(readFileSync(path, "utf8"));
|
|
129
|
+
if (state.schema_version !== 1 || state.root !== root)
|
|
130
|
+
throw Error("Invalid maintenance receipt");
|
|
131
|
+
return result(state, false);
|
|
132
|
+
}
|
|
133
|
+
const local = physical(root, [".local", "zaip-maintenance"], true);
|
|
134
|
+
const lock = resolve(local, "transition.lock");
|
|
135
|
+
let fd;
|
|
136
|
+
try {
|
|
137
|
+
fd = openSync(lock, "wx", 0o600);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error.code === "EEXIST")
|
|
140
|
+
throw Error(
|
|
141
|
+
"Maintenance transition locked; inspect transition.lock and coordinate its owner",
|
|
142
|
+
);
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
writeFileSync(fd, JSON.stringify({ pid: process.pid, created_at: new Date().toISOString() }));
|
|
147
|
+
source = readFileSync(config, "utf8");
|
|
148
|
+
parsed = inspect(source);
|
|
149
|
+
const statePath = resolve(local, "state.json");
|
|
150
|
+
let state = null;
|
|
151
|
+
if (existsSync(statePath)) {
|
|
152
|
+
physical(root, [".local", "zaip-maintenance", "state.json"]);
|
|
153
|
+
state = JSON.parse(readFileSync(statePath, "utf8"));
|
|
154
|
+
if (state.schema_version !== 1 || state.root !== root)
|
|
155
|
+
throw Error("Invalid maintenance receipt");
|
|
156
|
+
}
|
|
157
|
+
const desired = action === "exit";
|
|
158
|
+
const phase = desired ? "inactive" : "active";
|
|
159
|
+
if (state?.phase === phase && parsed.policy.governance === desired && parsed.policy.tracking)
|
|
160
|
+
return result(state, false);
|
|
161
|
+
if (!state || (action === "enter" && state.phase === "inactive")) {
|
|
162
|
+
state = {
|
|
163
|
+
schema_version: 1,
|
|
164
|
+
root,
|
|
165
|
+
started_at: new Date().toISOString(),
|
|
166
|
+
previous: {
|
|
167
|
+
zaip_enabled: parsed.policy.governance,
|
|
168
|
+
tracking_enabled: parsed.policy.tracking,
|
|
169
|
+
},
|
|
170
|
+
initial_config_sha256: hash(source),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
state.phase = desired ? "exiting" : "entering";
|
|
174
|
+
atomic(statePath, `${JSON.stringify(state, null, 2)}\n`);
|
|
175
|
+
let next = source;
|
|
176
|
+
const replacements = parsed.nodes
|
|
177
|
+
.map((node, index) => ({
|
|
178
|
+
start: node.range[0],
|
|
179
|
+
end: node.range[1],
|
|
180
|
+
value: String(index === 0 ? desired : true),
|
|
181
|
+
}))
|
|
182
|
+
.sort((a, b) => b.start - a.start);
|
|
183
|
+
for (const edit of replacements)
|
|
184
|
+
next = next.slice(0, edit.start) + edit.value + next.slice(edit.end);
|
|
185
|
+
inspect(next);
|
|
186
|
+
const changed = next !== source;
|
|
187
|
+
if (changed) atomic(config, next, source);
|
|
188
|
+
source = readFileSync(config, "utf8");
|
|
189
|
+
parsed = inspect(source);
|
|
190
|
+
if (parsed.policy.governance !== desired || !parsed.policy.tracking)
|
|
191
|
+
throw Error("Transition verification failed");
|
|
192
|
+
state.phase = phase;
|
|
193
|
+
state.updated_at = new Date().toISOString();
|
|
194
|
+
state.config_sha256 = hash(source);
|
|
195
|
+
atomic(statePath, `${JSON.stringify(state, null, 2)}\n`);
|
|
196
|
+
return result(state, changed);
|
|
197
|
+
} finally {
|
|
198
|
+
closeSync(fd);
|
|
199
|
+
unlinkSync(lock);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
204
|
+
const [action, flag, directory, ...extra] = process.argv.slice(2);
|
|
205
|
+
try {
|
|
206
|
+
if (extra.length || (flag !== undefined && (flag !== "--root" || !directory)))
|
|
207
|
+
throw Error("Use enter, exit or status [--root <directory>]");
|
|
208
|
+
console.log(JSON.stringify(await maintenance(action, directory)));
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error(`Maintenance transition failed: ${error.message}`);
|
|
211
|
+
process.exitCode = 1;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import {
|
|
4
|
+
copyFileSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
mkdtempSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
rmSync,
|
|
10
|
+
symlinkSync,
|
|
11
|
+
writeFileSync,
|
|
12
|
+
} from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { resolve } from "node:path";
|
|
15
|
+
import { test } from "node:test";
|
|
16
|
+
import { maintenance } from "./maintenance.mjs";
|
|
17
|
+
|
|
18
|
+
const sourceRoot = resolve(import.meta.dirname, "../../../..");
|
|
19
|
+
// Public-template tests use the installed hook contract from the builder workspace.
|
|
20
|
+
const hookRoot = existsSync(resolve(sourceRoot, ".codex/hooks/zaip-hook-activation.mjs"))
|
|
21
|
+
? sourceRoot
|
|
22
|
+
: resolve(sourceRoot, "../../../..");
|
|
23
|
+
const original =
|
|
24
|
+
"# preserved\r\nzaip:\r\n enabled: true # governance\r\ntracking:\r\n enabled: true\r\n storage_mode: tracked_sanitized\r\nproject:\r\n name: original\r\n";
|
|
25
|
+
function fixture(t, source = original) {
|
|
26
|
+
const root = mkdtempSync(resolve(tmpdir(), "zaip-maintenance-test-"));
|
|
27
|
+
t.after(() => rmSync(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 }));
|
|
28
|
+
mkdirSync(resolve(root, ".agents/config"), { recursive: true });
|
|
29
|
+
mkdirSync(resolve(root, ".codex/hooks"), { recursive: true });
|
|
30
|
+
const config = resolve(root, ".agents/config/project.yaml");
|
|
31
|
+
writeFileSync(config, source);
|
|
32
|
+
copyFileSync(
|
|
33
|
+
resolve(hookRoot, ".codex/hooks/zaip-hook-activation.mjs"),
|
|
34
|
+
resolve(root, ".codex/hooks/zaip-hook-activation.mjs"),
|
|
35
|
+
);
|
|
36
|
+
return {
|
|
37
|
+
root,
|
|
38
|
+
config,
|
|
39
|
+
read: () => readFileSync(config, "utf8"),
|
|
40
|
+
state: resolve(root, ".local/zaip-maintenance/state.json"),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
test("enter and exit retain telemetry, comments and CRLF; repetition is idempotent", async (t) => {
|
|
45
|
+
const f = fixture(t);
|
|
46
|
+
const entered = await maintenance("enter", f.root);
|
|
47
|
+
assert.equal(entered.zaip_enabled, false);
|
|
48
|
+
assert.equal(entered.tracking_enabled, true);
|
|
49
|
+
assert.equal(
|
|
50
|
+
f.read(),
|
|
51
|
+
original.replace("enabled: true # governance", "enabled: false # governance"),
|
|
52
|
+
);
|
|
53
|
+
const state = readFileSync(f.state, "utf8");
|
|
54
|
+
assert.equal((await maintenance("status", f.root)).phase, "active");
|
|
55
|
+
assert.equal((await maintenance("enter", f.root)).changed, false);
|
|
56
|
+
assert.equal(readFileSync(f.state, "utf8"), state);
|
|
57
|
+
assert.equal((await maintenance("exit", f.root)).zaip_enabled, true);
|
|
58
|
+
assert.equal(f.read(), original);
|
|
59
|
+
assert.equal((await maintenance("exit", f.root)).changed, false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("exit preserves configuration edits made during maintenance", async (t) => {
|
|
63
|
+
const f = fixture(t);
|
|
64
|
+
await maintenance("enter", f.root);
|
|
65
|
+
writeFileSync(f.config, f.read().replace("name: original", "name: changed"));
|
|
66
|
+
await maintenance("exit", f.root);
|
|
67
|
+
assert.equal(f.read(), original.replace("name: original", "name: changed"));
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("manual disabled state and disabled tracking become active telemetry and exit restores governance", async (t) => {
|
|
71
|
+
const f = fixture(t, original.replaceAll("enabled: true", "enabled: false"));
|
|
72
|
+
const entered = await maintenance("enter", f.root);
|
|
73
|
+
assert.equal(entered.zaip_enabled, false);
|
|
74
|
+
assert.equal(entered.tracking_enabled, true);
|
|
75
|
+
assert.equal((await maintenance("exit", f.root)).zaip_enabled, true);
|
|
76
|
+
assert.equal(f.read(), original);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("exit without a prior receipt supports manual maintenance", async (t) => {
|
|
80
|
+
const f = fixture(
|
|
81
|
+
t,
|
|
82
|
+
original.replace("enabled: true # governance", "enabled: false # governance"),
|
|
83
|
+
);
|
|
84
|
+
assert.equal((await maintenance("exit", f.root)).tracking_enabled, true);
|
|
85
|
+
assert.equal(f.read(), original);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("status never changes configuration or creates local state", async (t) => {
|
|
89
|
+
const f = fixture(t);
|
|
90
|
+
assert.equal((await maintenance("status", f.root)).zaip_enabled, true);
|
|
91
|
+
assert.equal(existsSync(resolve(f.root, ".local")), false);
|
|
92
|
+
assert.equal(f.read(), original);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("never executes hook code supplied by the target directory", async (t) => {
|
|
96
|
+
const f = fixture(t);
|
|
97
|
+
writeFileSync(
|
|
98
|
+
resolve(f.root, ".codex/hooks/zaip-hook-activation.mjs"),
|
|
99
|
+
'throw Error("Target code must not execute");\nexport function parseHookPolicies() {}\nexport async function hookPolicies() {}\n',
|
|
100
|
+
);
|
|
101
|
+
assert.equal((await maintenance("status", f.root)).zaip_enabled, true);
|
|
102
|
+
assert.equal((await maintenance("enter", f.root)).tracking_enabled, true);
|
|
103
|
+
assert.equal((await maintenance("exit", f.root)).zaip_enabled, true);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
for (const [label, source] of [
|
|
107
|
+
["string boolean", original.replace("enabled: true # governance", 'enabled: "true"')],
|
|
108
|
+
["duplicate control", original.replace("zaip:", "zaip:\n enabled: false")],
|
|
109
|
+
["missing tracking", "zaip:\n enabled: true\n"],
|
|
110
|
+
["malformed YAML", `${original}broken: [\n`],
|
|
111
|
+
["alias control", original.replace("enabled: true # governance", "enabled: &flag true")],
|
|
112
|
+
["hook-incompatible key", original.replace("zaip:", '"zaip":')],
|
|
113
|
+
["explicit enabled key", original.replace("enabled: true # governance", "? enabled\r\n : true")],
|
|
114
|
+
["space before colon", original.replace("enabled: true # governance", "enabled : true")],
|
|
115
|
+
]) {
|
|
116
|
+
test(`rejects ${label} before mutation`, async (t) => {
|
|
117
|
+
const f = fixture(t, source);
|
|
118
|
+
await assert.rejects(maintenance("enter", f.root));
|
|
119
|
+
assert.equal(f.read(), source);
|
|
120
|
+
assert.equal(existsSync(resolve(f.root, ".local")), false);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
test("contention refuses a second transition without touching the owner lock", async (t) => {
|
|
125
|
+
const f = fixture(t);
|
|
126
|
+
const dir = resolve(f.root, ".local/zaip-maintenance");
|
|
127
|
+
mkdirSync(dir, { recursive: true });
|
|
128
|
+
const lock = resolve(dir, "transition.lock");
|
|
129
|
+
writeFileSync(lock, "existing owner");
|
|
130
|
+
await assert.rejects(maintenance("enter", f.root), /locked/);
|
|
131
|
+
assert.equal(readFileSync(lock, "utf8"), "existing owner");
|
|
132
|
+
assert.equal(f.read(), original);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("interrupted receipt can be reconciled without resetting the original snapshot", async (t) => {
|
|
136
|
+
const f = fixture(t);
|
|
137
|
+
await maintenance("enter", f.root);
|
|
138
|
+
const state = JSON.parse(readFileSync(f.state, "utf8"));
|
|
139
|
+
state.phase = "entering";
|
|
140
|
+
writeFileSync(f.state, JSON.stringify(state));
|
|
141
|
+
assert.equal((await maintenance("enter", f.root)).changed, false);
|
|
142
|
+
assert.equal(
|
|
143
|
+
JSON.parse(readFileSync(f.state, "utf8")).initial_config_sha256,
|
|
144
|
+
state.initial_config_sha256,
|
|
145
|
+
);
|
|
146
|
+
assert.equal((await maintenance("exit", f.root)).zaip_enabled, true);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("linked configuration directories cannot mutate another project", async (t) => {
|
|
150
|
+
const target = fixture(t);
|
|
151
|
+
const f = fixture(t);
|
|
152
|
+
rmSync(resolve(f.root, ".agents/config"), { recursive: true });
|
|
153
|
+
symlinkSync(
|
|
154
|
+
resolve(target.root, ".agents/config"),
|
|
155
|
+
resolve(f.root, ".agents/config"),
|
|
156
|
+
"junction",
|
|
157
|
+
);
|
|
158
|
+
t.after(() => {
|
|
159
|
+
if (existsSync(resolve(f.root, ".agents/config"))) rmSync(resolve(f.root, ".agents/config"));
|
|
160
|
+
});
|
|
161
|
+
await assert.rejects(maintenance("enter", f.root), /Linked/);
|
|
162
|
+
assert.equal(target.read(), original);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("CLI works from another cwd with an explicit root and rejects extra arguments", (t) => {
|
|
166
|
+
const f = fixture(t);
|
|
167
|
+
const script = resolve(import.meta.dirname, "maintenance.mjs");
|
|
168
|
+
const run = (...args) =>
|
|
169
|
+
spawnSync(process.execPath, [script, ...args], {
|
|
170
|
+
cwd: tmpdir(),
|
|
171
|
+
encoding: "utf8",
|
|
172
|
+
windowsHide: true,
|
|
173
|
+
});
|
|
174
|
+
const entered = run("enter", "--root", f.root);
|
|
175
|
+
assert.equal(entered.status, 0, entered.stderr);
|
|
176
|
+
assert.equal(JSON.parse(entered.stdout).tracking_enabled, true);
|
|
177
|
+
const before = f.read();
|
|
178
|
+
assert.equal(run("exit", "--root", f.root, "extra").status, 1);
|
|
179
|
+
assert.equal(f.read(), before);
|
|
180
|
+
assert.equal(run("exit", "--root", f.root).status, 0);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("installed helper runs without node_modules or the builder checkout", (t) => {
|
|
184
|
+
const f = fixture(t);
|
|
185
|
+
const script = resolve(
|
|
186
|
+
f.root,
|
|
187
|
+
".agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.mjs",
|
|
188
|
+
);
|
|
189
|
+
const parser = resolve(f.root, ".agents/skills/zaip-skill-github-deploy/scripts/yaml-parser.mjs");
|
|
190
|
+
mkdirSync(resolve(script, ".."), { recursive: true });
|
|
191
|
+
mkdirSync(resolve(parser, ".."), { recursive: true });
|
|
192
|
+
copyFileSync(resolve(import.meta.dirname, "maintenance.mjs"), script);
|
|
193
|
+
copyFileSync(
|
|
194
|
+
resolve(import.meta.dirname, "../../zaip-skill-github-deploy/scripts/yaml-parser.mjs"),
|
|
195
|
+
parser,
|
|
196
|
+
);
|
|
197
|
+
for (const action of ["enter", "exit"]) {
|
|
198
|
+
const result = spawnSync(process.execPath, [script, action], {
|
|
199
|
+
cwd: f.root,
|
|
200
|
+
encoding: "utf8",
|
|
201
|
+
windowsHide: true,
|
|
202
|
+
});
|
|
203
|
+
assert.equal(result.status, 0, result.stderr);
|
|
204
|
+
assert.equal(JSON.parse(result.stdout).tracking_enabled, true);
|
|
205
|
+
}
|
|
206
|
+
assert.equal(f.read(), original);
|
|
207
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zaip-skill-maintenance-exit
|
|
3
|
+
description: Finish authorized ZAIP maintenance by reactivating governance, retaining telemetry, and verifying the next native hook invocation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sair da manutenção
|
|
7
|
+
|
|
8
|
+
Use ao concluir manutenção autorizada ou quando o usuário pedir reativação. Funciona também após desativação manual sem recibo prévio. No agente principal, termine ou coordene os escritores de manutenção antes de reativar a governança compartilhada do projeto.
|
|
9
|
+
|
|
10
|
+
1. Execute na raiz, com o Node do host:
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
node .agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.mjs exit
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
O [script compartilhado](../zaip-skill-maintenance-enter/scripts/maintenance.mjs) também aceita `status` e `--root <diretório>`. Depende da skill de entrada e do parser YAML distribuído; não exige Node instalado no projeto.
|
|
17
|
+
2. Exija `zaip_enabled: true` e `tracking_enabled: true` na saída. A saída reativa ZAIP mesmo quando ele já estava desligado antes da entrada, e mantém tracking ligado. Ela altera somente esses booleanos no arquivo atual: preserva edições de configuração feitas durante a manutenção. Não restaure um backup integral nem desligue tracking ao sair. Repetir a saída é seguro.
|
|
18
|
+
3. Faça uma nova chamada nativa de leitura. Confira `.local/hooks/activation/PreToolUse.json` e o recibo de observação da sessão/turno realmente vistos. Se os hooks estiverem desligados na interface, peça sua reativação e deixe a validação pendente; não falsifique recibos. Não execute comandos destrutivos, de energia do computador ou publicações como teste.
|
|
19
|
+
4. A partir da chamada seguinte, volte a aplicar a governança ativa. Informe estado final, testes observados e qualquer lacuna de telemetria. Não declare encerramento de Tasks/SPECs históricas nem totais finais de consumo sem prova.
|
|
20
|
+
|
|
21
|
+
Se houver falha, execute `status` e leia o erro. A gravação atômica mantém a configuração anterior quando a substituição falha; uma interrupção posterior pode deixar a transição aplicada e o recibo pendente. Repita a mesma operação após resolver a causa. Lock ocupado impede transições simultâneas: verifique o processo registrado, sem apagar um lock de processo ativo. Nunca desligue os hooks ou tracking para contornar o erro.
|
|
@@ -5,6 +5,12 @@ description: Create, refine, prepare, or analyze a ZAIP PRD on explicit PRD work
|
|
|
5
5
|
|
|
6
6
|
# Trabalhar com PRDs
|
|
7
7
|
|
|
8
|
+
Antes de promover um PRD novo, aplique o aceite binário `proportional-acceptance-v1` de `@zaip-ai/specs` conforme `docs/quality/production-readiness.md` via `zaip plan build --acceptance <contrato.json>` e valide o plano embutido. Registre por outcome obrigatório origem, aplicabilidade, resultado observável, prova suficiente, responsável e condição de parada. Só adicione obrigação ligada ao pedido, Requirement ativo ou risco concreto. Melhoria opcional não bloqueia a promoção; resultado ausente não é aprovação. Preserve snapshots anteriores até migração explícita versionada e atribuída.
|
|
9
|
+
|
|
10
|
+
Ao definir ACs, selecione testes de código e fluxos de dashboard pelo impacto; a
|
|
11
|
+
qualificação integral do pacote pertence ao candidato final antes da publicação.
|
|
12
|
+
Reuso de prova exige identidade e contrato ainda válidos.
|
|
13
|
+
|
|
8
14
|
PRD é opcional e permanece em Definição com `status: draft` durante criação, revisão, refinamento e `prepare`. Ter SPECs ou PreparedPlan válido não autoriza promoção.
|
|
9
15
|
|
|
10
16
|
## Limite de autoridade
|