@jaimevalasek/aioson 1.28.1 → 1.30.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 (155) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +7 -5
  3. package/docs/en/5-reference/cli-reference.md +40 -10
  4. package/docs/pt/4-agentes/briefing.md +2 -0
  5. package/docs/pt/4-agentes/copywriter.md +2 -0
  6. package/docs/pt/4-agentes/genome.md +1 -0
  7. package/docs/pt/4-agentes/pm.md +1 -1
  8. package/docs/pt/4-agentes/profiler-enricher.md +2 -0
  9. package/docs/pt/4-agentes/profiler-forge.md +2 -0
  10. package/docs/pt/4-agentes/sheldon.md +2 -0
  11. package/docs/pt/4-agentes/squad.md +12 -10
  12. package/docs/pt/5-referencia/autopilot-handoff.md +4 -4
  13. package/docs/pt/5-referencia/comandos-cli.md +7 -3
  14. package/docs/pt/5-referencia/fluxo-artefatos.md +1 -1
  15. package/docs/pt/5-referencia/memoria-e-contexto.md +62 -2
  16. package/docs/pt/_arquivo/monitor-de-contexto.md +2 -2
  17. package/package.json +4 -2
  18. package/src/cli.js +72 -24
  19. package/src/commands/ac-test-audit.js +45 -0
  20. package/src/commands/artifact-validate.js +62 -50
  21. package/src/commands/classify.js +73 -2
  22. package/src/commands/context-brief.js +59 -0
  23. package/src/commands/context-guard.js +88 -0
  24. package/src/commands/context-monitor.js +1 -1
  25. package/src/commands/context-search.js +101 -52
  26. package/src/commands/context-select.js +11 -2
  27. package/src/commands/feature-archive.js +21 -12
  28. package/src/commands/feature-current.js +82 -0
  29. package/src/commands/gate-check.js +32 -15
  30. package/src/commands/harness-check.js +17 -1
  31. package/src/commands/hooks-install.js +169 -26
  32. package/src/commands/hygiene-scan.js +423 -0
  33. package/src/commands/rules-lint.js +124 -0
  34. package/src/commands/sdd-benchmark.js +134 -0
  35. package/src/commands/spec-analyze.js +6 -4
  36. package/src/commands/store-system.js +329 -49
  37. package/src/constants.js +8 -3
  38. package/src/context-brief.js +585 -0
  39. package/src/context-guard.js +209 -0
  40. package/src/context-search.js +796 -96
  41. package/src/context-selector.js +802 -420
  42. package/src/handoff-contract.js +14 -6
  43. package/src/harness/contract-schema.js +1 -1
  44. package/src/i18n/messages/en.js +12 -5
  45. package/src/i18n/messages/es.js +11 -4
  46. package/src/i18n/messages/fr.js +11 -4
  47. package/src/i18n/messages/pt-BR.js +12 -5
  48. package/src/lib/ac-test-audit.js +194 -0
  49. package/src/preflight-engine.js +10 -6
  50. package/src/squad/state-manager.js +1 -1
  51. package/template/.aioson/agents/analyst.md +93 -53
  52. package/template/.aioson/agents/architect.md +41 -32
  53. package/template/.aioson/agents/briefing-refiner.md +15 -2
  54. package/template/.aioson/agents/briefing.md +105 -86
  55. package/template/.aioson/agents/committer.md +1 -1
  56. package/template/.aioson/agents/copywriter.md +53 -10
  57. package/template/.aioson/agents/design-hybrid-forge.md +9 -5
  58. package/template/.aioson/agents/dev.md +22 -25
  59. package/template/.aioson/agents/deyvin.md +126 -124
  60. package/template/.aioson/agents/discover.md +8 -9
  61. package/template/.aioson/agents/discovery-design-doc.md +52 -36
  62. package/template/.aioson/agents/forge-run.md +3 -0
  63. package/template/.aioson/agents/genome.md +12 -6
  64. package/template/.aioson/agents/neo.md +30 -24
  65. package/template/.aioson/agents/orache.md +16 -21
  66. package/template/.aioson/agents/orchestrator.md +40 -31
  67. package/template/.aioson/agents/pentester.md +22 -12
  68. package/template/.aioson/agents/pm.md +11 -2
  69. package/template/.aioson/agents/product.md +162 -183
  70. package/template/.aioson/agents/profiler-enricher.md +29 -6
  71. package/template/.aioson/agents/profiler-forge.md +16 -6
  72. package/template/.aioson/agents/profiler-researcher.md +10 -6
  73. package/template/.aioson/agents/qa.md +29 -19
  74. package/template/.aioson/agents/scope-check.md +14 -2
  75. package/template/.aioson/agents/sheldon.md +51 -21
  76. package/template/.aioson/agents/site-forge.md +4 -6
  77. package/template/.aioson/agents/squad.md +7 -12
  78. package/template/.aioson/agents/tester.md +40 -30
  79. package/template/.aioson/agents/ux-ui.md +56 -41
  80. package/template/.aioson/agents/validator.md +2 -2
  81. package/template/.aioson/config.md +4 -3
  82. package/template/.aioson/design-docs/agent-loading-contract.md +3 -3
  83. package/template/.aioson/docs/LAYERS.md +2 -0
  84. package/template/.aioson/docs/autonomy-protocol.md +7 -5
  85. package/template/.aioson/docs/autopilot-handoff.md +5 -3
  86. package/template/.aioson/docs/dev/execution-discipline.md +3 -0
  87. package/template/.aioson/docs/dev/simple-plan-lane.md +126 -77
  88. package/template/.aioson/docs/dev/stack-conventions.md +4 -1
  89. package/template/.aioson/docs/deyvin/continuity-recovery.md +21 -18
  90. package/template/.aioson/docs/deyvin/debugging-escalation.md +3 -0
  91. package/template/.aioson/docs/deyvin/pair-execution.md +3 -0
  92. package/template/.aioson/docs/deyvin/runtime-handoffs.md +6 -3
  93. package/template/.aioson/docs/dossier/agent-templates.md +3 -0
  94. package/template/.aioson/docs/dossier/schema.md +3 -0
  95. package/template/.aioson/docs/example-external-api-context.md +2 -0
  96. package/template/.aioson/docs/feature-expansion-taxonomy.md +53 -0
  97. package/template/.aioson/docs/handoff-persistence.md +95 -91
  98. package/template/.aioson/docs/pentester/app-playbooks.md +3 -0
  99. package/template/.aioson/docs/pentester/browser-dast-playbook.md +401 -398
  100. package/template/.aioson/docs/pentester/llm-supplychain.md +3 -0
  101. package/template/.aioson/docs/product/conversation-playbook.md +1 -1
  102. package/template/.aioson/docs/quality/code-health-analysis.md +2 -0
  103. package/template/.aioson/docs/sheldon/enrichment-paths.md +47 -1
  104. package/template/.aioson/docs/sheldon/harness-contract.md +26 -21
  105. package/template/.aioson/docs/sheldon/quality-lens.md +3 -0
  106. package/template/.aioson/docs/sheldon/research-loop.md +3 -0
  107. package/template/.aioson/docs/sheldon/web-intelligence.md +3 -0
  108. package/template/.aioson/docs/site-forge-build.md +4 -2
  109. package/template/.aioson/docs/site-forge-extraction.md +2 -0
  110. package/template/.aioson/docs/site-forge-qa.md +2 -0
  111. package/template/.aioson/docs/site-forge-recon.md +7 -5
  112. package/template/.aioson/docs/site-forge-transform.md +2 -0
  113. package/template/.aioson/docs/squad/content-output.md +3 -0
  114. package/template/.aioson/docs/squad/creation-flow.md +22 -1
  115. package/template/.aioson/docs/squad/domain-breadth.md +3 -0
  116. package/template/.aioson/docs/squad/domain-classification.md +3 -0
  117. package/template/.aioson/docs/squad/eval-gate.md +3 -0
  118. package/template/.aioson/docs/squad/genome-bindings.md +14 -0
  119. package/template/.aioson/docs/squad/package-contract.md +5 -0
  120. package/template/.aioson/docs/squad/persona-grounding.md +65 -62
  121. package/template/.aioson/docs/squad/quality-lens.md +3 -0
  122. package/template/.aioson/docs/squad/research-loop.md +3 -0
  123. package/template/.aioson/docs/squad/session-operations.md +3 -0
  124. package/template/.aioson/docs/squad/workflow-quality.md +3 -0
  125. package/template/.aioson/docs/tester/coverage-quality.md +4 -1
  126. package/template/.aioson/docs/ux-ui/design-execution.md +9 -7
  127. package/template/.aioson/rules/README.md +48 -2
  128. package/template/.aioson/rules/agent-language-policy.md +26 -21
  129. package/template/.aioson/rules/agent-structural-contract.md +168 -158
  130. package/template/.aioson/rules/aioson-context-boundary.md +7 -1
  131. package/template/.aioson/rules/canonical-path-contract.md +16 -10
  132. package/template/.aioson/rules/data-format-convention.md +17 -11
  133. package/template/.aioson/rules/disk-first-artifacts.md +12 -8
  134. package/template/.aioson/rules/example-monetary-values.md +4 -0
  135. package/template/.aioson/rules/implementation-structure-and-data-access.md +50 -0
  136. package/template/.aioson/rules/output-brevity.md +2 -0
  137. package/template/.aioson/rules/prd-section-ownership.md +17 -12
  138. package/template/.aioson/rules/security-baseline.md +8 -3
  139. package/template/.aioson/rules/simple-plan-lane.md +22 -5
  140. package/template/.aioson/rules/source-code-language-convention.md +34 -0
  141. package/template/.aioson/rules/spec-level-ownership.md +10 -5
  142. package/template/.aioson/rules/squad-driver-pattern.md +5 -0
  143. package/template/.aioson/skills/process/aioson-spec-driven/references/artifact-map.md +24 -23
  144. package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +4 -0
  145. package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +2 -2
  146. package/template/.aioson/skills/process/aioson-spec-driven/references/qa.md +1 -1
  147. package/template/.aioson/skills/process/briefing-expansion-scout/SKILL.md +72 -0
  148. package/template/.aioson/skills/process/product-scope-expansion/SKILL.md +74 -0
  149. package/template/.aioson/skills/process/sheldon-expansion-audit/SKILL.md +67 -0
  150. package/template/.aioson/skills/static/context-budget-guide.md +1 -1
  151. package/template/.aioson/skills/static/multi-agent-patterns.md +5 -4
  152. package/template/.aioson/tasks/squad-create.md +11 -0
  153. package/template/.aioson/tasks/squad-design.md +3 -3
  154. package/template/AGENTS.md +36 -19
  155. package/template/CLAUDE.md +9 -5
