@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.
Files changed (195) hide show
  1. package/dist/services/adapter-hook-sources.d.ts +3 -0
  2. package/dist/services/adapter-hook-sources.d.ts.map +1 -1
  3. package/dist/services/adapter-hook-sources.js +3 -0
  4. package/dist/services/adapter-hook-sources.js.map +1 -1
  5. package/dist/services/adapters.d.ts +1 -1
  6. package/dist/services/adapters.d.ts.map +1 -1
  7. package/dist/services/adapters.js +5 -2
  8. package/dist/services/adapters.js.map +1 -1
  9. package/dist/services/adapters.test.js +1 -1
  10. package/dist/services/adapters.test.js.map +1 -1
  11. package/dist/services/codex-conversation-lifecycle-hook.d.ts.map +1 -1
  12. package/dist/services/codex-conversation-lifecycle-hook.js +16 -6
  13. package/dist/services/codex-conversation-lifecycle-hook.js.map +1 -1
  14. package/dist/services/codex-hook-activation.d.ts +1 -1
  15. package/dist/services/codex-hook-activation.d.ts.map +1 -1
  16. package/dist/services/codex-hook-activation.js +116 -10
  17. package/dist/services/codex-hook-activation.js.map +1 -1
  18. package/dist/services/codex-pre-tool-cli-capture-template.d.ts +2 -0
  19. package/dist/services/codex-pre-tool-cli-capture-template.d.ts.map +1 -0
  20. package/dist/services/codex-pre-tool-cli-capture-template.js +104 -0
  21. package/dist/services/codex-pre-tool-cli-capture-template.js.map +1 -0
  22. package/dist/services/codex-pre-tool-cloud-template.d.ts +2 -0
  23. package/dist/services/codex-pre-tool-cloud-template.d.ts.map +1 -0
  24. package/dist/services/codex-pre-tool-cloud-template.js +190 -0
  25. package/dist/services/codex-pre-tool-cloud-template.js.map +1 -0
  26. package/dist/services/codex-pre-tool-dependencies.test-support.d.ts +2 -0
  27. package/dist/services/codex-pre-tool-dependencies.test-support.d.ts.map +1 -0
  28. package/dist/services/codex-pre-tool-dependencies.test-support.js +9 -0
  29. package/dist/services/codex-pre-tool-dependencies.test-support.js.map +1 -0
  30. package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts +5 -0
  31. package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts.map +1 -0
  32. package/dist/services/codex-pre-tool-hook-fixture.test-support.js +19 -0
  33. package/dist/services/codex-pre-tool-hook-fixture.test-support.js.map +1 -0
  34. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts +2 -0
  35. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts.map +1 -0
  36. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js +107 -0
  37. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js.map +1 -0
  38. package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
  39. package/dist/services/codex-pre-tool-hook-template.js +152 -190
  40. package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
  41. package/dist/services/codex-pre-tool-hook-template.test.js +63 -75
  42. package/dist/services/codex-pre-tool-hook-template.test.js.map +1 -1
  43. package/dist/services/codex-pre-tool-host-context-template.d.ts +2 -0
  44. package/dist/services/codex-pre-tool-host-context-template.d.ts.map +1 -0
  45. package/dist/services/codex-pre-tool-host-context-template.js +139 -0
  46. package/dist/services/codex-pre-tool-host-context-template.js.map +1 -0
  47. package/dist/services/codex-pre-tool-support-template.d.ts.map +1 -1
  48. package/dist/services/codex-pre-tool-support-template.js +77 -100
  49. package/dist/services/codex-pre-tool-support-template.js.map +1 -1
  50. package/dist/services/codex-subagent-hook-template.d.ts +1 -1
  51. package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
  52. package/dist/services/codex-subagent-hook-template.js +59 -15
  53. package/dist/services/codex-subagent-hook-template.js.map +1 -1
  54. package/dist/services/codex-validation-runtime-template.d.ts.map +1 -1
  55. package/dist/services/codex-validation-runtime-template.js +37 -10
  56. package/dist/services/codex-validation-runtime-template.js.map +1 -1
  57. package/dist/services/runtime-template-test-support.d.ts +1 -0
  58. package/dist/services/runtime-template-test-support.d.ts.map +1 -1
  59. package/dist/services/runtime-template-test-support.js +23 -2
  60. package/dist/services/runtime-template-test-support.js.map +1 -1
  61. package/dist/services/runtime-template.test.js +7 -24
  62. package/dist/services/runtime-template.test.js.map +1 -1
  63. package/package.json +1 -1
  64. package/templates/.agents/README-ENG.md +49 -7
  65. package/templates/.agents/README.md +41 -10
  66. package/templates/.agents/config/agents/discovery.json +1 -1
  67. package/templates/.agents/dashboard/.zaip-managed.json +125 -73
  68. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-BW7Vy1cp.js → CostCalculationGuidePage-w6pjt4OM.js} +1 -1
  69. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-BN8KQPrr.js → DashboardHelpDialog-DhlntQcR.js} +117 -17
  70. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Prcs-ydy.js → PricingSettingsDialog-BHoZT2an.js} +1 -1
  71. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-HqGUImIG.js → ProjectSettingsPage-IcncIZ9Y.js} +1 -1
  72. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CNpmQMO4.js → TraceabilityWorkspace-C6rORjpr.js} +1 -1
  73. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-uPnOBShc.js +68 -0
  74. package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-CF7B1Hjn.js → dashboard-localization-DWXjEIgc.js} +49 -7
  75. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DOhy6D2-.js → icons-vendor-Hc9fp_gs.js} +1 -1
  76. package/templates/.agents/dashboard/apps/frontend/dist/assets/{index-DX65Ufuu.js → index-BqeOEZW5.js} +2 -2
  77. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CkPC-yMz.js → react-vendor-C9ta_O10.js} +1 -1
  78. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-BEdGoIDm.js → traceability-IcefoTOv.js} +1 -1
  79. package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
  80. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  81. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  82. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +10 -3
  83. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +5 -1
  84. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostGroupsTable.period-values.test.tsx +2 -2
  85. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables-keeps-legacy-codex-identity-fields-blank.test.ts +5 -5
  86. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +36 -43
  87. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-loaders.ts +1 -0
  88. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-formatters.ts +6 -4
  89. package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.ts +2 -2
  90. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.ts +1 -1
  91. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +11 -25
  92. package/templates/.agents/dashboard/modules/cost-monitor/src/server/cost-summary-defaults.ts +26 -0
  93. package/templates/.agents/dashboard/modules/operations/package.json +1 -1
  94. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +11 -16
  95. package/templates/.agents/dashboard/modules/operations/src/planning-contracts.ts +14 -0
  96. package/templates/.agents/dashboard/modules/operations/src/react/DecisionTable.tsx +65 -0
  97. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +5 -71
  98. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +10 -36
  99. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +7 -2
  100. package/templates/.agents/dashboard/modules/operations/src/react/use-operations-filter-sync.ts +66 -0
  101. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  102. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  103. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  104. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  105. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +229 -222
  106. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +167 -167
  107. package/templates/.agents/dashboard/packages/local-runtime/package.json +3 -3
  108. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +229 -222
  109. package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.test.ts +38 -0
  110. package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.ts +35 -0
  111. package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.test.ts +69 -6
  112. package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.ts +62 -15
  113. package/templates/.agents/dashboard/packages/local-runtime/src/manifest-runs.ts +8 -2
  114. package/templates/.agents/dashboard/packages/local-runtime/src/server-capture-projection-order.test.ts +118 -0
  115. package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.test.ts +90 -0
  116. package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.ts +86 -0
  117. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-routes.ts +8 -1
  118. package/templates/.agents/dashboard/packages/local-runtime/src/server-projection-loaders.ts +13 -0
  119. package/templates/.agents/dashboard/packages/local-runtime/src/server-projections.ts +30 -74
  120. package/templates/.agents/dashboard/packages/local-runtime/src/server-route-handlers.ts +42 -0
  121. package/templates/.agents/dashboard/packages/local-runtime/src/server-routes.test.ts +60 -0
  122. package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +12 -2
  123. package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-command.test.ts +29 -4
  124. package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-refresh.ts +7 -0
  125. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +51 -54
  126. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +37 -48
  127. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-filter-matrix.test.ts +5 -0
  128. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +49 -0
  129. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +44 -1
  130. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-cached-cost-page.ts +11 -10
  131. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-summary.ts +1 -1
  132. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-work-item-index.test.ts +18 -1
  133. package/templates/.agents/dashboard/packages/local-runtime/vitest.config.ts +12 -0
  134. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  135. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  136. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +13 -6
  137. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +1 -1
  138. package/templates/.agents/design/.zaip-managed.json +3 -3
  139. package/templates/.agents/design/apps/catalog/package.json +1 -1
  140. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  141. package/templates/.agents/requirements/autonomous-assurance.md +39 -3
  142. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
  143. package/templates/.agents/requirements/comparative-audit.md +1 -1
  144. package/templates/.agents/requirements/context-engine.md +3 -3
  145. package/templates/.agents/requirements/cost-observability.md +1 -1
  146. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  147. package/templates/.agents/requirements/data-export.md +1 -1
  148. package/templates/.agents/requirements/design-system.md +1 -1
  149. package/templates/.agents/requirements/execution-engine.md +11 -2
  150. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  151. package/templates/.agents/requirements/model-orchestration.md +1 -1
  152. package/templates/.agents/requirements/public-distribution.md +1 -1
  153. package/templates/.agents/requirements/software-architecture.md +1 -1
  154. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  155. package/templates/.agents/requirements/work-lifecycle.md +2 -1
  156. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  157. package/templates/.agents/rules/cloud-hostinger-autonomy.md +1 -1
  158. package/templates/.agents/rules/production-readiness.md +24 -0
  159. package/templates/.agents/rules/run-lifecycle-closure.md +2 -1
  160. package/templates/.agents/rules/security.md +2 -2
  161. package/templates/.agents/rules/workflow.md +4 -3
  162. package/templates/.agents/runtime/VERSION +1 -1
  163. package/templates/.agents/runtime/bin/zaip.mjs +1185 -610
  164. package/templates/.agents/runtime/checksums.sha256 +6 -6
  165. package/templates/.agents/runtime/manifest.json +1 -1
  166. package/templates/.agents/runtime/pnpm-lock.sha256 +1 -1
  167. package/templates/.agents/runtime/provenance.json +5 -5
  168. package/templates/.agents/runtime/sbom.cdx.json +71 -71
  169. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +4 -2
  170. package/templates/.agents/skills/zaip-skill-execution/references/controlled-release-recovery.md +44 -0
  171. package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +1 -1
  172. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +7 -1
  173. package/templates/.agents/skills/zaip-skill-github-deploy/references/release-gate-matrix.md +15 -20
  174. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.mjs +38 -2
  175. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.test.mjs +62 -4
  176. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +8 -6
  177. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/guarded-commits.mjs +5 -3
  178. package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +7 -0
  179. package/templates/.agents/skills/zaip-skill-maintenance-enter/SKILL.md +23 -0
  180. package/templates/.agents/skills/zaip-skill-maintenance-enter/agents/openai.yaml +4 -0
  181. package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.mjs +213 -0
  182. package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.test.mjs +207 -0
  183. package/templates/.agents/skills/zaip-skill-maintenance-exit/SKILL.md +21 -0
  184. package/templates/.agents/skills/zaip-skill-maintenance-exit/agents/openai.yaml +4 -0
  185. package/templates/.agents/skills/zaip-skill-prd/SKILL.md +6 -0
  186. package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +26 -0
  187. package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +46 -11
  188. package/templates/.agents/skills/zaip-skill-spec/SKILL.md +9 -0
  189. package/templates/.agents/skills/zaip-skill-testing-strategy/SKILL.md +15 -0
  190. package/templates/.agents/zaip-aios/VERSION +1 -1
  191. package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +22 -0
  192. package/templates/.agents/zaip-aios/guides/14-troubleshooting.md +1 -1
  193. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +52 -5
  194. package/templates/template-manifest.json +210 -126
  195. 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 qualifies locally and waits for exact main CI", async () => {
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: process.cwd(),
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
- await guardedCommits(value, {
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-"));
@@ -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 readFile(resolve(root, "rules", "workflow.md"), "utf8");
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(workflow, /current-work/);
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
- const candidateGate = critical
138
- ? await import(pathToFileURL(resolve(root, "scripts/remote-candidate-gate.mjs")).href)
139
- : null;
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,4 @@
1
+ interface:
2
+ display_name: "Entrar em manutenção"
3
+ short_description: "Desativa a governança e mantém a telemetria"
4
+ default_prompt: "Use $zaip-skill-maintenance-enter para entrar em manutenção com telemetria ativa."
@@ -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.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Sair da manutenção"
3
+ short_description: "Reativa o ZAIP preservando a telemetria"
4
+ default_prompt: "Use $zaip-skill-maintenance-exit para sair da manutenção e verificar o ZAIP ativo."
@@ -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