@polymorphism-tech/morph-spec 9.0.0 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/CLAUDE.md +8 -9
  2. package/README.md +38 -36
  3. package/bin/morph-spec.js +11 -63
  4. package/bin/validate.js +1 -39
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +2 -1
  9. package/framework/MORPH.md +3 -3
  10. package/framework/agents.json +19 -11
  11. package/framework/commands/morph-apply.md +1 -1
  12. package/framework/commands/morph-status.md +1 -1
  13. package/framework/docs/CLI.md +22 -31
  14. package/framework/evals/review-checklist.md +12 -2
  15. package/framework/hooks/README.md +8 -44
  16. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  17. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  18. package/framework/hooks/claude-code/core/chain.js +4 -65
  19. package/framework/hooks/claude-code/core/comment-guard.js +2 -16
  20. package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
  21. package/framework/hooks/claude-code/core/gate-guard.js +7 -67
  22. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  23. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  24. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  25. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  26. package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
  27. package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
  28. package/framework/hooks/claude-code/core/state-sync.js +12 -68
  29. package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
  30. package/framework/hooks/claude-code/core/validate-completion.js +11 -57
  31. package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
  32. package/framework/hooks/claude-code/core/worktree-context.js +10 -110
  33. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  34. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  35. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  36. package/framework/hooks/claude-code/statusline.sh +11 -11
  37. package/framework/hooks/shared/activity-logger.js +9 -89
  38. package/framework/hooks/shared/agent-reap.js +2 -17
  39. package/framework/hooks/shared/build-check.js +6 -75
  40. package/framework/hooks/shared/claim-registry.js +9 -119
  41. package/framework/hooks/shared/command-tokens.js +6 -51
  42. package/framework/hooks/shared/comment-rules.js +5 -20
  43. package/framework/hooks/shared/comment-scanner.js +6 -25
  44. package/framework/hooks/shared/comment-stripper.js +11 -85
  45. package/framework/hooks/shared/e2e-check.js +0 -0
  46. package/framework/hooks/shared/errata.js +2 -15
  47. package/framework/hooks/shared/feature-locator.js +11 -95
  48. package/framework/hooks/shared/frontend-locations.js +1 -3
  49. package/framework/hooks/shared/gate-question.js +1 -7
  50. package/framework/hooks/shared/git-exclude.js +3 -20
  51. package/framework/hooks/shared/git-paths.js +8 -104
  52. package/framework/hooks/shared/graph-freshness.js +22 -166
  53. package/framework/hooks/shared/graph-refresh.js +21 -149
  54. package/framework/hooks/shared/heavy-command.js +4 -12
  55. package/framework/hooks/shared/hook-response.js +8 -83
  56. package/framework/hooks/shared/morph-cli.js +8 -52
  57. package/framework/hooks/shared/payload-utils.js +10 -132
  58. package/framework/hooks/shared/phase-utils.js +1 -42
  59. package/framework/hooks/shared/redact-secrets.js +4 -15
  60. package/framework/hooks/shared/reparse-scan.js +6 -66
  61. package/framework/hooks/shared/slice-progress.js +4 -23
  62. package/framework/hooks/shared/source-files.js +4 -17
  63. package/framework/hooks/shared/state-reader.js +13 -183
  64. package/framework/hooks/shared/state-writer.js +8 -52
  65. package/framework/hooks/shared/stdin-reader.js +1 -11
  66. package/framework/hooks/shared/telemetry-logger.js +7 -67
  67. package/framework/hooks/shared/transcript-usage.js +7 -29
  68. package/framework/hooks/shared/worktree-root.js +7 -29
  69. package/framework/rules/csharp-standards.md +1 -1
  70. package/framework/rules/frontend-standards.md +1 -0
  71. package/framework/rules/hangfire-standards.md +1 -1
  72. package/framework/rules/neon-standards.md +1 -1
  73. package/framework/rules/nextjs-standards.md +1 -1
  74. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  75. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  76. package/framework/skills/core/morph-apply/SKILL.md +4 -4
  77. package/framework/skills/core/morph-hotfix/SKILL.md +3 -3
  78. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  79. package/framework/skills/core/morph-media/SKILL.md +1 -1
  80. package/framework/skills/core/morph-plan/SKILL.md +9 -7
  81. package/framework/skills/core/morph-review/SKILL.md +9 -6
  82. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  83. package/framework/skills/core/morph-uiux/SKILL.md +29 -12
  84. package/framework/standards/STANDARDS.json +36 -14
  85. package/framework/standards/STANDARDS.meta.json +16 -2
  86. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  87. package/framework/standards/ai-agents/agent-session.md +1 -1
  88. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  89. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  90. package/framework/standards/ai-agents/guardrails.md +1 -1
  91. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  92. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  93. package/framework/standards/ai-agents/production.md +1 -1
  94. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  95. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  96. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  97. package/framework/standards/ai-agents/setup.md +2 -2
  98. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  99. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  100. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  101. package/framework/standards/ai-agents/workflows.md +1 -1
  102. package/framework/standards/architecture/code-comments.md +1 -1
  103. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  104. package/framework/standards/backend/api/validation.md +1 -1
  105. package/framework/standards/backend/dotnet/testing.md +10 -4
  106. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  107. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  108. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  109. package/framework/standards/data/nosql/cache/redis.md +1 -1
  110. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  111. package/framework/standards/frontend/admin-app/forms.md +1 -1
  112. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  113. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  114. package/framework/standards/frontend/nextjs/testing.md +10 -6
  115. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  116. package/framework/templates/.idea/morph-templates.xml +92 -0
  117. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  118. package/framework/templates/CLAUDE.md.core.template +3 -3
  119. package/framework/templates/IDE-SNIPPETS.md +262 -262
  120. package/framework/templates/REGISTRY.json +9 -9
  121. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  122. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  123. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  124. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  125. package/framework/templates/docs/plan.md +3 -1
  126. package/framework/templates/docs/ui-mockups.md +4 -0
  127. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  128. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  129. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  130. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  131. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  132. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  133. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  134. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  135. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  136. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  137. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  138. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  139. package/framework/templates/state.template.json +17 -17
  140. package/framework/templates/ui/design-system.css +226 -226
  141. package/package.json +1 -1
  142. package/src/commands/context/context-refresh.js +0 -16
  143. package/src/commands/dashboard/dashboard.js +2 -8
  144. package/src/commands/e2e/e2e.js +17 -104
  145. package/src/commands/graph/affected.js +0 -14
  146. package/src/commands/graph/build.js +11 -50
  147. package/src/commands/graph/explain.js +0 -16
  148. package/src/commands/graph/status.js +1 -12
  149. package/src/commands/mcp/mcp-setup.js +4 -62
  150. package/src/commands/observability/cost.js +5 -57
  151. package/src/commands/observability/telemetry.js +1 -14
  152. package/src/commands/project/branch.js +4 -45
  153. package/src/commands/project/claim.js +4 -48
  154. package/src/commands/project/doctor.js +48 -302
  155. package/src/commands/project/env.js +1 -27
  156. package/src/commands/project/fleet.js +3 -32
  157. package/src/commands/project/init.js +4 -68
  158. package/src/commands/project/install-plugin-cmd.js +0 -1
  159. package/src/commands/project/standards.js +1 -62
  160. package/src/commands/project/status.js +0 -53
  161. package/src/commands/project/update.js +31 -239
  162. package/src/commands/project/worktree.js +108 -882
  163. package/src/commands/scaffold/scaffold.js +0 -25
  164. package/src/commands/state/advance.js +14 -85
  165. package/src/commands/state/approve.js +15 -97
  166. package/src/commands/state/archive.js +12 -64
  167. package/src/commands/state/auto-approval.js +2 -5
  168. package/src/commands/state/create.js +22 -109
  169. package/src/commands/state/delete.js +11 -63
  170. package/src/commands/state/finish.js +98 -475
  171. package/src/commands/state/gate-check.js +22 -132
  172. package/src/commands/state/gate-decision.js +3 -39
  173. package/src/commands/state/handoff.js +3 -30
  174. package/src/commands/state/list.js +14 -84
  175. package/src/commands/state/report-check.js +8 -58
  176. package/src/commands/state/retype.js +4 -26
  177. package/src/commands/validation/index.js +0 -3
  178. package/src/commands/validation/mutate.js +11 -11
  179. package/src/commands/validation/plan-check.js +4 -2
  180. package/src/commands/validation/tests-audit.js +7 -0
  181. package/src/commands/validation/validate-feature.js +10 -97
  182. package/src/commands/validation/verify.js +30 -113
  183. package/src/core/paths/output-schema.js +2 -101
  184. package/src/core/state/feature-identity.js +1 -25
  185. package/src/core/state/gate-order.js +0 -27
  186. package/src/core/state/lifecycle-audit.js +8 -100
  187. package/src/core/state/state-manager.js +22 -304
  188. package/src/core/templates/template-registry.js +2 -97
  189. package/src/core/templates/template-renderer.js +13 -93
  190. package/src/core/templates/template-seed.js +0 -1
  191. package/src/core/work-types.js +1 -36
  192. package/src/dashboard/app/App.jsx +1 -8
  193. package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
  194. package/src/dashboard/app/components/CommandPalette.jsx +0 -7
  195. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  196. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  197. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  198. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  199. package/src/dashboard/app/components/Markdown.jsx +0 -9
  200. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  201. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  202. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  203. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  204. package/src/dashboard/app/hooks/usePref.js +1 -5
  205. package/src/dashboard/app/lib/format.js +0 -2
  206. package/src/dashboard/app/lib/phases.js +1 -5
  207. package/src/dashboard/app/lib/project.js +0 -12
  208. package/src/dashboard/app/main.jsx +13 -13
  209. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  210. package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
  211. package/src/dashboard/app/pages/Features.jsx +1 -8
  212. package/src/dashboard/app/pages/Guide.jsx +0 -1
  213. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  214. package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
  215. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  216. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  217. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  218. package/src/dashboard/app/pages/Standards.jsx +2 -17
  219. package/src/dashboard/scanner/activity-scanner.js +10 -69
  220. package/src/dashboard/scanner/agents-scanner.js +1 -23
  221. package/src/dashboard/scanner/docs-scanner.js +0 -6
  222. package/src/dashboard/scanner/feature-status.js +2 -5
  223. package/src/dashboard/scanner/features-scanner.js +10 -66
  224. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  225. package/src/dashboard/scanner/index.js +0 -4
  226. package/src/dashboard/scanner/portfolio.js +5 -29
  227. package/src/dashboard/scanner/project-api.js +8 -54
  228. package/src/dashboard/scanner/rules-scanner.js +1 -2
  229. package/src/dashboard/scanner/skills-scanner.js +2 -12
  230. package/src/dashboard/scanner/standards-scanner.js +2 -6
  231. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  232. package/src/dashboard/vite.config.js +24 -24
  233. package/src/lib/ai/ai-checks.js +7 -52
  234. package/src/lib/ai/ai-project-scan.js +14 -108
  235. package/src/lib/ai/code-audit.js +7 -63
  236. package/src/lib/ai/pin-drift.js +8 -51
  237. package/src/lib/ai/registry-audit.js +11 -90
  238. package/src/lib/ci/ci-verify.js +4 -19
  239. package/src/lib/ci/github-ci.js +9 -37
  240. package/src/lib/ci/workflow-scaffold.js +14 -10
  241. package/src/lib/detectors/claude-config-detector.js +13 -74
  242. package/src/lib/detectors/index.js +4 -50
  243. package/src/lib/e2e/applicability.js +37 -0
  244. package/src/lib/e2e/compose-env.js +5 -66
  245. package/src/lib/e2e/stack-teardown.js +6 -40
  246. package/src/lib/generators/architecture-scanner.js +18 -156
  247. package/src/lib/generators/handoff-generator.js +9 -53
  248. package/src/lib/generators/scaffold-generator.js +5 -101
  249. package/src/lib/generators/settings-generator.js +4 -37
  250. package/src/lib/git/branch-classifier.js +11 -84
  251. package/src/lib/git/default-branch.js +14 -169
  252. package/src/lib/git/evidence-staleness.js +1 -0
  253. package/src/lib/git/gh.js +2 -17
  254. package/src/lib/git/pending-prs.js +6 -37
  255. package/src/lib/git/pr-files.js +3 -28
  256. package/src/lib/git/pr-index.js +3 -34
  257. package/src/lib/git/tree-fingerprint.js +3 -15
  258. package/src/lib/git/tree-state.js +6 -33
  259. package/src/lib/graph/god-nodes.js +2 -22
  260. package/src/lib/graph/graph-health.js +3 -24
  261. package/src/lib/graph/graph-loader.js +5 -56
  262. package/src/lib/graph/graphify-runtime.js +7 -37
  263. package/src/lib/graph/graphifyignore.js +8 -74
  264. package/src/lib/graph/sanitize.js +16 -136
  265. package/src/lib/heavy/cleanup.js +1 -1
  266. package/src/lib/installers/effects.js +8 -93
  267. package/src/lib/installers/mcp-installer.js +9 -169
  268. package/src/lib/installers/memory-installer.js +4 -27
  269. package/src/lib/installers/templates-reconcile.js +5 -60
  270. package/src/lib/mutation/adapters.js +6 -17
  271. package/src/lib/mutation/dotnet-introspection.js +20 -106
  272. package/src/lib/mutation/runner.js +23 -75
  273. package/src/lib/mutation/subset.js +1 -4
  274. package/src/lib/mutation/trx.js +0 -1
  275. package/src/lib/plan/artifact-vocabulary.js +5 -25
  276. package/src/lib/plan/checks.js +34 -33
  277. package/src/lib/plan/plan-parser.js +7 -8
  278. package/src/lib/projects/registry.js +7 -44
  279. package/src/lib/review-reports.js +11 -26
  280. package/src/lib/risk-detector.js +41 -173
  281. package/src/lib/specs/adr-index.js +6 -34
  282. package/src/lib/specs/frontmatter.js +2 -15
  283. package/src/lib/specs/gfm-table.js +1 -7
  284. package/src/lib/specs/revocations.js +1 -14
  285. package/src/lib/specs/screen-checklist.js +47 -124
  286. package/src/lib/stack/dotnet-build-ignores.js +2 -6
  287. package/src/lib/stack/stack-profile.js +1 -28
  288. package/src/lib/stack/stack-scan.js +1 -8
  289. package/src/lib/stack-filter.js +4 -27
  290. package/src/lib/standards/registry.js +1 -7
  291. package/src/lib/standards/sections.js +2 -13
  292. package/src/lib/standards/verified-against.js +19 -191
  293. package/src/lib/tasks/eval-node.js +21 -169
  294. package/src/lib/tasks/linux-runner.js +18 -142
  295. package/src/lib/tasks/test-nodes.js +43 -240
  296. package/src/lib/tasks/test-runner.js +43 -341
  297. package/src/lib/telemetry/metrics.js +7 -36
  298. package/src/lib/telemetry/telemetry-reader.js +13 -125
  299. package/src/lib/telemetry/verify-event.js +2 -30
  300. package/src/lib/tests-audit/audit.js +96 -0
  301. package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
  302. package/src/lib/validators/code-budget/index.js +0 -7
  303. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
  304. package/src/lib/validators/contracts/index.js +0 -6
  305. package/src/lib/validators/nextjs/index.js +0 -5
  306. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  307. package/src/lib/validators/packages/index.js +0 -6
  308. package/src/lib/validators/packages/package-validator.js +6 -56
  309. package/src/lib/validators/reuse/index.js +0 -8
  310. package/src/lib/validators/reuse/reuse-validator.js +11 -101
  311. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  312. package/src/lib/validators/traceability-check.js +5 -63
  313. package/src/lib/validators/validation-runner.js +21 -158
  314. package/src/lib/validators/verify-runner.js +85 -376
  315. package/src/lib/work-type-detector.js +5 -67
  316. package/src/lib/worktree/docker-audit.js +17 -136
  317. package/src/lib/worktree/env-allocator.js +10 -98
  318. package/src/lib/worktree/feature-truth.js +12 -115
  319. package/src/lib/worktree/finish-backup.js +32 -242
  320. package/src/lib/worktree/fleet-scanner.js +13 -95
  321. package/src/lib/worktree/ignored-triage.js +15 -91
  322. package/src/lib/worktree/vscode-settings.js +2 -3
  323. package/src/lib/worktree/worktree-audit.js +21 -213
  324. package/src/lib/worktree/worktree-include.js +3 -5
  325. package/src/scripts/global-install.js +3 -9
  326. package/src/scripts/install-plugin.js +1 -13
  327. package/src/scripts/setup-infra.js +17 -147
  328. package/src/utils/agents-installer.js +29 -276
  329. package/src/utils/banner.js +4 -25
  330. package/src/utils/claude-md-injector.js +1 -37
  331. package/src/utils/claude-settings-manager.js +1 -52
  332. package/src/utils/file-copier.js +11 -95
  333. package/src/utils/hooks-installer.js +21 -298
  334. package/src/utils/skills-installer.js +4 -101
  335. package/src/utils/version-checker.js +4 -83
  336. package/framework/commands/morph-proposal.md +0 -9
  337. package/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
  338. package/framework/hooks/git/pre-commit/agents.sh +0 -25
  339. package/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
  340. package/framework/hooks/git/pre-push/run-tests.sh +0 -44
  341. package/src/commands/utils/index.js +0 -3
  342. package/src/lib/detectors/design-system-detector.js +0 -188
  343. package/src/lib/generators/design-system-generator.js +0 -508
  344. package/src/lib/specs/old-format.js +0 -68
  345. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,28 +1,10 @@