package/src/cli.js CHANGED
@@ -14,6 +14,8 @@ const { runAgentsList, runAgentPrompt } = require('./commands/agents');
14
14
  const { runContextValidate } = require('./commands/context-validate');
15
15
  const { runContextPack } = require('./commands/context-pack');
16
16
  const { runContextSelect } = require('./commands/context-select');
17
+ const { runContextBrief } = require('./commands/context-brief');
18
+ const { runRulesLint } = require('./commands/rules-lint');
17
19
  const { runContextLoad } = require('./commands/context-load');
18
20
  const { runChainAudit } = require('./commands/chain-audit');
19
21
  const { runMemorySearch } = require('./commands/memory-search');
@@ -147,15 +149,17 @@ const { runBackupLocal } = require('./commands/backup-local-cmd');
147
149
  const { runRecoveryGenerate, runRecoveryShow } = require('./commands/recovery');
148
150
  const { runContextMonitor } = require('./commands/context-monitor');
149
151
  const { runContextSearch, runContextSearchIndex } = require('./commands/context-search');
152
+ const { runContextGuard } = require('./commands/context-guard');
150
153
  const { runContextCacheList, runContextCacheSave, runContextCacheRestore, runContextCacheCleanup } = require('./commands/context-cache');
151
154
  const { runSandboxExec } = require('./commands/sandbox');
