@polymorphism-tech/morph-spec 8.44.0 → 8.46.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 (199) hide show
  1. package/CLAUDE.md +11 -9
  2. package/README.md +10 -10
  3. package/bin/morph-spec.js +11 -13
  4. package/bin/validate.js +379 -379
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +2 -4
  7. package/docs/QUICKSTART.md +6 -6
  8. package/framework/CLAUDE.md +6 -8
  9. package/framework/MORPH.md +39 -45
  10. package/framework/agents.json +8 -9
  11. package/framework/commands/morph-apply.md +1 -3
  12. package/framework/commands/morph-hotfix.md +1 -1
  13. package/framework/commands/morph-proposal.md +2 -2
  14. package/framework/docs/CLI.md +15 -14
  15. package/framework/evals/review-checklist.md +90 -0
  16. package/framework/evals/trace-schema.md +2 -12
  17. package/framework/hooks/README.md +2 -3
  18. package/framework/hooks/claude-code/core/heavy-gate.js +1 -1
  19. package/framework/hooks/claude-code/core/protect-spec-files.js +2 -2
  20. package/framework/hooks/claude-code/core/telemetry-log.js +2 -2
  21. package/framework/hooks/claude-code/core/trace-autogen.js +1 -22
  22. package/framework/hooks/claude-code/core/validate-completion.js +3 -56
  23. package/framework/hooks/claude-code/statusline.sh +11 -11
  24. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  25. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  26. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  27. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  28. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  29. package/framework/hooks/shared/build-check.js +1 -1
  30. package/framework/hooks/shared/comment-scanner.js +1 -1
  31. package/framework/hooks/shared/phase-utils.js +0 -5
  32. package/framework/hooks/shared/stdin-reader.js +26 -26
  33. package/framework/phases.json +4 -6
  34. package/framework/rubrics/backend.json +1 -1
  35. package/framework/rubrics/default.json +1 -1
  36. package/framework/rubrics/frontend.json +1 -1
  37. package/framework/rules/morph-workflow.md +6 -6
  38. package/framework/schemas/config.schema.json +4 -0
  39. package/framework/schemas/feature.schema.json +5 -29
  40. package/framework/schemas/tasks.schema.json +6 -2
  41. package/framework/skills/README.md +1 -2
  42. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  43. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  44. package/framework/skills/core/morph-apply/SKILL.md +145 -246
  45. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  46. package/framework/skills/core/morph-hotfix/SKILL.md +5 -3
  47. package/framework/skills/core/morph-implement/SKILL.md +101 -201
  48. package/framework/skills/core/morph-machine/SKILL.md +3 -3
  49. package/framework/skills/core/morph-media/SKILL.md +1 -1
  50. package/framework/skills/core/morph-plan/SKILL.md +171 -287
  51. package/framework/skills/core/morph-proposal/SKILL.md +4 -5
  52. package/framework/skills/core/morph-review/SKILL.md +122 -430
  53. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  54. package/framework/skills/core/morph-standards/SKILL.md +2 -2
  55. package/framework/skills/core/morph-uiux/SKILL.md +2 -3
  56. package/framework/standards/STANDARDS.json +10 -10
  57. package/framework/standards/ai-agents/agent-archetypes.md +1 -1
  58. package/framework/standards/ai-agents/agent-session.md +1 -1
  59. package/framework/standards/ai-agents/agent-spec.md +1 -1
  60. package/framework/standards/ai-agents/batching.md +1 -1
  61. package/framework/standards/ai-agents/context-providers.md +1 -1
  62. package/framework/standards/ai-agents/conversational-agent-with-phases.md +1 -1
  63. package/framework/standards/ai-agents/cost-and-budget.md +1 -1
  64. package/framework/standards/ai-agents/durable-workflows-hitl.md +1 -1
  65. package/framework/standards/ai-agents/guardrails.md +1 -1
  66. package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
  67. package/framework/standards/ai-agents/mcp-server.md +1 -1
  68. package/framework/standards/ai-agents/mcp-tools.md +1 -1
  69. package/framework/standards/ai-agents/media-pipeline.md +1 -1
  70. package/framework/standards/ai-agents/media-video.md +1 -1
  71. package/framework/standards/ai-agents/middleware-patterns.md +1 -1
  72. package/framework/standards/ai-agents/modalities-image-gen.md +1 -1
  73. package/framework/standards/ai-agents/modalities-speech-to-text.md +1 -1
  74. package/framework/standards/ai-agents/modalities-text-to-speech.md +1 -1
  75. package/framework/standards/ai-agents/modalities-vision.md +1 -1
  76. package/framework/standards/ai-agents/multi-agent-patterns.md +1 -1
  77. package/framework/standards/ai-agents/observability-patterns.md +3 -3
  78. package/framework/standards/ai-agents/production.md +2 -2
  79. package/framework/standards/ai-agents/prompt-sources.md +1 -1
  80. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  81. package/framework/standards/ai-agents/rag-custom-pgvector.md +1 -1
  82. package/framework/standards/ai-agents/service-tier-flex.md +1 -1
  83. package/framework/standards/ai-agents/setup.md +1 -1
  84. package/framework/standards/ai-agents/structured-output.md +1 -1
  85. package/framework/standards/ai-agents/sweet-spot.md +1 -1
  86. package/framework/standards/ai-agents/testing-ai.md +1 -1
  87. package/framework/standards/ai-agents/tool-adapters.md +1 -1
  88. package/framework/standards/ai-agents/vector-data-extensions.md +1 -1
  89. package/framework/standards/ai-agents/workflows.md +1 -1
  90. package/framework/templates/CLAUDE.md.core.template +1 -1
  91. package/framework/templates/IDE-SNIPPETS.md +262 -262
  92. package/framework/templates/README.md +1 -1
  93. package/framework/templates/REGISTRY.json +0 -81
  94. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  95. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  96. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  97. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  98. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  99. package/framework/templates/docs/spec.md +19 -205
  100. package/framework/templates/docs/ui-components.md +52 -52
  101. package/framework/templates/docs/ui-design-system.md +76 -76
  102. package/framework/templates/docs/ui-flows.md +167 -167
  103. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  104. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  105. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  106. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  107. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  108. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  109. package/framework/templates/infrastructure/github/README.md +56 -56
  110. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  111. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  112. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  113. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  114. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  115. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  116. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  117. package/framework/templates/state.template.json +17 -17
  118. package/framework/templates/ui/design-system.css +226 -226
  119. package/package.json +2 -2
  120. package/src/commands/e2e/e2e.js +1 -1
  121. package/src/commands/project/heavy.js +1 -1
  122. package/src/commands/project/status.js +3 -3
  123. package/src/commands/state/approve.js +7 -49
  124. package/src/commands/state/gate-check.js +16 -106
  125. package/src/commands/state/gate-decision.js +1 -1
  126. package/src/commands/state/report-check.js +42 -17
  127. package/src/commands/validation/mutate.js +1 -1
  128. package/src/commands/validation/scout-check.js +28 -0
  129. package/src/commands/validation/validate-feature.js +5 -32
  130. package/src/commands/validation/verify.js +98 -48
  131. package/src/core/paths/output-schema.js +0 -2
  132. package/src/core/state/state-manager.js +1 -2
  133. package/src/core/templates/template-renderer.js +477 -477
  134. package/src/dashboard/app/lib/format.js +5 -0
  135. package/src/dashboard/app/main.jsx +13 -13
  136. package/src/dashboard/app/pages/FeatureDetail.jsx +16 -22
  137. package/src/dashboard/app/pages/Features.jsx +7 -7
  138. package/src/dashboard/app/pages/PhasePipeline.jsx +1 -1
  139. package/src/dashboard/app/pages/Rubrics.jsx +7 -11
  140. package/src/dashboard/scanner/docs-scanner.js +1 -2
  141. package/src/dashboard/scanner/features-scanner.js +3 -4
  142. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  143. package/src/dashboard/vite.config.js +24 -24
  144. package/src/lib/agents/output-collector.js +0 -1
  145. package/src/lib/ci/ci-verify.js +118 -0
  146. package/src/lib/ci/github-ci.js +192 -0
  147. package/src/lib/ci/workflow-scaffold.js +122 -0
  148. package/src/lib/detectors/design-system-detector.js +188 -188
  149. package/src/lib/evaluator-report.js +52 -334
  150. package/src/lib/generators/design-system-generator.js +508 -508
  151. package/src/lib/generators/handoff-generator.js +4 -5
  152. package/src/lib/installers/memory-installer.js +2 -2
  153. package/src/lib/mutation/manifest.js +8 -2
  154. package/src/lib/mutation/report.js +5 -3
  155. package/src/lib/mutation/runner.js +9 -7
  156. package/src/lib/risk-detector.js +60 -122
  157. package/src/lib/scout/scout-check.js +293 -0
  158. package/src/lib/tasks/task-classifier.js +30 -30
  159. package/src/lib/tasks/task-parser.js +209 -209
  160. package/src/lib/tasks/tasks-json-parser.js +7 -5
  161. package/src/lib/tasks/test-nodes.js +1 -1
  162. package/src/lib/tasks/test-runner.js +1 -1
  163. package/src/lib/telemetry/verify-event.js +3 -2
  164. package/src/lib/validators/css/css-validator.js +340 -340
  165. package/src/lib/validators/reuse/reuse-validator.js +6 -8
  166. package/src/lib/validators/verify-runner.js +1 -1
  167. package/src/scripts/setup-infra.js +1 -1
  168. package/src/utils/agent-id-mapper.js +11 -11
  169. package/src/utils/agents-installer.js +5 -9
  170. package/src/utils/claude-settings-manager.js +300 -300
  171. package/src/utils/hooks-installer.js +3 -10
  172. package/src/utils/skills-installer.js +1 -1
  173. package/framework/evals/proof-rubrics/README.md +0 -76
  174. package/framework/evals/proof-rubrics/eval-de-modelo.md +0 -80
  175. package/framework/evals/proof-rubrics/fiacao.md +0 -54
  176. package/framework/evals/proof-rubrics/guard-cego.md +0 -52
  177. package/framework/evals/proof-rubrics/media-claims.md +0 -109
  178. package/framework/evals/proof-rubrics/mutation-proof.md +0 -107
  179. package/framework/evals/proof-rubrics/observabilidade-genai.md +0 -119
  180. package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +0 -48
  181. package/framework/evals/rubrics/README.md +0 -45
  182. package/framework/evals/rubrics/architecture.md +0 -103
  183. package/framework/evals/rubrics/code-quality.md +0 -98
  184. package/framework/evals/rubrics/condensed.md +0 -57
  185. package/framework/evals/rubrics/contracts.md +0 -67
  186. package/framework/evals/rubrics/test-coverage.md +0 -99
  187. package/framework/evals/scenarios/dotnet-crud.yaml +0 -42
  188. package/framework/evals/scenarios/nextjs-auth.yaml +0 -38
  189. package/framework/evals/suites/standard.yaml +0 -5
  190. package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
  191. package/framework/hooks/claude-code/core/post-edit-typecheck.js +0 -127
  192. package/framework/skills/core/morph-eval/SKILL.md +0 -64
  193. package/framework/templates/.idea/morph-templates.xml +0 -92
  194. package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
  195. package/framework/templates/docs/clarifications.md +0 -253
  196. package/framework/templates/docs/onboarding.md +0 -122
  197. package/framework/templates/docs/schema-analysis.md +0 -119
  198. package/framework/templates/docs/user-stories.md +0 -34
  199. package/src/commands/state/score.js +0 -147