1
- /**
2
- * ai-project-scan.js — varredura READ-ONLY dos `.csproj` / `Directory.Packages.props`
3
- * de um projeto .NET, para responder "o que este projeto declara sobre pacotes de IA?".
4
- *
5
- * Três invariantes deste módulo, todas cobradas por canário:
6
- *
7
- * 1. **Nada aqui escreve.** Nenhum import de `fs` que mute, nenhum `spawn`.
8
- * A auditoria não pode inventar o estado que reporta — é a lição do T10 da
9
- * #90, onde a auditoria de portas ia ALOCAR um bloco ao auditar.
10
- * 2. **`null` é DESCONHECIDO, `[]` é "perguntei e não achei".** Colapsar os
11
- * dois é a doutrina que `src/lib/git/pr-index.js` já pinou neste repositório.
12
- * Aqui: `scanAiPackageRefs` devolve `null` quando havia arquivos candidatos
13
- * e NENHUM foi legível; devolve um resultado com `projectFiles: []` quando
14
- * varreu e não achou `.csproj` nenhum.
15
- * 3. **Worktree é o checkout de OUTRA feature.** `worktrees/` e `.worktrees/`
16
- * ficam fora da varredura pela mesma razão pinada em `test-runner.js`:
17
- * varrê-los reporta sobre código fora da árvore avaliada. Os nomes vêm de
18
- * `src/lib/worktree/dirs.js`, nunca duplicados aqui.
19
- */
1
+ // Nada aqui escreve: a auditoria não pode inventar o estado que reporta. `null` é DESCONHECIDO,
2
+ // `[]` é "perguntei e não achei". Worktrees ficam fora: são o checkout de outra feature.
20
3
 