152
155
  const { runAgentLoad, runAgentShardIndex } = require('./commands/agent-loader');
153
156
  const { runLearningEvolve, runLearningApply } = require('./commands/learning-evolve');
154
157
  const { runLearningRollback } = require('./commands/learning-rollback');
155
- const { runToolRegistry } = require('./commands/tool-registry-cmd');
156
- const { runHealth } = require('./commands/health');
157
- const { runContextHealth } = require('./commands/context-health');
158
- const { runContextTrim } = require('./commands/context-trim');
158
+ const { runToolRegistry } = require('./commands/tool-registry-cmd');
159
+ const { runHealth } = require('./commands/health');
160
+ const { runContextHealth } = require('./commands/context-health');
161
+ const { runHygieneScan } = require('./commands/hygiene-scan');
162
+ const { runContextTrim } = require('./commands/context-trim');
159
163
  const { runHooksEmit } = require('./commands/hooks-emit');
160
164
  const { runHooksInstall, runHooksUninstall } = require('./commands/hooks-install');
161
165
  const { runSessionGuard } = require('./commands/session-guard');
@@ -197,12 +201,15 @@ const { runOpMigrate } = require('./commands/op-migrate');
197
201
  const { runFeatureClose } = require('./commands/feature-close');
198
202
  const { runFeatureArchive, runFeatureSweep } = require('./commands/feature-archive');
199
203
  const { runFeatureExport } = require('./commands/feature-export');
204
+ const { runFeatureCurrent } = require('./commands/feature-current');
200
205
  const { runDossierInit, runDossierShow, runDossierAddFinding, runDossierAddCodemap, runDossierLinkRule, runDossierCompact } = require('./commands/dossier');
201
206
  const { runDossierAddResearch } = require('./commands/dossier-add-research');
202
207
  const { runDossierAudit } = require('./commands/dossier-audit');
203
208
  const { runDevResumeData } = require('./commands/dev-resume');
204
- const { runRevisionOpen, runRevisionList, runRevisionResolve } = require('./commands/revision');
205
- const { runGateCheck } = require('./commands/gate-check');
209
+ const { runRevisionOpen, runRevisionList, runRevisionResolve } = require('./commands/revision');
210
+ const { runAcTestAudit } = require('./commands/ac-test-audit');
211
+ const { runSddBenchmark } = require('./commands/sdd-benchmark');
212
+ const { runGateCheck } = require('./commands/gate-check');
206
213
  const { runGateApprove } = require('./commands/gate-approve');
207
214
  const { runArtifactValidate } = require('./commands/artifact-validate');
208
215
  const { runWorkflowExecute } = require('./commands/workflow-execute');