@@ -4,7 +4,7 @@ import { getFeature, updateFeature } from '../../core/state/state-manager.js';
4
4
  import { runVerify, readTask, VerifyUsageError } from '../../lib/validators/verify-runner.js';
5
5
  import { resolvePrimaryRoot } from '../project/worktree.js';
6
6
  import { resolveFeatureTree } from '../../lib/worktree/feature-truth.js';
7
- import { canonicalPath } from '../../lib/tasks/test-runner.js';
7
+ import { canonicalPath, readMorphConfig } from '../../lib/tasks/test-runner.js';
8
8
  import { getHeadSha } from '../../lib/git/default-branch.js';
9
9
  import { EVAL_RUNNER } from '../../lib/tasks/eval-node.js';
10
10
  import { buildVerifyEvent, recordVerifyEvent, findRepeatedVerify } from '../../lib/telemetry/verify-event.js';
@@ -12,6 +12,7 @@ import { treeFingerprint } from '../../lib/git/tree-fingerprint.js';
12
12
  import { HeavyBusyError } from '../../lib/heavy/gate.js';
13
13
  import { afterHeavyRun } from '../../lib/heavy/cleanup.js';
14
14
  import { readHeavyConfig } from '../../../framework/hooks/shared/heavy-config.js';
15
+ import { runCiVerify, CiUsageError } from '../../lib/ci/ci-verify.js';
15
16
 