21
4
  import { readdirSync, readFileSync, statSync, existsSync } from 'node:fs';
22
5
  import { join, basename } from 'node:path';
23
6
  import { WORKTREE_DIR_NAMES } from '../worktree/dirs.js';
24
7
 
25
- /** Diretórios que a varredura nunca desce. */
26
8
  export const SCAN_IGNORED_DIRS = new Set([
27
9
  'node_modules',
28
10
  'bin',
@@ -31,14 +13,8 @@ export const SCAN_IGNORED_DIRS = new Set([
31
13
  ...WORKTREE_DIR_NAMES,
32
14
  ]);
33
15
 
34
- /**
35
- * Prefixos de pacote NuGet que caracterizam "pacote de IA".
36
- *
37
- * Deliberadamente curto: a lista existe para decidir se há assunto (condição 3
38
- * do silêncio do `doctor --ai`), não para catalogar o ecossistema. Um pacote de
39
- * IA fora desta lista aparece na varredura como pacote comum e, se estiver no
40
- * pin, `pin-drift` o pega mesmo assim — o pin é a outra metade do predicado.
41
- */
16
+ // Deliberadamente curto: decide se há assunto, não cataloga o ecossistema; um pacote de IA fora daqui
17
+ // que esteja no pin ainda é pego por `pin-drift`.
42
18
  export const AI_PACKAGE_PREFIXES = [
43
19
  'Microsoft.Agents.AI',
44
20
  'Microsoft.Extensions.AI',
@@ -50,10 +26,6 @@ export const AI_PACKAGE_PREFIXES = [
50
26
  'Anthropic.',
51
27
  ];
52
28
 
53
- /**
54
- * @param {string} name - nome do pacote NuGet
55
- * @returns {boolean} true quando o nome casa com um dos prefixos de IA
56
- */
57
29
  export function isAiPackage(name) {
58
30
  if (typeof name !== 'string' || name.length === 0) return false;
59
31
  return AI_PACKAGE_PREFIXES.some(
@@ -61,22 +33,11 @@ export function isAiPackage(name) {
61
33
  );
62
34
  }
63
35
 
64
- /** Remove o BOM UTF-8 do início do texto (mesmo cuidado de `doctor.js` e `env.js`). */
65
36
  function stripBom(text) {
66
37
  return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
67
38
  }
68
39
 
69
- /**
70
- * Checagem de boa-formação de XML suficiente para MSBuild, sem dependência nova.
71
- *
72
- * Não valida schema: valida que as tags fecham na ordem certa e que todo `<`
73
- * tem o seu `>`. É o predicado que separa "li o arquivo" de "não pude
74
- * perguntar" — e é ele que faz um `.csproj` truncado virar `unreadable` em vez
75
- * de virar "sem drift".
76
- *
77
- * @param {string} xml
78
- * @returns {boolean}
79
- */
40
+ /** Tags fecham na ordem e todo `<` tem `>`: um `.csproj` truncado vira `unreadable`, não "sem drift". */
80
41
  export function isWellFormedXml(xml) {
81
42
  const stack = [];
82
43
  let i = 0;
@@ -121,7 +82,6 @@ export function isWellFormedXml(xml) {
121
82
  return stack.length === 0;
122
83
  }
123
84
 
124
- /** Todas as tags de um nome, com os atributos crus. */
125
85
  function* elements(xml, tagName) {
126
86
  const re = new RegExp(`<${tagName}\\b([^>]*?)(/?)>`, 'g');
127
87
  let m;
@@ -130,17 +90,13 @@ function* elements(xml, tagName) {
130
90
  }
131
91
  }
132
92
 
133
- /** Valor de um atributo dentro do texto cru de atributos de uma tag. */
134
93
  function attr(attrs, name) {
135
94
  const m = new RegExp(`\\b${name}\\s*=\\s*"([^"]*)"`, 'i').exec(attrs)
136
95
  || new RegExp(`\\b${name}\\s*=\\s*'([^']*)'`, 'i').exec(attrs);
137
96
  return m ? m[1].trim() : null;
138
97
  }
139
98
 
140
- /**
141
- * Elemento filho `<Version>x</Version>` de um `<PackageReference>` não
142
- * auto-fechado. Recebe o XML e o índice logo após a tag de abertura.
143
- */
99
+ // Recebe o índice logo após a tag de abertura de um `<PackageReference>` não auto-fechado.
144
100
  function childVersion(xml, startIndex, tagName) {
145
101
  const close = xml.indexOf(`</${tagName}>`, startIndex);
146
102
  if (close === -1) return null;
@@ -149,18 +105,7 @@ function childVersion(xml, startIndex, tagName) {
149
105
  return m ? m[1].trim() : null;
150
106
  }
151
107
 
152
- /**
153
- * Classifica a string de versão declarada no projeto.
154
- *
155
- * - `fixed` — `1.20.0` ou a forma de igualdade exata `[1.20.0]`
156
- * - `floating` — contém `*`, ou é intervalo (`[1.0,)`, `[1.0,2.0)`, `(,2.0]`)
157
- * - `unresolved` — referência a propriedade MSBuild (`$(MafVersion)`): a
158
- * varredura NÃO resolve propriedades, e chutar seria inventar o dado
159
- * - `absent` — sem versão declarada e sem entrada de CPM
160
- *
161
- * @param {string|null} raw
162
- * @returns {{ kind: 'fixed'|'floating'|'unresolved'|'absent', value: string|null }}
163
- */
108
+ /** `unresolved` é referência a propriedade MSBuild (`$(X)`): a varredura não resolve, e chutar inventaria o dado. */
164
109
  export function classifyVersion(raw) {
165
110
  if (raw === null || raw === undefined || String(raw).trim() === '') {
166
111
  return { kind: 'absent', value: null };
@@ -175,18 +120,7 @@ export function classifyVersion(raw) {
175
120
  return { kind: 'fixed', value };
176
121
  }
177
122
 
178
- /**
179
- * Coleta recursiva de arquivos que casam com um predicado, respeitando as
180
- * exclusões de `SCAN_IGNORED_DIRS`.
181
- *
182
- * Exportada porque `registry-audit.js` e `code-audit.js` precisam da MESMA
183
- * travessia com as MESMAS exclusões. Uma segunda cópia da lista de diretórios
184
- * ignorados é como um dos três volta a varrer `worktrees/` sozinho.
185
- *
186
- * @param {string} root
187
- * @param {(name: string, fullPath: string) => boolean} matches
188
- * @returns {string[]}
189
- */
123
+ /** Exportada para que registry-audit e code-audit usem a mesma travessia com as mesmas exclusões. */
190
124
  export function collectProjectFiles(root, matches) {
191
125
  const out = [];
192
126
  if (!root || !existsSync(root)) return out;
@@ -194,7 +128,6 @@ export function collectProjectFiles(root, matches) {
194
128
  return out;
195
129
  }
196
130
 
197
- /** Coleta recursiva de arquivos que casam com um predicado, respeitando as exclusões. */
198
131
  function collectFiles(root, matches, out, depth = 0) {
199
132
  if (depth > 24) return;
200
133
  let entries;
@@ -208,8 +141,7 @@ function collectFiles(root, matches, out, depth = 0) {
208
141
  const full = join(root, name);
209
142
  let isDir = entry.isDirectory();
210
143
  if (entry.isSymbolicLink()) {
211
- // Junction/symlink: pode apontar para fora da árvore (é assim que um
212
- // worktree do Windows enxerga `.claude/`). Não descemos.
144
+ // Junction/symlink pode apontar para fora da árvore (assim um worktree do Windows enxerga `.claude/`).
213
145
  continue;
214
146
  }
215
147
  if (isDir) {
@@ -221,23 +153,8 @@ function collectFiles(root, matches, out, depth = 0) {
221
153
  }
222
154
  }
223
155
 
224
- /**
225
- * Varre `root` e devolve o que os projetos .NET declaram sobre pacotes.
226
- *
227
- * Sem limite de 10 arquivos (o de `scanStackDependencies`, que é do relatório
228
- * de arquitetura e tem outro consumidor): uma solução .NET real tem dezenas de
229
- * `.csproj`, e cortar em 10 é reportar sobre uma amostra chamando-a de todo.
230
- *
231
- * @param {string} root - raiz da varredura (o projeto)
232
- * @returns {null | {
233
- * projectFiles: string[],
234
- * cpmFiles: string[],
235
- * unreadable: string[],
236
- * packages: Array<{ name: string, file: string, ai: boolean,
237
- * version: string|null, versionKind: string,
238
- * declared: string|null, fromCpm: boolean }>
239
- * }} `null` = não pude perguntar (havia candidatos e nenhum era legível).
240
- */
156
+ // Sem limite de arquivos: cortar uma solução real é reportar sobre uma amostra chamando-a de todo.
157
+ // `null` = havia candidatos e nenhum era legível.
241
158
  export function scanAiPackageRefs(root) {
242
159
  if (!root || !existsSync(root)) return null;
243
160
  try {
@@ -280,7 +197,7 @@ export function scanAiPackageRefs(root) {
280
197
  }
281
198
 
282
199
  // Central Package Management primeiro: um PackageReference sem Version num
283
- // projeto com CPM não é achado de versão ausente (UC-24 A1).
200
+ // projeto com CPM não é achado de versão ausente.
284
201
  const cpm = new Map();
285
202
  for (const file of cpmFiles) {
286
203
  const xml = readable.get(file);
@@ -319,28 +236,17 @@ export function scanAiPackageRefs(root) {
319
236
  }
320
237
  }
321
238
 
322
- // Um Directory.Packages.props com PackageVersion de pacote de IA e nenhum
323
- // .csproj que o referencie ainda declara a intenção — mas não é uso, e este
324
- // módulo reporta declaração de uso. Fica de fora, de propósito.
239
+ // PackageVersion de IA sem .csproj que o referencie declara intenção, não uso: fica de fora.
325
240
 
326
241
  return { projectFiles, cpmFiles, unreadable, packages };
327
242
  }
328
243
 
329
- /**
330
- * Versão declarada de um pacote específico, ou `null` quando o projeto não a
331
- * declara de forma resolvível. Usado pelo gate do `removed-api` (só roda com
332
- * `Microsoft.Agents.AI` >= 1.0) e pelo relatório.
333
- *
334
- * @param {ReturnType<typeof scanAiPackageRefs>} scan
335
- * @param {string} name
336
- * @returns {{ name: string, version: string|null, versionKind: string, declared: string|null, file: string } | null}
337
- */
244
+ /** `null` quando o projeto não declara o pacote de forma resolvível. */
338
245
  export function findPackage(scan, name) {
339
246
  if (!scan || !Array.isArray(scan.packages)) return null;
340
247
  return scan.packages.find((p) => p.name === name) ?? null;
341
248
  }
342
249
 
343
- /** Nomes de arquivo (sem diretório) dos projetos varridos — para mensagens. */
344
250
  export function projectFileNames(scan) {
345
251
  if (!scan) return [];
346
252
  return scan.projectFiles.map((f) => basename(f));
@@ -1,26 +1,6 @@
1
- /**
2
- * code-audit.js — duas varreduras sobre os `.cs` do projeto.
3
- *
4
- * (a) `removed-api` — nomes renomeados ANTES do 1.0 GA do `Microsoft.Agents.AI`
5
- * (b) `prompt-source` — `Instructions` recebendo literal de string no ponto de
6
- * construção do agente
7
- *
8
- * Nenhuma das duas é grep cego, e as duas são `warn`:
9
- *
10
- * - A #93 apurou, e o standard `ai-agents/agent-session.md` registra, que
11
- * `AgentThread` → `AgentSession` e `GetNewThread()` → `CreateSessionAsync()`
12
- * aconteceram no ciclo PRÉ-GA. Dentro do `Microsoft.Agents.AI` >= 1.0 os
13
- * nomes antigos são nomes mortos — mas o MESMO identificador pertence a
14
- * outros SDKs de agente na mesma solução, e é por isso que o achado é aviso
15
- * e a mensagem manda CONFERIR a qual cliente a chamada pertence, em vez de
16
- * afirmar defeito. Sem o pacote declarado em >= 1.0 a checagem nem roda.
17
- *
18
- * - O `prompt-source` é deliberadamente conservador: acusa só o literal no
19
- * ponto de construção. Leitura de arquivo, banco, configuração ou constante
20
- * nomeada não é acusada. Falso negativo é aceitável; falso positivo não —
21
- * uma checagem que grita em código correto é desligada pelo usuário na
22
- * primeira semana.
23
- */
1
+ // As duas checagens são `warn`: o nome renomeado no ciclo pré-GA é o mesmo identificador de outros SDKs
2
+ // de agente, então a mensagem manda conferir. `prompt-source` acusa só o literal no ponto de construção:
3
+ // falso negativo é aceitável, falso positivo faz o usuário desligar a checagem.
24
4
 
25
5
  import { readFileSync } from 'node:fs';
26
6
  import { relative } from 'node:path';
@@ -40,22 +20,11 @@ function lineOf(text, index) {
40
20
  return line;
41
21
  }
42
22
 
43
- /** Todos os `.cs` do projeto, com as mesmas exclusões da varredura de `.csproj`. */
44
23
  export function collectCsharpFiles(root) {
45
24
  return collectProjectFiles(root, (name) => name.toLowerCase().endsWith('.cs'));
46
25
  }
47
26
 
48
- /**
49
- * Menor major que a versão declarada pode assumir, ou `null` quando a
50
- * declaração não permite decidir.
51
- *
52
- * `1.0.*` e `[1.0,)` têm limite inferior 1 — sobem, nunca descem. `$(Prop)` e
53
- * versão ausente NÃO permitem decidir, e decidir mesmo assim seria inventar o
54
- * dado que a checagem reporta.
55
- *
56
- * @param {{ declared: string|null, versionKind: string }|null} pkg
57
- * @returns {number|null}
58
- */
27
+ /** `null` quando a declaração não permite decidir (`$(Prop)`, ausente): decidir inventaria o dado. */
59
28
  export function lowerBoundMajor(pkg) {
60
29
  if (!pkg) return null;
61
30
  if (pkg.versionKind === 'unresolved' || pkg.versionKind === 'absent') return null;
@@ -66,12 +35,7 @@ export function lowerBoundMajor(pkg) {
66
35
  return m ? Number(m[1]) : null;
67
36
  }
68
37
 
69
- /**
70
- * Por que o `removed-api` NÃO roda, quando não roda.
71
- *
72
- * @param {{ declared: string|null, versionKind: string }|null} mafPackage
73
- * @returns {string|null} razão do "não verificado", ou `null` quando pode rodar
74
- */
38
+ /** A razão do "não verificado", ou `null` quando pode rodar. */
75
39
  export function renamedApiGate(mafPackage) {
76
40
  if (!mafPackage) {
77
41
  return 'o projeto não declara `Microsoft.Agents.AI` — os nomes antigos não pertencem a este código';
@@ -88,14 +52,7 @@ export function renamedApiGate(mafPackage) {
88
52
  return null;
89
53
  }
90
54
 
91
- /**
92
- * (a) Uso de API renomeada antes do 1.0 GA.
93
- *
94
- * @param {string} root
95
- * @param {{ mafPackage?: object|null }} [options]
96
- * @returns {null | Array<{ id: string, severity: 'warn', symbol: string, file: string, line: number, message: string }>}
97
- * `null` = não pude perguntar (gate fechado ou raiz ilegível).
98
- */
55
+ /** `null` = não pude perguntar (gate fechado ou raiz ilegível). */
99
56
  export function auditRenamedApi(root, { mafPackage = null } = {}) {
100
57
  if (renamedApiGate(mafPackage) !== null) return null;
101
58
 
@@ -131,22 +88,9 @@ export function auditRenamedApi(root, { mafPackage = null } = {}) {
131
88
  return findings;
132
89
  }
133
90
 
134
- /**
135
- * `Instructions` recebendo literal de string no ponto de construção.
136
- *
137
- * Casa `Instructions = "…"`, `Instructions = @"…"`, `Instructions = $"…"`,
138
- * `Instructions = """…"""` e o argumento nomeado `instructions: "…"`.
139
- * NÃO casa atribuição de variável, chamada, `await`, constante nomeada nem
140
- * literal dentro de comentário.
141
- */
91
+ // NÃO casa atribuição de variável, chamada, `await`, constante nomeada nem literal dentro de comentário.
142
92
  const INSTRUCTIONS_LITERAL_RE = /(?<![\w."])(?:Instructions\s*=(?!=)|instructions\s*:)\s*(?:[$@]){0,2}"/g;
143
93
 
144
- /**
145
- * (b) Prompt sem fonte declarada.
146
- *
147
- * @param {string} root
148
- * @returns {null | Array<{ id: string, severity: 'warn', file: string, line: number, message: string }>}
149
- */
150
94
  export function auditPromptSource(root) {
151
95
  const files = collectCsharpFiles(root);
152
96
  if (files.length === 0) return null;
@@ -1,70 +1,27 @@
1
- /**
2
- * pin-drift.js — o pin de pacotes de IA (`framework/ai-pin.json`) contra o que
3
- * o `.csproj` do projeto declara.
4
- *
5
- * É a ÚNICA das cinco checagens do `doctor --ai` que produz `error`, e só na
6
- * faixa flutuante. O motivo é prejuízo medido, não estética: uma faixa `2.*`
7
- * sobe sozinha para uma versão com assinatura mudada e o `MissingMethodException`
8
- * chega DEPOIS de a chamada ao modelo ter sido paga. As outras quatro checagens
9
- * informam — uma checagem que grita em código correto é desligada pelo usuário
10
- * na primeira semana.
11
- *
12
- * O pin é lido por `readAiPin` (#92), nunca por um segundo leitor: duas cópias
13
- * do mesmo fato divergem, e é para isso que a Dependency Matrix existe.
14
- */
1
+ // Única checagem que produz `error`, e só na faixa flutuante: `2.*` sobe sozinha para uma assinatura mudada
2
+ // e o `MissingMethodException` chega depois de a chamada ao modelo ter sido paga.
15
3
 
16
4
  import { join } from 'node:path';
17
5
  import { readAiPin } from '../standards/verified-against.js';
18
6
 
19
- /**
20
- * Resolve o pin para um projeto, sem NUNCA lançar.
21
- *
22
- * Ordem: (1) `<target>/.morph/framework/ai-pin.json` — a régua com que aquele
23
- * projeto foi instalado; (2) o pin do pacote da CLI. Nenhum dos dois resolve →
24
- * `null`, que o relatório traduz em "não verificado". Falta de régua é ausência
25
- * de dado, nunca defeito do projeto: é literalmente o estado de todo projeto
26
- * cujo framework é anterior ao pin.
27
- *
28
- * @param {string} targetPath - raiz do projeto
29
- * @returns {{ pinnedAt: string, packages: Record<string, {version: string}> } | null}
30
- */
7
+ // O pin instalado no projeto, senão o do pacote da CLI; nenhum resolve → `null` ("não verificado"):
8
+ // falta de régua é ausência de dado, nunca defeito do projeto.
31
9
  export function resolveAiPin(targetPath) {
32
10
  const roots = [];
33
11
  if (targetPath) roots.push(join(targetPath, '.morph'));
34
- roots.push(undefined); // root default de readAiPin = pacote da CLI
12
+ roots.push(undefined);
35
13
 
36
14
  for (const root of roots) {
37
15
  try {
38
16
  const pin = root === undefined ? readAiPin() : readAiPin(root);
39
17
  if (pin && pin.packages && Object.keys(pin.packages).length > 0) return pin;
40
- } catch {
41
- // Ausente ou inválido nesta raiz — tenta a próxima.
42
- }
18
+ } catch {}
43
19
  }
44
20
  return null;
45
21
  }
46
22
 
47
- /**
48
- * Compara a varredura do projeto com o pin.
49
- *
50
- * Três classes de achado, e a diferença entre elas é de quem é o defeito:
51
- *
52
- * | classe | severidade | de quem é |
53
- * |---|---|---|
54
- * | pacote do pin declarado com faixa flutuante | `error` | do projeto |
55
- * | pacote do pin declarado com versão fixa ≠ pin | `warn` | do projeto |
56
- * | pacote de IA presente no projeto e ausente do pin | `warn` | **do pin** (lacuna) |
57
- *
58
- * A terceira classe é descrita como lacuna do PIN de propósito: medido,
59
- * `Microsoft.Agents.AI.OpenAI` aparece em três repositórios do acervo e não
60
- * está no pin. Acusar o projeto ali seria acusar o certo pelo errado.
61
- *
62
- * @param {object} params
63
- * @param {ReturnType<import('./ai-project-scan.js').scanAiPackageRefs>} params.scan
64
- * @param {ReturnType<typeof resolveAiPin>} params.pin
65
- * @returns {null | Array<{ id: string, severity: 'error'|'warn', package: string, message: string }>}
66
- * `null` = não pude perguntar (sem varredura ou sem pin).
67
- */
23
+ // Pacote de IA presente no projeto e ausente do pin é lacuna do PIN, não do projeto.
24
+ // `null` = não pude perguntar (sem varredura ou sem pin).
68
25
  export function auditPinDrift({ scan, pin } = {}) {
69
26
  if (!scan || !pin || !pin.packages) return null;
70
27
 
@@ -1,22 +1,5 @@
1
- /**
2
- * registry-audit.js — o registry de modelos do projeto: tarifa por alias e
3
- * protocolo (`api`) por alias.
4
- *
5
- * As duas perguntas saem do standard `ai-agents/providers/model-registry.md`,
6
- * não da memória de quem implementa:
7
- *
8
- * - **tarifa por alias** — um alias sem tarifa é um custo que ninguém
9
- * consegue prever antes de a chamada ser paga. O caso medido no acervo é o
10
- * GHLBrain, cujo mapa `Pricing.Models` é literalmente `{}`.
11
- * - **`api` por alias** — o standard mede, sobre o pin, que
12
- * `AgentApi.Responses = 0`, ou seja `default(AgentApi)` é Responses, e que
13
- * `ModelRegistry.ReadApi` devolve `AgentApi.Responses` quando o campo está
14
- * ausente. A ausência do campo, portanto, NÃO é defeito: é uma decisão
15
- * tomada por omissão. O achado NOMEIA o default assumido e pede
16
- * confirmação — nunca afirma defeito.
17
- *
18
- * Severidade: `warn` nas duas. Só `pin-drift` produz `error`.
19
- */
1
+ // Ausência de `api` não é defeito: o default medido sobre o pin é Responses, então o achado nomeia o
2
+ // default e pede confirmação. Severidade `warn`; só `pin-drift` produz `error`.
20
3
 
21
4
  import { readFileSync } from 'node:fs';
22
5
  import { existsSync } from 'node:fs';
@@ -24,22 +7,10 @@ import { isAbsolute, join, basename } from 'node:path';
24
7
  import { collectProjectFiles } from './ai-project-scan.js';
25
8
  import { jsoncToJson } from '../../../framework/hooks/shared/comment-stripper.js';
26
9
 
27
- /** Limite de tamanho para um JSON candidato (bytes de texto) — evita ler dumps. */
28
10
  const MAX_JSON_CHARS = 512 * 1024;
29
11
 
30
- /**
31
- * Lê um candidato a registry aceitando **JSONC**.
32
- *
33
- * Não é indulgência: é paridade com o leitor de produção. O exemplar canônico
34
- * `framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json`
35
- * — que diz na primeira linha "copie para `config/model-registry.json` no
36
- * projeto" — tem 20 linhas de `//`, e `ModelRegistry.FromJson` do kit declara
37
- * `CommentHandling = JsonCommentHandling.Skip` + `AllowTrailingCommas = true`.
38
- * Com `JSON.parse` cru, as duas checagens de registry saíam `skip` sobre a
39
- * ÚNICA forma que existem para auditar — e `skip` não muda o código de saída,
40
- * então o silêncio era total. A auditoria tem de enxergar a mesma forma que o
41
- * leitor de produção enxerga.
42
- */
12
+ // JSONC por paridade com o leitor de produção (`ModelRegistry.FromJson` aceita comentário e vírgula
13
+ // sobrando): com `JSON.parse` cru o exemplar canônico saía `skip`, e `skip` não muda o código de saída.
43
14
  function readJsonSafe(file) {
44
15
  try {
45
16
  const raw = readFileSync(file, 'utf8');
@@ -67,7 +38,7 @@ function asAliasSource(file, data) {
67
38
  return { file, aliases: data.aliases, defaultAlias: data.defaultAlias, data };
68
39
  }
69
40
 
70
- /** A forma medida no GHLBrain: a seção `Pricing` de um `appsettings*.json`. */
41
+ /** A seção `Pricing` de um `appsettings*.json`. */
71
42
  function asPricingSource(file, data) {
72
43
  if (!isPlainObject(data)) return null;
73
44
  const pricing = data.Pricing ?? data.pricing;
@@ -77,19 +48,8 @@ function asPricingSource(file, data) {
77
48
  return { file, models };
78
49
  }
79
50
 
80
- /**
81
- * Localiza o registry de modelos do projeto.
82
- *
83
- * Ordem, primeira ocorrência ganha (spec §3.5):
84
- * 1. path declarado em `.morph/config/config.json → project.ai.modelRegistry`;
85
- * 2. um `*.json` cujo objeto raiz tenha `defaultAlias` **e** um mapa `aliases`;
86
- * 3. a seção `Pricing` de um `appsettings*.json`.
87
- *
88
- * @param {string} root - raiz do projeto
89
- * @param {object|null} config - conteúdo de `.morph/config/config.json`, se houver
90
- * @returns {null | { aliasSource: object|null, pricingSource: object|null }}
91
- * `null` quando NENHUMA das três formas foi encontrada.
92
- */
51
+ // Primeira forma ganha: o path de `project.ai.modelRegistry`, um `*.json` com `defaultAlias` e `aliases`,
52
+ // ou a seção `Pricing` de um `appsettings*.json`. `null` sem nenhuma.
93
53
  export function locateAiRegistry(root, config = null) {
94
54
  let aliasSource = null;
95
55
  let pricingSource = null;
@@ -132,7 +92,6 @@ export function locateAiRegistry(root, config = null) {
132
92
  return { aliasSource, pricingSource };
133
93
  }
134
94
 
135
- /** Mapa de tarifas embutido no próprio registry de modelos, quando existir. */
136
95
  function extractPricingFromRegistry(file, data) {
137
96
  if (!isPlainObject(data)) return null;
138
97
  const pricing = data.pricing ?? data.Pricing;
@@ -153,40 +112,14 @@ function aliasHasWaiver(def) {
153
112
  return isPlainObject(def) && typeof def.pricingWaiver === 'string' && def.pricingWaiver.trim() !== '';
154
113
  }
155
114
 
156
- /**
157
- * O documento usa a forma **por alias** (`pricing` / `pricingWaiver` dentro de
158
- * cada alias) em vez da forma de mapa no topo?
159
- *
160
- * Basta UM alias declarar qualquer uma das duas: um documento meio-preenchido
161
- * ainda é um documento na forma v2, e os aliases que faltam é que são o achado.
162
- */
115
+ // Basta um alias declarar `pricing` ou `pricingWaiver`: documento meio-preenchido ainda é forma por alias.
163
116
  export function usesPerAliasPricing(aliasSource) {
164
117
  if (!aliasSource) return false;
165
118
  return Object.values(aliasSource.aliases).some((d) => aliasHasPricing(d) || aliasHasWaiver(d));
166
119
  }
167
120
 
168
- /**
169
- * Todo alias do registry tem uma DECISÃO de tarifa?
170
- *
171
- * Duas formas, e a checagem cobra a que o documento usa:
172
- *
173
- * 1. **Por alias** (`model-registry.v2.json`, o exemplar que o framework manda
174
- * copiar) — a REGRA DURA escrita no próprio exemplar e imposta por
175
- * `ModelRegistry.ReadPricing`: *"todo alias declara `pricing` OU
176
- * `pricingWaiver` — nunca os dois, nunca nenhum"*. Um `pricingWaiver` é uma
177
- * tarifa **decidida**: a ausência está escrita, não esquecida, e acusá-la
178
- * seria gritar em código correto.
179
- * 2. **Mapa no topo** (`pricing` na raiz, ou a seção `Pricing.Models` de um
180
- * `appsettings*.json` — a forma medida no GHLBrain, cujo mapa é
181
- * literalmente `{}`).
182
- *
183
- * Os achados de MAPA (`pricing-map-missing` / `pricing-map-empty`) só existem
184
- * na forma 2: num documento v2 não há mapa a faltar, e emitir o achado ali era
185
- * o primeiro dos seis falsos positivos sobre o exemplar canônico.
186
- *
187
- * @param {ReturnType<typeof locateAiRegistry>} registry
188
- * @returns {null | Array<{ id: string, severity: 'warn', alias?: string, message: string }>}
189
- */
121
+ // Por alias, todo alias declara `pricing` OU `pricingWaiver`, nunca os dois, nunca nenhum (um waiver é
122
+ // tarifa decidida). Os achados de mapa só existem na forma de mapa no topo.
190
123
  export function auditModelPricing(registry) {
191
124
  if (!registry) return null;
192
125
  const { aliasSource, pricingSource } = registry;
@@ -253,19 +186,7 @@ export function auditModelPricing(registry) {
253
186
  return findings;
254
187
  }
255
188
 
256
- /**
257
- * Todo alias declara `api`?
258
- *
259
- * O default do standard é **`responses`**: medido sobre o pin, `AgentApi.Responses = 0`
260
- * e `ModelRegistry.ReadApi` devolve Responses quando o campo está ausente. A mensagem
261
- * nomeia esse default e PEDE CONFIRMAÇÃO — a ausência do campo é uma decisão por
262
- * omissão, não um defeito. É a pergunta que o `ModelRegistry.cs` do ProspectPRO
263
- * documenta ter custado um bug.
264
- *
265
- * @param {ReturnType<typeof locateAiRegistry>} registry
266
- * @returns {null | Array<{ id: string, severity: 'warn', alias: string, message: string }>}
267
- * `null` quando não há aliases para perguntar.
268
- */
189
+ /** `null` quando não há aliases para perguntar. */
269
190
  export function auditApiPerAlias(registry) {
270
191
  if (!registry || !registry.aliasSource) return null;
271
192
  const findings = [];
@@ -47,13 +47,8 @@ function dispatchWorkflow(root, workflow, branch) {
47
47
  }
48
48
  }
49
49
 
50
- /**
51
- * When CI falls back to local, the last green feature stamp still proves the tree if nothing moved
52
- * since it by the gate's own staleness rule and nothing is uncommitted (treeMovedSince sees only
53
- * commits). A null sha or HEAD would read as "not moved", so both are required.
54
- *
55
- * @returns {object|null} the stamp to reuse, or null = run local
56
- */
50
+ // The last green stamp still proves the tree if nothing moved since it and nothing is uncommitted
51
+ // (treeMovedSince sees only commits). A null sha or HEAD would read as "not moved", so both are required.
57
52
  export function reusableLocalStamp(root, stamp) {
58
53
  if (stamp?.status !== 'pass' || !stamp.sha) return null;
59
54
  const head = getHeadSha(root);
@@ -63,18 +58,8 @@ export function reusableLocalStamp(root, stamp) {
63
58
  return treeMovedSince(root, stamp.sha, head) ? null : stamp;
64
59
  }
65
60
 
66
- /**
67
- * `verify {f} --ci`: the feature-scope proof, run by the project's CI over the pushed commit.
68
- * Every way CI cannot answer is `mode: 'local'` with the reason, and the caller runs the local
69
- * verify: no CI is never a pause. Only the uploaded `verify --json` becomes a result, so the
70
- * gate judges a CI run by the same stamp and the same rules as a local one.
71
- *
72
- * @returns {Promise<
73
- * { mode: 'local', reason: string } |
74
- * { mode: 'pending', reason: string, run: object|null } |
75
- * { mode: 'ci', result: object, run: object, created: string|null }>}
76
- * @throws {CiUsageError} code CI would not see (uncommitted work)
77
- */
61
+ // Every way CI cannot answer is `mode: 'local'` with the reason: no CI is never a pause. Only the
62
+ // uploaded `verify --json` becomes a result, judged by the same stamp and rules as a local run.
78
63
  export async function runCiVerify(root, featureName, config, { log = () => {}, waitMs = CI_WAIT_MS, wait = waitForRun } = {}) {
79
64
  const avail = ciAvailability(root, featureName, config);
80
65
  if (!avail.ok) return { mode: 'local', reason: avail.reason };