@@ -257,6 +264,12 @@ const JSON_SUPPORTED_COMMANDS = new Set([
257
264
  'context-pack',
258
265
  'context:select',
259
266
  'context-select',
267
+ 'context:brief',
268
+ 'context-brief',
269
+ 'context:guard',
270
+ 'context-guard',
271
+ 'rules:lint',
272
+ 'rules-lint',
260
273
  'context:load',
261
274
  'context-load',
262
275
  'chain:audit',
@@ -275,6 +288,8 @@ const JSON_SUPPORTED_COMMANDS = new Set([
275
288
  'workflow-next',
276
289
  'workflow:status',
277
290
  'workflow-status',
291
+ 'feature:current',
292
+ 'feature-current',
278
293
  'harness:retro',
279
294
  'harness-retro',
280
295
  'harness:preview',
@@ -596,12 +611,16 @@ const JSON_SUPPORTED_COMMANDS = new Set([
596
611
  'recovery-show',
597
612
  'context:monitor',
598
613
  'context-monitor',
599
- 'context:health',
600
- 'context-health',
601
- 'context:trim',
614
+ 'context:health',
615
+ 'context-health',
616
+ 'hygiene:scan',
617
+ 'hygiene-scan',
618
+ 'context:trim',
602
619
  'context-trim',
603
620
  'context:search',
604
621
  'context-search',
622
+ 'context:index',
623
+ 'context-index',
605
624
  'context:search:index',
606
625
  'context-search-index',
607
626
  'context:cache',
@@ -666,6 +685,8 @@ const JSON_SUPPORTED_COMMANDS = new Set([
666
685
  'feature-sweep',
667
686
  'feature:export',
668
687
  'feature-export',
688
+ 'feature:current',
689
+ 'feature-current',
669
690
  'dossier:init',
670
691
  'dossier-init',
671
692
  'dossier:show',
@@ -688,9 +709,13 @@ const JSON_SUPPORTED_COMMANDS = new Set([
688
709
  'revision-open',
689
710
  'revision:list',
690
711
  'revision-list',
691
- 'revision:resolve',
692
- 'revision-resolve',
693
- 'gate:check',
712
+ 'revision:resolve',
713
+ 'revision-resolve',
714
+ 'ac:test-audit',
715
+ 'ac-test-audit',
716
+ 'sdd:benchmark',
717
+ 'sdd-benchmark',
718
+ 'gate:check',
694
719
  'gate-check',
695
720
  'gate:approve',
696
721
  'gate-approve',
@@ -807,9 +832,10 @@ function printHelp(t, logger) {
807
832
  logHelpLine(t, logger, 'cli.help_install');
808
833
  logHelpLine(t, logger, 'cli.help_setup');
809
834
  logHelpLine(t, logger, 'cli.help_update');
810
- logHelpLine(t, logger, 'cli.help_info');
811
- logHelpLine(t, logger, 'cli.help_doctor');
812
- logHelpLine(t, logger, 'cli.help_i18n_add');
835
+ logHelpLine(t, logger, 'cli.help_info');
836
+ logHelpLine(t, logger, 'cli.help_doctor');
837
+ logHelpLine(t, logger, 'cli.help_hygiene_scan');
838
+ logHelpLine(t, logger, 'cli.help_i18n_add');
813
839
  logHelpLine(t, logger, 'cli.help_agents');
814
840
  logHelpLine(t, logger, 'cli.help_agent_prompt');
815
841
  logHelpLine(t, logger, 'cli.help_agent_help');
@@ -817,7 +843,10 @@ function printHelp(t, logger) {
817
843
  logHelpLine(t, logger, 'cli.help_agent_epilogue');
818
844
  logHelpLine(t, logger, 'cli.help_context_validate');
819
845
  logHelpLine(t, logger, 'cli.help_context_pack');
846
+ logHelpLine(t, logger, 'cli.help_context_search');
820
847
  logHelpLine(t, logger, 'cli.help_context_select');
848
+ logHelpLine(t, logger, 'cli.help_context_brief');
849
+ logHelpLine(t, logger, 'cli.help_context_guard');
821
850
  logHelpLine(t, logger, 'cli.help_context_load');
822
851
  logHelpLine(t, logger, 'cli.help_memory_status');
823
852
  logHelpLine(t, logger, 'cli.help_memory_summary');
@@ -1105,6 +1134,12 @@ async function main() {
1105
1134
  result = await runContextPack({ args, options, logger: commandLogger, t });
1106
1135
  } else if (command === 'context:select' || command === 'context-select') {
1107
1136
  result = await runContextSelect({ args, options, logger: commandLogger, t });
1137
+ } else if (command === 'context:brief' || command === 'context-brief') {
1138
+ result = await runContextBrief({ args, options, logger: commandLogger, t });
1139
+ } else if (command === 'context:guard' || command === 'context-guard') {
1140
+ result = await runContextGuard({ args, options, logger: commandLogger, t });
1141
+ } else if (command === 'rules:lint' || command === 'rules-lint') {
1142
+ result = await runRulesLint({ args, options, logger: commandLogger, t });
1108
1143
  } else if (command === 'context:load' || command === 'context-load') {
1109
1144
  result = await runContextLoad({ args, options, logger: commandLogger, t });
1110
1145
  } else if (command === 'chain:audit' || command === 'chain-audit') {
@@ -1505,13 +1540,20 @@ async function main() {
1505
1540
  result = await runRecoveryShow({ args, options, logger: commandLogger, t });
1506
1541
  } else if (command === 'context:monitor' || command === 'context-monitor') {
1507
1542
  result = await runContextMonitor({ args, options, logger: commandLogger, t });
1508
- } else if (command === 'context:health' || command === 'context-health') {
1509
- result = await runContextHealth({ args, options, logger: commandLogger });
1510
- } else if (command === 'context:trim' || command === 'context-trim') {
1511
- result = await runContextTrim({ args, options, logger: commandLogger });
1543
+ } else if (command === 'context:health' || command === 'context-health') {
1544
+ result = await runContextHealth({ args, options, logger: commandLogger });
1545
+ } else if (command === 'hygiene:scan' || command === 'hygiene-scan') {
1546
+ result = await runHygieneScan({ args, options, logger: commandLogger });
1547
+ } else if (command === 'context:trim' || command === 'context-trim') {
1548
+ result = await runContextTrim({ args, options, logger: commandLogger });
1512
1549
  } else if (command === 'context:search' || command === 'context-search') {
1513
1550
  result = await runContextSearch({ args, options, logger: commandLogger, t });
1514
- } else if (command === 'context:search:index' || command === 'context-search-index') {
1551
+ } else if (
1552
+ command === 'context:index' ||
1553
+ command === 'context-index' ||
1554
+ command === 'context:search:index' ||
1555
+ command === 'context-search-index'
1556
+ ) {
1515
1557
  result = await runContextSearchIndex({ args, options, logger: commandLogger, t });
1516
1558
  } else if (command === 'context:cache' || command === 'context-cache') {
1517
1559
  result = await runContextCacheList({ args, options, logger: commandLogger, t });
@@ -1591,6 +1633,8 @@ async function main() {
1591
1633
  result = await runFeatureSweep({ args, options, logger: commandLogger });
1592
1634
  } else if (command === 'feature:export' || command === 'feature-export') {
1593
1635
  result = await runFeatureExport({ args, options, logger: commandLogger });
1636
+ } else if (command === 'feature:current' || command === 'feature-current') {
1637
+ result = await runFeatureCurrent({ args, options, logger: commandLogger });
1594
1638
  } else if (command === 'dossier:init' || command === 'dossier-init') {
1595
1639
  result = await runDossierInit({ args, options, logger: commandLogger });
1596
1640
  } else if (command === 'dossier:show' || command === 'dossier-show') {
@@ -1613,10 +1657,14 @@ async function main() {
1613
1657
  result = await runRevisionOpen({ args, options, logger: commandLogger });
1614
1658
  } else if (command === 'revision:list' || command === 'revision-list') {
1615
1659
  result = await runRevisionList({ args, options, logger: commandLogger });
1616
- } else if (command === 'revision:resolve' || command === 'revision-resolve') {
1617
- result = await runRevisionResolve({ args, options, logger: commandLogger });
1618
- } else if (command === 'gate:check' || command === 'gate-check') {
1619
- result = await runGateCheck({ args, options, logger: commandLogger });
1660
+ } else if (command === 'revision:resolve' || command === 'revision-resolve') {
1661
+ result = await runRevisionResolve({ args, options, logger: commandLogger });
1662
+ } else if (command === 'ac:test-audit' || command === 'ac-test-audit') {
1663
+ result = await runAcTestAudit({ args, options, logger: commandLogger });
1664
+ } else if (command === 'sdd:benchmark' || command === 'sdd-benchmark') {
1665
+ result = await runSddBenchmark({ args, options, logger: commandLogger });
1666
+ } else if (command === 'gate:check' || command === 'gate-check') {
1667
+ result = await runGateCheck({ args, options, logger: commandLogger });
1620
1668
  } else if (command === 'gate:approve' || command === 'gate-approve') {
1621
1669
  result = await runGateApprove({ args, options, logger: commandLogger });
1622
1670
  } else if (command === 'artifact:validate' || command === 'artifact-validate') {
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const { auditAcceptanceCriteriaTests } = require('../lib/ac-test-audit');
5
+
6
+ async function runAcTestAudit({ args, options = {}, logger }) {
7
+ const targetDir = path.resolve(process.cwd(), args?.[0] || '.');
8
+ const slug = String(options.feature || options.slug || '').trim();
9
+
10
+ if (!slug) {
11
+ if (options.json) return { ok: false, error: 'missing_feature' };
12
+ logger.error('--feature=<slug> is required.');
13
+ return { ok: false, error: 'missing_feature' };
14
+ }
15
+
16
+ const report = await auditAcceptanceCriteriaTests(targetDir, slug);
17
+
18
+ if (options.json) {
19
+ logger.log(JSON.stringify(report, null, 2));
20
+ return report;
21
+ }
22
+
23
+ logger.log('');
24
+ logger.log(`AC test audit — ${slug}`);
25
+ logger.log('━'.repeat(45));
26
+ logger.log(`ACs: ${report.summary.covered}/${report.summary.acs_total} covered; tests scanned: ${report.summary.test_files_scanned}`);
27
+
28
+ if (report.summary.acs_total === 0) {
29
+ logger.log('No acceptance criteria IDs found in requirements, PRD, or conformance artifacts.');
30
+ } else {
31
+ for (const item of report.items) {
32
+ const mark = item.status === 'covered' ? '✓' : '✗';
33
+ const evidence = item.evidence.length
34
+ ? ` — ${item.evidence.map((e) => e.file).join(', ')}`
35
+ : '';
36
+ logger.log(` ${mark} ${item.ac}: ${item.status}${evidence}`);
37
+ }
38
+ }
39
+
40
+ logger.log('');
41
+ logger.log(report.ok ? 'Result: PASS' : `Result: BLOCKED — missing tests for ${report.missing.join(', ')}`);
42
+ return report;
43
+ }
44
+
45
+ module.exports = { runAcTestAudit };
@@ -19,23 +19,25 @@ const {
19
19
  parseFrontmatter,
20
20
  contextDir
21
21
  } = require('../preflight-engine');
22
+ const { AC_ID_RE } = require('../lib/ac-test-audit');
22
23
 
23
24
  const BAR = '━'.repeat(45);
25
+ const REQ_ID_RE = /\bREQ(?:-[A-Za-z0-9]+)+\b/g;
24
26
 
25
- function gateDisplay(gates) {
26
- const letters = { requirements: 'A', design: 'B', plan: 'C', execution: 'D' };
27
+ function gateDisplay(gates) {
28
+ const letters = { requirements: 'A', design: 'B', plan: 'C', execution: 'D' };
27
29
  return Object.entries(letters).map(([name, letter]) => {
28
30
  const status = gates[name];
29
31
  return status === 'approved' ? `${letter}✓` : `${letter}○`;
30
- }).join(' ');
31
- }
32
-
33
- function artifactDisplayName(artifact, fallbackName) {
34
- if (artifact && artifact.exists && artifact.path) return path.basename(artifact.path);
35
- return fallbackName;
36
- }
37
-
38
- async function runArtifactValidate({ args, options = {}, logger }) {
32
+ }).join(' ');
33
+ }
34
+
35
+ function artifactDisplayName(artifact, fallbackName) {
36
+ if (artifact && artifact.exists && artifact.path) return path.basename(artifact.path);
37
+ return fallbackName;
38
+ }
39
+
40
+ async function runArtifactValidate({ args, options = {}, logger }) {
39
41
  const targetDir = path.resolve(process.cwd(), args[0] || '.');
40
42
  const slug = options.feature ? String(options.feature) : null;
41
43
 
@@ -60,6 +62,9 @@ async function runArtifactValidate({ args, options = {}, logger }) {
60
62
  const sheldonReady = artifacts.sheldon_enrichment.exists
61
63
  ? (artifacts.sheldon_enrichment.frontmatter.readiness === 'ready_for_downstream' ? 'ready_for_downstream' : 'present')
62
64
  : null;
65
+ const sheldonValidationReady = artifacts.sheldon_validation.exists
66
+ ? (artifacts.sheldon_validation.frontmatter.verdict || artifacts.sheldon_validation.frontmatter.readiness || 'present')
67
+ : null;
63
68
 
64
69
  // Implementation plan status
65
70
  const planStatus = artifacts.implementation_plan.exists
@@ -70,19 +75,19 @@ async function runArtifactValidate({ args, options = {}, logger }) {
70
75
  // (REQ-SDLC-01), because feature contracts use slugged identifiers.
71
76
  let reqCount = null;
72
77
  if (artifacts.requirements.exists && artifacts.requirements.content) {
73
- const reqs = artifacts.requirements.content.match(/\bREQ(?:-[A-Z0-9]+)+\b/g) || [];
74
- const acs = artifacts.requirements.content.match(/\bAC(?:-[A-Z0-9]+)+\b/g) || [];
78
+ const reqs = artifacts.requirements.content.match(REQ_ID_RE) || [];
79
+ const acs = artifacts.requirements.content.match(AC_ID_RE) || [];
75
80
  reqCount = `${new Set(reqs).size} REQs, ${new Set(acs).size} ACs`;
76
81
  }
77
82
 
78
- // Conformance required?
79
- const conformanceRequired = classification === 'MEDIUM';
80
- const designDocRequired = classification === 'SMALL' || classification === 'MEDIUM';
81
- const designDocName = artifactDisplayName(artifacts.design_doc, `design-doc-${slug}.md`);
82
- const readinessName = artifactDisplayName(artifacts.readiness, `readiness-${slug}.md`);
83
-
84
- // Build chain items
85
- const chain = [
83
+ // Conformance required?
84
+ const conformanceRequired = classification === 'MEDIUM';
85
+ const designDocRequired = classification === 'SMALL' || classification === 'MEDIUM';
86
+ const designDocName = artifactDisplayName(artifacts.design_doc, `design-doc-${slug}.md`);
87
+ const readinessName = artifactDisplayName(artifacts.readiness, `readiness-${slug}.md`);
88
+
89
+ // Build chain items
90
+ const chain = [
86
91
  {
87
92
  name: 'project.context.md',
88
93
  exists: artifacts.project_context.exists,
@@ -104,6 +109,13 @@ async function runArtifactValidate({ args, options = {}, logger }) {
104
109
  required: false,
105
110
  indent: 1
106
111
  },
112
+ {
113
+ name: `sheldon-validation-${slug}.md`,
114
+ exists: artifacts.sheldon_validation.exists,
115
+ detail: sheldonValidationReady ? `verdict: ${sheldonValidationReady}` : 'MEDIUM readiness verdict when @sheldon runs',
116
+ required: false,
117
+ indent: 1
118
+ },
107
119
  {
108
120
  name: `requirements-${slug}.md`,
109
121
  exists: artifacts.requirements.exists,
@@ -118,29 +130,29 @@ async function runArtifactValidate({ args, options = {}, logger }) {
118
130
  required: true,
119
131
  indent: 1
120
132
  },
121
- {
122
- name: 'architecture.md',
123
- exists: artifacts.architecture.exists,
124
- detail: null,
125
- required: true,
126
- indent: 1
127
- },
128
- {
129
- name: designDocName,
130
- exists: artifacts.design_doc.exists,
131
- detail: designDocRequired ? 'pre-dev design governance contract' : `SMALL/MEDIUM only — NOT required for ${classification || 'MICRO'}`,
132
- required: designDocRequired,
133
- indent: 1
134
- },
135
- {
136
- name: readinessName,
137
- exists: artifacts.readiness.exists,
138
- detail: designDocRequired ? 'pre-dev readiness contract' : `SMALL/MEDIUM only — NOT required for ${classification || 'MICRO'}`,
139
- required: designDocRequired,
140
- indent: 1
141
- },
142
- {
143
- name: `implementation-plan-${slug}.md`,
133
+ {
134
+ name: 'architecture.md',
135
+ exists: artifacts.architecture.exists,
136
+ detail: null,
137
+ required: true,
138
+ indent: 1
139
+ },
140
+ {
141
+ name: designDocName,
142
+ exists: artifacts.design_doc.exists,
143
+ detail: designDocRequired ? 'pre-dev design governance contract' : `SMALL/MEDIUM only — NOT required for ${classification || 'MICRO'}`,
144
+ required: designDocRequired,
145
+ indent: 1
146
+ },
147
+ {
148
+ name: readinessName,
149
+ exists: artifacts.readiness.exists,
150
+ detail: designDocRequired ? 'pre-dev readiness contract' : `SMALL/MEDIUM only — NOT required for ${classification || 'MICRO'}`,
151
+ required: designDocRequired,
152
+ indent: 1
153
+ },
154
+ {
155
+ name: `implementation-plan-${slug}.md`,
144
156
  exists: artifacts.implementation_plan.exists,
145
157
  detail: planStatus ? `status: ${planStatus}` : null,
146
158
  required: true,
@@ -164,12 +176,12 @@ async function runArtifactValidate({ args, options = {}, logger }) {
164
176
  // Determine next_missing and next_agent (AC-SDLC-22)
165
177
  const ARTIFACT_OWNER_MAP = {
166
178
  'project.context.md': { agent: '@setup', reason: 'setup not complete' },
167
- [`prd-${slug}.md`]: { agent: '@product', reason: 'PRD not produced yet' },
168
- [`requirements-${slug}.md`]: { agent: '@analyst', reason: 'requirements not produced yet (Gate A)' },
169
- 'architecture.md': { agent: '@architect', reason: 'architecture not produced yet (Gate B)' },
170
- [designDocName]: { agent: '@discovery-design-doc', reason: 'design governance contract not produced yet' },
171
- [readinessName]: { agent: '@discovery-design-doc', reason: 'readiness contract not produced yet' },
172
- [`implementation-plan-${slug}.md`]: { agent: '@pm', reason: 'implementation plan not produced yet (Gate C)' },
179
+ [`prd-${slug}.md`]: { agent: '@product', reason: 'PRD not produced yet' },
180
+ [`requirements-${slug}.md`]: { agent: '@analyst', reason: 'requirements not produced yet (Gate A)' },
181
+ 'architecture.md': { agent: '@architect', reason: 'architecture not produced yet (Gate B)' },
182
+ [designDocName]: { agent: '@discovery-design-doc', reason: 'design governance contract not produced yet' },
183
+ [readinessName]: { agent: '@discovery-design-doc', reason: 'readiness contract not produced yet' },
184
+ [`implementation-plan-${slug}.md`]: { agent: '@pm', reason: 'implementation plan not produced yet (Gate C)' },
173
185
  [`spec-${slug}.md`]: { agent: '@analyst', reason: 'spec not produced yet — @analyst seeds the feature memory' },
174
186
  [`conformance-${slug}.yaml`]: { agent: '@analyst', reason: 'conformance contract missing — @analyst creates it for MEDIUM features' }
175
187
  };
@@ -116,6 +116,60 @@ const COMPLEXITY_SOME_PATTERNS = [
116
116
  /\b(notification|trigger|event)\b/gi
117
117
  ];
118
118
 
119
+ // Sensitive-surface floor (Gap 3B): a feature touching any of these surfaces is
120
+ // never MICRO. Mirrors the secure-tdd sensitive list in @dev. The floor can only
121
+ // RAISE the tier (MICRO -> SMALL); it never lowers it. Keep patterns tight — a
122
+ // false positive needlessly costs the SMALL chain. Tune as the project learns.
123
+ const SENSITIVE_SURFACE_PATTERNS = [
124
+ { surface: 'money', re: /\b(money|stripe|paypal|braintree|square|payments?|payouts?|refunds?|subscriptions?|billing|invoices?|credit card)\b/i },
125
+ { surface: 'auth', re: /\b(oauth|jwt|saml|sso|auth0|firebase auth|log[- ]?in|sign[- ]?in|sign[- ]?up|passwords?|authenticat\w*|2fa|mfa)\b/i },
126
+ { surface: 'authz', re: /\b(authoriz\w*|access control|role[- ]based|rbac|ownership|owner[- ]only|only the owner)\b/i },
127
+ { surface: 'uploads', re: /\b(file uploads?|uploads?|attachments?)\b/i },
128
+ { surface: 'external_url', re: /\b(webhooks?|callback urls?|ssrf|user[- ]?supplied urls?)\b/i },
129
+ { surface: 'secrets', re: /\b(secrets?|api keys?|credentials?|private key|access tokens?)\b/i },
130
+ { surface: 'sensitive_storage', re: /\b(pii|personal data|ssn|sensitive (data|storage|information))\b/i }
131
+ ];
132
+
133
+ function detectSensitiveSurfaces(content) {
134
+ const found = [];
135
+ for (const { surface, re } of SENSITIVE_SURFACE_PATTERNS) {
136
+ if (re.test(content)) found.push(surface);
137
+ }
138
+ return found;
139
+ }
140
+
141
+ // Explicit `sensitive_surfaces:` frontmatter override — additive, can only force
142
+ // the floor when content detection misses. Supports inline (`[a, b]` / `a, b`)
143
+ // and YAML block list forms.
144
+ function parseSensitiveSurfacesOverride(content) {
145
+ const fm = String(content || '').match(/^---\r?\n([\s\S]*?)\r?\n---/);
146
+ if (!fm) return [];
147
+ const body = fm[1];
148
+ const items = [];
149
+ const inline = body.match(/^sensitive_surfaces:[ \t]*(.+)$/m);
150
+ if (inline) {
151
+ inline[1].trim().replace(/^\[|\]$/g, '').split(',').forEach((s) => {
152
+ const v = s.trim().replace(/^["']|["']$/g, '');
153
+ if (v) items.push(v);
154
+ });
155
+ }
156
+ const block = body.match(/^sensitive_surfaces:[ \t]*\r?\n((?:[ \t]*-[ \t]*.+\r?\n?)+)/m);
157
+ if (block) {
158
+ block[1].split(/\r?\n/).forEach((line) => {
159
+ const m = line.match(/^[ \t]*-[ \t]*(.+)$/);
160
+ if (m) {
161
+ const v = m[1].trim().replace(/^["']|["']$/g, '');
162
+ if (v) items.push(v);
163
+ }
164
+ });
165
+ }
166
+ return items;
167
+ }
168
+
169
+ function applySensitiveFloor(classification) {
170
+ return classification === 'MICRO' ? 'SMALL' : classification;
171
+ }
172
+
119
173
  function analyzeContent(content) {
120
174
  // Count unique user types
121
175
  const userTypeSet = new Set();
@@ -185,6 +239,7 @@ async function runClassify({ args, options = {}, logger }) {
185
239
 
186
240
  let userTypeCount, integrationCount, complexityLevel;
187
241
  let sourceFile = null;
242
+ let content = null;
188
243
 
189
244
  if (interactive) {
190
245
  ({ userTypeCount, integrationCount, complexityLevel } = await runInteractive(logger));
@@ -199,7 +254,6 @@ async function runClassify({ args, options = {}, logger }) {
199
254
  ]
200
255
  : [path.join(dir, 'requirements.md'), path.join(dir, 'prd.md')];
201
256
 
202
- let content = null;
203
257
  for (const candidate of candidates) {
204
258
  content = await readFileSafe(candidate);
205
259
  if (content) { sourceFile = path.relative(targetDir, candidate); break; }
@@ -218,7 +272,19 @@ async function runClassify({ args, options = {}, logger }) {
218
272
  const intScore = scoreIntegrations(integrationCount);
219
273
  const cxScore = scoreComplexity(complexityLevel);
220
274
  const totalScore = utScore + intScore + cxScore;
221
- const classification = scoreToClassification(totalScore);
275
+ let classification = scoreToClassification(totalScore);
276
+
277
+ // Gap 3B — sensitive-surface floor (deterministic; raises MICRO -> SMALL only).
278
+ const detectedSurfaces = content ? detectSensitiveSurfaces(content) : [];
279
+ const declaredSurfaces = content ? parseSensitiveSurfacesOverride(content) : [];
280
+ const sensitiveSurfaces = [...new Set([...detectedSurfaces, ...declaredSurfaces])];
281
+ let floored = false;
282
+ if (sensitiveSurfaces.length > 0) {
283
+ const scored = classification;
284
+ classification = applySensitiveFloor(classification);
285
+ floored = classification !== scored;
286
+ }
287
+
222
288
  const phaseDepth = classificationToPhaseDepth(classification);
223
289
 
224
290
  const result = {
@@ -228,6 +294,8 @@ async function runClassify({ args, options = {}, logger }) {
228
294
  inputs: { user_types: userTypeCount, external_integrations: integrationCount, rule_complexity: complexityLevel },
229
295
  scores: { user_types: utScore, integrations: intScore, complexity: cxScore, total: totalScore },
230
296
  classification,
297
+ sensitive_surfaces: sensitiveSurfaces,
298
+ floored,
231
299
  phase_depth: phaseDepth
232
300
  };
233
301
 
@@ -243,6 +311,9 @@ async function runClassify({ args, options = {}, logger }) {
243
311
  logger.log(`Business rule complexity: ${complexityLevel} → +${cxScore}`);
244
312
  logger.log(BAR);
245
313
  logger.log(`Score: ${totalScore} → ${classification}`);
314
+ if (sensitiveSurfaces.length > 0) {
315
+ logger.log(`Sensitive surfaces: ${sensitiveSurfaces.join(', ')}${floored ? ' → floored to SMALL' : ''}`);
316
+ }
246
317
  logger.log('');
247
318
  logger.log('Phase depth:');
248
319
  for (const [phase, desc] of Object.entries(phaseDepth)) {
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const { buildContextBrief } = require('../context-brief');
5
+
6
+ async function runContextBrief({ args, options = {}, logger }) {
7
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
8
+ const result = await buildContextBrief(targetDir, {
9
+ agent: options.agent || options.a || 'dev',
10
+ mode: options.mode || 'planning',
11
+ task: options.task || options.goal || '',
12
+ paths: options.paths || options.path || '',
13
+ feature: options.feature || options.slug || '',
14
+ semantic: options.semantic,
15
+ noSemantic: options.noSemantic || options['no-semantic'],
16
+ recall: !(options['no-recall'] || options.recall === false)
17
+ });
18
+
19
+ if (options.json) return result;
20
+
21
+ logger.log(`Context brief for @${result.agent} (${result.mode})`);
22
+ if (result.task) logger.log(`Task: ${result.task}`);
23
+ logger.log(`Intent: ${result.intent.operation}${result.intent.stack ? ` / ${result.intent.stack}` : ''}`);
24
+ if (result.intent.concerns.length > 0) logger.log(`Concerns: ${result.intent.concerns.join(', ')}`);
25
+ logger.log(`Confidence: ${result.confidence}`);
26
+
27
+ if (result.must_load.length > 0) {
28
+ logger.log('Must load:');
29
+ for (const item of result.must_load) logger.log(`- ${item.path} [${item.surface}] ${item.reason}`);
30
+ }
31
+ if (result.should_load.length > 0) {
32
+ logger.log('Should load when needed:');
33
+ for (const item of result.should_load) logger.log(`- ${item.path} [${item.surface}] ${item.reason}`);
34
+ }
35
+ if (result.constraints.length > 0) {
36
+ logger.log('Constraints:');
37
+ for (const item of result.constraints.slice(0, 8)) logger.log(`- ${item}`);
38
+ }
39
+ if (result.forbidden_patterns.length > 0) {
40
+ logger.log('Forbidden patterns:');
41
+ for (const item of result.forbidden_patterns.slice(0, 8)) logger.log(`- ${item}`);
42
+ }
43
+ if (result.verification_hints.length > 0) {
44
+ logger.log('Verification hints:');
45
+ for (const item of result.verification_hints.slice(0, 8)) logger.log(`- ${item}`);
46
+ }
47
+ if (result.gaps.length > 0) {
48
+ logger.log('Gaps:');
49
+ for (const gap of result.gaps) logger.log(`- ${gap.code}: ${gap.message}`);
50
+ }
51
+ if (result.related && result.related.length > 0) {
52
+ logger.log('Related (recall — history/archive select cannot see):');
53
+ for (const item of result.related) logger.log(`- ${item.path} [${item.source_type}] ${item.reason || ''}`);
54
+ }
55
+
56
+ return result;
57
+ }
58
+
59
+ module.exports = { runContextBrief };