16
17
  /** ✓ pass · ✗ fail · – skip */
17
18
  function statusIcon(status) {
@@ -190,6 +191,54 @@ function evalsStatusOf(result) {
190
191
  */
191
192
  const PARTIAL_RUN_FLAGS = [['filter', 'filtered'], ['skipTests', 'skip-tests'], ['skipBuild', 'skip-build']];
192
193
 
194
+ const CI_EXCLUSIVE_FLAGS = [
195
+ ['filter', '--filter'], ['skipTests', '--skip-tests'], ['skipBuild', '--skip-build'], ['skipE2e', '--skip-e2e'],
196
+ ['freshE2e', '--fresh-e2e'], ['linux', '--linux'], ['recordBaseline', '--record-baseline'], ['refreshCache', '--refresh-cache'],
197
+ ['noPersist', '--no-persist'],
198
+ ];
199
+
200
+ async function runLocalVerify(featureName, taskId, options) {
201
+ try {
202
+ return await runVerify(process.cwd(), featureName, taskId || null, {
203
+ rubric: options.rubric,
204
+ timeout: options.timeout,
205
+ skipTests: options.skipTests,
206
+ skipBuild: options.skipBuild,
207
+ skipE2e: options.skipE2e,
208
+ freshE2e: options.freshE2e,
209
+ filter: options.filter,
210
+ recordBaseline: options.recordBaseline,
211
+ refreshCache: options.refreshCache,
212
+ linux: options.linux,
213
+ linuxImage: options.linuxImage,
214
+ // Task scope: switch the recorte off (#87). At feature scope nothing is
215
+ // ever narrowed, so the flag is a documented no-op there, not an error.
216
+ full: options.full,
217
+ });
218
+ } catch (err) {
219
+ // A wrong invocation is exit 2, like an unknown feature or task — never a
220
+ // verdict. `--filter` that selects nothing must not print green (or red):
221
+ // there is no verification to report, only a command to fix.
222
+ if (err instanceof VerifyUsageError) {
223
+ console.error(chalk.red(`\n❌ ${err.message}\n`));
224
+ process.exit(2);
225
+ }
226
+ // Ocupado não é veredito: nada foi verificado, então não há stamp nem
227
+ // evento, e o exit 3 nunca é "vermelho".
228
+ if (err instanceof HeavyBusyError) {
229
+ if (options.json) {
230
+ const holders = err.holders.map(({ pid, label, feature, node, since }) => ({ pid, label, feature, node, since }));
231
+ console.log(JSON.stringify({
232
+ status: 'busy', persisted: false, busy: { reason: err.reason, holders, freeGb: err.freeGb, waitedMs: err.waitedMs },
233
+ }, null, 2));
234
+ }
235
+ console.error(chalk.yellow(`\n⏳ verify não rodou: ${err.message}. Não é vermelho — a fila já esperou o prazo dela: não repita em laço — siga sem esta rodada e avise o usuário (\`morph-spec heavy status\` mostra quem segura os slots).\n`));
236
+ process.exit(3);
237
+ }
238
+ throw err;
239
+ }
240
+ }
241
+
193
242
  /**
194
243
  * `morph-spec verify <feature> [task]` — the deterministic code-before-judge
195
244
  * gate: build + tests + validators, BEFORE the LLM eval. See verify-runner.js
@@ -203,8 +252,7 @@ const PARTIAL_RUN_FLAGS = [['filter', 'filtered'], ['skipTests', 'skip-tests'],
203
252
  * paths to it — never a verdict, because a verdict about nothing is worse
204
253
  * than a refusal:
205
254
  * - unknown feature, or unknown task id (the task's `group` decides the
206
- * rubric, so it is a hard usage error here, unlike the advisory warning
207
- * `morph-spec score` gives for the same situation);
255
+ * rubric, so it is a hard usage error here);
208
256
  * - `--filter` that matches no declared node (a filtered-to-empty run is
209
257
  * green and proves nothing);
210
258
  * - `--record-baseline` with nowhere to write (no `config.json`), or with a
@@ -224,6 +272,14 @@ export async function verifyCommand(featureName, taskId, options = {}) {
224
272
  process.exit(2);
225
273
  }
226
274
 
275
+ if (options.ci) {
276
+ const conflict = taskId ? `a task ${taskId} (o loop por task é local)` : CI_EXCLUSIVE_FLAGS.find(([flag]) => options[flag])?.[1];
277
+ if (conflict) {
278
+ console.error(chalk.red(`\n❌ --ci roda a suíte inteira da feature no CI; não combina com ${conflict}\n`));
279
+ process.exit(2);
280
+ }
281
+ }
282
+
227
283
  // The runner's own reader (one reader of tasks.json for verify, #87): null
228
284
  // means tasks.json or the task is missing, and both are this usage error.
229
285
  if (taskId) {
@@ -233,51 +289,36 @@ export async function verifyCommand(featureName, taskId, options = {}) {
233
289
  }
234
290
  }
235
291
 
236
- // Taken before the run: it is the tree the run is about to read.
237
- const fingerprint = treeFingerprint(process.cwd());
238
-
239
- let result;
240
- try {
241
- result = await runVerify(process.cwd(), featureName, taskId || null, {
242
- rubric: options.rubric,
243
- timeout: options.timeout,
244
- skipTests: options.skipTests,
245
- skipBuild: options.skipBuild,
246
- skipE2e: options.skipE2e,
247
- freshE2e: options.freshE2e,
248
- filter: options.filter,
249
- recordBaseline: options.recordBaseline,
250
- refreshCache: options.refreshCache,
251
- linux: options.linux,
252
- linuxImage: options.linuxImage,
253
- // Task scope: switch the recorte off (#87). At feature scope nothing is
254
- // ever narrowed, so the flag is a documented no-op there, not an error.
255
- full: options.full,
256
- });
257
- } catch (err) {
258
- // A wrong invocation is exit 2, like an unknown feature or task — never a
259
- // verdict. `--filter` that selects nothing must not print green (or red):
260
- // there is no verification to report, only a command to fix.
261
- if (err instanceof VerifyUsageError) {
262
- console.error(chalk.red(`\n❌ ${err.message}\n`));
263
- process.exit(2);
264
- }
265
- // Ocupado não é veredito: nada foi verificado, então não há stamp nem
266
- // evento, e o exit 3 diz "tente de novo", nunca "vermelho".
267
- if (err instanceof HeavyBusyError) {
268
- if (options.json) {
269
- const holders = err.holders.map(({ pid, label, feature, node, since }) => ({ pid, label, feature, node, since }));
270
- console.log(JSON.stringify({
271
- status: 'busy', persisted: false, busy: { reason: err.reason, holders, freeGb: err.freeGb, waitedMs: err.waitedMs },
272
- }, null, 2));
292
+ let ci = null;
293
+ if (options.ci) {
294
+ try {
295
+ const outcome = await runCiVerify(process.cwd(), featureName, readMorphConfig(process.cwd()), {
296
+ log: (line) => console.error(chalk.gray(` ${line}`)),
297
+ });
298
+ if (outcome.mode === 'ci') {
299
+ ci = outcome;
300
+ } else if (outcome.mode === 'pending') {
301
+ if (options.json) console.log(JSON.stringify({ status: 'pending', persisted: false, reason: outcome.reason }, null, 2));
302
+ console.error(chalk.yellow(`\n⏳ ${outcome.reason}. Não é vermelho e não há stamp: não repita em laço — siga e deixe o gate-check decidir.\n`));
303
+ process.exit(3);
304
+ } else {
305
+ console.error(chalk.yellow(`\n↪ CI indisponível (${outcome.reason}) — verify local.\n`));
273
306
  }
274
- console.error(chalk.yellow(`\n⏳ verify não rodou: ${err.message}. Não é vermelho — tente de novo quando liberar (\`morph-spec heavy status\`).\n`));
275
- process.exit(3);
307
+ } catch (err) {
308
+ if (err instanceof CiUsageError) {
309
+ console.error(chalk.red(`\n❌ ${err.message}\n`));
310
+ process.exit(2);
311
+ }
312
+ throw err;
276
313
  }
277
- throw err;
278
314
  }
279
315
 
280
- const cleanup = afterHeavyRun({
316
+ // Taken before the run: it is the tree the run is about to read.
317
+ const fingerprint = treeFingerprint(process.cwd());
318
+
319
+ const result = ci ? ci.result : await runLocalVerify(featureName, taskId, options);
320
+
321
+ const cleanup = ci ? { status: 'skipped', reason: 'ci' } : afterHeavyRun({
281
322
  projectPath: process.cwd(),
282
323
  config: readHeavyConfig(process.cwd()),
283
324
  scope: taskId ? 'task' : 'feature',
@@ -330,9 +371,10 @@ export async function verifyCommand(featureName, taskId, options = {}) {
330
371
  status: result.status,
331
372
  nodes: nodeStatuses(result),
332
373
  e2eReason: result.nodes.e2e.status === 'skip' ? (result.nodes.e2e.reason ?? null) : null,
333
- sha: getHeadSha(process.cwd()),
374
+ sha: ci ? ci.run.headSha : getHeadSha(process.cwd()),
334
375
  ts: now,
335
376
  scope: taskId ? `task:${taskId}` : 'feature',
377
+ ...(ci ? { source: 'ci', ciRun: ci.run.url } : {}),
336
378
  };
337
379
  await updateFeature(featureName, 'verification', stamp);
338
380
  }
@@ -342,9 +384,9 @@ export async function verifyCommand(featureName, taskId, options = {}) {
342
384
  let repeatOf = null;
343
385
  try {
344
386
  const cwd = process.cwd();
345
- if (!taskId && persist) repeatOf = findRepeatedVerify(cwd, { feature: featureName, task: null, scope: result.scope, fingerprint });
387
+ if (!taskId && persist && !ci) repeatOf = findRepeatedVerify(cwd, { feature: featureName, task: null, scope: result.scope, fingerprint });
346
388
  const tree = resolve(cwd).replace(/\\/g, '/') === resolvePrimaryRoot(cwd) ? 'root' : 'worktree';
347
- recordVerifyEvent(cwd, buildVerifyEvent(result, { sha: getHeadSha(cwd), tree, persisted: persist, reason, fingerprint }));
389
+ recordVerifyEvent(cwd, buildVerifyEvent(result, { sha: ci ? ci.run.headSha : getHeadSha(cwd), tree, persisted: persist, reason, fingerprint, source: ci ? 'ci' : 'local' }));
348
390
  } catch { /* telemetry never changes the verdict */ }
349
391
 
350
392
  // The refusal always goes to stderr, so it survives --json (whose stdout has
@@ -356,10 +398,18 @@ export async function verifyCommand(featureName, taskId, options = {}) {
356
398
  }
357
399
 
358
400
  if (options.json) {
359
- const output = { ...result, persisted: persist, ...(persist ? {} : { reason }), repeatOf, cleanup };
401
+ const output = {
402
+ ...result, persisted: persist, ...(persist ? {} : { reason }), repeatOf, cleanup,
403
+ ...(ci ? { source: 'ci', ciRun: { id: ci.run.databaseId, url: ci.run.url, sha: ci.run.headSha }, workflowCreated: ci.created } : {}),
404
+ };
360
405
  console.log(JSON.stringify(output, null, 2));
361
406
  } else {
362
407
  console.log(formatVerifyResult(result));
408
+ if (ci) {
409
+ console.log(chalk.cyan(`fonte: CI ${ci.run.url}`));
410
+ if (result.status === 'fail') console.log(chalk.gray(` log das falhas: gh run view ${ci.run.databaseId} --log-failed`));
411
+ console.log('');
412
+ }
363
413
  if (cleanup.status !== 'skipped') {
364
414
  const mark = cleanup.status === 'ran' ? chalk.green('✓') : chalk.yellow('⚠');
365
415
  console.log(chalk.gray(`${mark} dotnet build-server shutdown: ${cleanup.status}${cleanup.reason ? ` (${cleanup.reason})` : ''}\n`));
@@ -46,7 +46,6 @@ export const OUTPUT_SCHEMA = {
46
46
 
47
47
  // 2-plan/
48
48
  spec: { filename: 'spec.md', phaseDir: '2-plan', phase: 'plan', protected: true, approvalGate: 'plan' },
49
- mandate: { filename: 'mandate.md', phaseDir: '2-plan', phase: 'plan', protected: true, approvalGate: 'plan' },
50
49
  tasks: { filename: 'tasks.json', phaseDir: '2-plan', phase: 'plan', protected: true, approvalGate: 'plan', mutableFields: ['status', 'outputs', 'notes'] },
51
50
  decisions: { filename: 'decisions.md', phaseDir: '2-plan', phase: 'plan', protected: false },
52
51
  // Veredito REUSAR/ESTENDER/CRIAR por conceito que a feature introduz, com a
@@ -55,7 +54,6 @@ export const OUTPUT_SCHEMA = {
55
54
  // como decisions.md: é registro vivo — o implementador que descobre um alvo
56
55
  // melhor no meio da task precisa poder anotar, e congelá-lo transformaria a
57
56
  // correção num bloqueio de gate.
58
- reuseMap: { filename: 'reuse-map.md', phaseDir: '2-plan', phase: 'plan', protected: false },
59
57
  // N files, one per system use case — the behaviour layer the plan distils
60
58
  // BEFORE spec.md/tasks.json. Deliberately NOT protected: editing a use case
61
59
  // after Gate 2 is the supported way to change behaviour and re-run implement;
@@ -581,7 +581,6 @@ export async function createFeature(featureName, options = {}) {
581
581
  review: { approved: false, timestamp: null, approvedBy: null }
582
582
  },
583
583
  gateDecisions: {},
584
- taskScores: {},
585
584
  tasks: {
586
585
  total: 0,
587
586
  completed: 0,
@@ -782,7 +781,7 @@ function findClosestMatch(inputType, validTypes) {
782
781
  /**
783
782
  * Mark output as created
784
783
  * @param {string} featureName - Feature name
785
- * @param {string} outputType - Output type (proposal, spec, mandate, etc.)
784
+ * @param {string} outputType - Output type (proposal, spec, tasks, etc.)
786
785
  */
787
786
  export async function markOutput(featureName, outputType) {
788
787
  await ensureFeature(featureName);