cap-pro 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/.claude-plugin/README.md +26 -0
  2. package/.claude-plugin/marketplace.json +24 -0
  3. package/.claude-plugin/plugin.json +24 -0
  4. package/LICENSE +21 -0
  5. package/README.ja-JP.md +834 -0
  6. package/README.ko-KR.md +823 -0
  7. package/README.md +806 -0
  8. package/README.pt-BR.md +452 -0
  9. package/README.zh-CN.md +800 -0
  10. package/agents/cap-architect.md +269 -0
  11. package/agents/cap-brainstormer.md +207 -0
  12. package/agents/cap-curator.md +276 -0
  13. package/agents/cap-debugger.md +365 -0
  14. package/agents/cap-designer.md +246 -0
  15. package/agents/cap-historian.md +464 -0
  16. package/agents/cap-migrator.md +291 -0
  17. package/agents/cap-prototyper.md +197 -0
  18. package/agents/cap-validator.md +308 -0
  19. package/bin/install.js +5433 -0
  20. package/cap/bin/cap-tools.cjs +853 -0
  21. package/cap/bin/lib/arc-scanner.cjs +344 -0
  22. package/cap/bin/lib/cap-affinity-engine.cjs +862 -0
  23. package/cap/bin/lib/cap-anchor.cjs +228 -0
  24. package/cap/bin/lib/cap-annotation-writer.cjs +340 -0
  25. package/cap/bin/lib/cap-checkpoint.cjs +434 -0
  26. package/cap/bin/lib/cap-cluster-detect.cjs +945 -0
  27. package/cap/bin/lib/cap-cluster-display.cjs +52 -0
  28. package/cap/bin/lib/cap-cluster-format.cjs +245 -0
  29. package/cap/bin/lib/cap-cluster-helpers.cjs +295 -0
  30. package/cap/bin/lib/cap-cluster-io.cjs +212 -0
  31. package/cap/bin/lib/cap-completeness.cjs +540 -0
  32. package/cap/bin/lib/cap-deps.cjs +583 -0
  33. package/cap/bin/lib/cap-design-families.cjs +332 -0
  34. package/cap/bin/lib/cap-design.cjs +966 -0
  35. package/cap/bin/lib/cap-divergence-detector.cjs +400 -0
  36. package/cap/bin/lib/cap-doctor.cjs +752 -0
  37. package/cap/bin/lib/cap-feature-map-internals.cjs +19 -0
  38. package/cap/bin/lib/cap-feature-map-migrate.cjs +335 -0
  39. package/cap/bin/lib/cap-feature-map-monorepo.cjs +885 -0
  40. package/cap/bin/lib/cap-feature-map-shard.cjs +315 -0
  41. package/cap/bin/lib/cap-feature-map.cjs +1943 -0
  42. package/cap/bin/lib/cap-fitness-score.cjs +1075 -0
  43. package/cap/bin/lib/cap-impact-analysis.cjs +652 -0
  44. package/cap/bin/lib/cap-learn-review.cjs +1072 -0
  45. package/cap/bin/lib/cap-learning-signals.cjs +627 -0
  46. package/cap/bin/lib/cap-loader.cjs +227 -0
  47. package/cap/bin/lib/cap-logger.cjs +57 -0
  48. package/cap/bin/lib/cap-memory-bridge.cjs +764 -0
  49. package/cap/bin/lib/cap-memory-confidence.cjs +452 -0
  50. package/cap/bin/lib/cap-memory-dir.cjs +987 -0
  51. package/cap/bin/lib/cap-memory-engine.cjs +698 -0
  52. package/cap/bin/lib/cap-memory-extends.cjs +398 -0
  53. package/cap/bin/lib/cap-memory-graph.cjs +790 -0
  54. package/cap/bin/lib/cap-memory-migrate.cjs +2015 -0
  55. package/cap/bin/lib/cap-memory-pin.cjs +183 -0
  56. package/cap/bin/lib/cap-memory-platform.cjs +490 -0
  57. package/cap/bin/lib/cap-memory-prune.cjs +707 -0
  58. package/cap/bin/lib/cap-memory-schema.cjs +812 -0
  59. package/cap/bin/lib/cap-migrate-tags.cjs +309 -0
  60. package/cap/bin/lib/cap-migrate.cjs +540 -0
  61. package/cap/bin/lib/cap-pattern-apply.cjs +1203 -0
  62. package/cap/bin/lib/cap-pattern-pipeline.cjs +1034 -0
  63. package/cap/bin/lib/cap-plugin-manifest.cjs +80 -0
  64. package/cap/bin/lib/cap-realtime-affinity.cjs +399 -0
  65. package/cap/bin/lib/cap-reconcile.cjs +570 -0
  66. package/cap/bin/lib/cap-research-gate.cjs +218 -0
  67. package/cap/bin/lib/cap-scope-filter.cjs +402 -0
  68. package/cap/bin/lib/cap-semantic-pipeline.cjs +1038 -0
  69. package/cap/bin/lib/cap-session-extract.cjs +987 -0
  70. package/cap/bin/lib/cap-session.cjs +445 -0
  71. package/cap/bin/lib/cap-snapshot-linkage.cjs +963 -0
  72. package/cap/bin/lib/cap-stack-docs.cjs +646 -0
  73. package/cap/bin/lib/cap-tag-observer.cjs +371 -0
  74. package/cap/bin/lib/cap-tag-scanner.cjs +1766 -0
  75. package/cap/bin/lib/cap-telemetry.cjs +466 -0
  76. package/cap/bin/lib/cap-test-audit.cjs +1438 -0
  77. package/cap/bin/lib/cap-thread-migrator.cjs +307 -0
  78. package/cap/bin/lib/cap-thread-synthesis.cjs +545 -0
  79. package/cap/bin/lib/cap-thread-tracker.cjs +519 -0
  80. package/cap/bin/lib/cap-trace.cjs +399 -0
  81. package/cap/bin/lib/cap-trust-mode.cjs +336 -0
  82. package/cap/bin/lib/cap-ui-design-editor.cjs +642 -0
  83. package/cap/bin/lib/cap-ui-mind-map.cjs +712 -0
  84. package/cap/bin/lib/cap-ui-thread-nav.cjs +693 -0
  85. package/cap/bin/lib/cap-ui.cjs +1245 -0
  86. package/cap/bin/lib/cap-upgrade.cjs +1028 -0
  87. package/cap/bin/lib/cli/arg-helpers.cjs +49 -0
  88. package/cap/bin/lib/cli/frontmatter-router.cjs +31 -0
  89. package/cap/bin/lib/cli/init-router.cjs +68 -0
  90. package/cap/bin/lib/cli/phase-router.cjs +102 -0
  91. package/cap/bin/lib/cli/state-router.cjs +61 -0
  92. package/cap/bin/lib/cli/template-router.cjs +37 -0
  93. package/cap/bin/lib/cli/uat-router.cjs +29 -0
  94. package/cap/bin/lib/cli/validation-router.cjs +26 -0
  95. package/cap/bin/lib/cli/verification-router.cjs +31 -0
  96. package/cap/bin/lib/cli/workstream-router.cjs +39 -0
  97. package/cap/bin/lib/commands.cjs +961 -0
  98. package/cap/bin/lib/config.cjs +467 -0
  99. package/cap/bin/lib/convention-reader.cjs +258 -0
  100. package/cap/bin/lib/core.cjs +1241 -0
  101. package/cap/bin/lib/feature-aggregator.cjs +423 -0
  102. package/cap/bin/lib/frontmatter.cjs +337 -0
  103. package/cap/bin/lib/init.cjs +1443 -0
  104. package/cap/bin/lib/manifest-generator.cjs +383 -0
  105. package/cap/bin/lib/milestone.cjs +253 -0
  106. package/cap/bin/lib/model-profiles.cjs +69 -0
  107. package/cap/bin/lib/monorepo-context.cjs +226 -0
  108. package/cap/bin/lib/monorepo-migrator.cjs +509 -0
  109. package/cap/bin/lib/phase.cjs +889 -0
  110. package/cap/bin/lib/profile-output.cjs +989 -0
  111. package/cap/bin/lib/profile-pipeline.cjs +540 -0
  112. package/cap/bin/lib/roadmap.cjs +330 -0
  113. package/cap/bin/lib/security.cjs +394 -0
  114. package/cap/bin/lib/session-manager.cjs +292 -0
  115. package/cap/bin/lib/skeleton-generator.cjs +179 -0
  116. package/cap/bin/lib/state.cjs +1032 -0
  117. package/cap/bin/lib/template.cjs +231 -0
  118. package/cap/bin/lib/test-detector.cjs +62 -0
  119. package/cap/bin/lib/uat.cjs +283 -0
  120. package/cap/bin/lib/verify.cjs +889 -0
  121. package/cap/bin/lib/workspace-detector.cjs +371 -0
  122. package/cap/bin/lib/workstream.cjs +492 -0
  123. package/cap/commands/gsd/workstreams.md +63 -0
  124. package/cap/references/arc-standard.md +315 -0
  125. package/cap/references/cap-agent-architecture.md +101 -0
  126. package/cap/references/cap-gitignore-template +9 -0
  127. package/cap/references/cap-zero-deps.md +158 -0
  128. package/cap/references/checkpoints.md +778 -0
  129. package/cap/references/continuation-format.md +249 -0
  130. package/cap/references/contract-test-templates.md +312 -0
  131. package/cap/references/feature-map-template.md +25 -0
  132. package/cap/references/git-integration.md +295 -0
  133. package/cap/references/git-planning-commit.md +38 -0
  134. package/cap/references/model-profiles.md +174 -0
  135. package/cap/references/phase-numbering.md +126 -0
  136. package/cap/references/planning-config.md +202 -0
  137. package/cap/references/property-test-templates.md +316 -0
  138. package/cap/references/security-test-templates.md +347 -0
  139. package/cap/references/session-template.json +8 -0
  140. package/cap/references/tdd.md +263 -0
  141. package/cap/references/user-profiling.md +681 -0
  142. package/cap/references/verification-patterns.md +612 -0
  143. package/cap/templates/UAT.md +265 -0
  144. package/cap/templates/claude-md.md +175 -0
  145. package/cap/templates/codebase/architecture.md +255 -0
  146. package/cap/templates/codebase/concerns.md +310 -0
  147. package/cap/templates/codebase/conventions.md +307 -0
  148. package/cap/templates/codebase/integrations.md +280 -0
  149. package/cap/templates/codebase/stack.md +186 -0
  150. package/cap/templates/codebase/structure.md +285 -0
  151. package/cap/templates/codebase/testing.md +480 -0
  152. package/cap/templates/config.json +44 -0
  153. package/cap/templates/context.md +352 -0
  154. package/cap/templates/continue-here.md +78 -0
  155. package/cap/templates/copilot-instructions.md +7 -0
  156. package/cap/templates/debug-subagent-prompt.md +91 -0
  157. package/cap/templates/discussion-log.md +63 -0
  158. package/cap/templates/milestone-archive.md +123 -0
  159. package/cap/templates/milestone.md +115 -0
  160. package/cap/templates/phase-prompt.md +610 -0
  161. package/cap/templates/planner-subagent-prompt.md +117 -0
  162. package/cap/templates/project.md +186 -0
  163. package/cap/templates/requirements.md +231 -0
  164. package/cap/templates/research-project/ARCHITECTURE.md +204 -0
  165. package/cap/templates/research-project/FEATURES.md +147 -0
  166. package/cap/templates/research-project/PITFALLS.md +200 -0
  167. package/cap/templates/research-project/STACK.md +120 -0
  168. package/cap/templates/research-project/SUMMARY.md +170 -0
  169. package/cap/templates/research.md +552 -0
  170. package/cap/templates/roadmap.md +202 -0
  171. package/cap/templates/state.md +176 -0
  172. package/cap/templates/summary.md +364 -0
  173. package/cap/templates/user-preferences.md +498 -0
  174. package/cap/templates/verification-report.md +322 -0
  175. package/cap/workflows/add-phase.md +112 -0
  176. package/cap/workflows/add-tests.md +351 -0
  177. package/cap/workflows/add-todo.md +158 -0
  178. package/cap/workflows/audit-milestone.md +340 -0
  179. package/cap/workflows/audit-uat.md +109 -0
  180. package/cap/workflows/autonomous.md +891 -0
  181. package/cap/workflows/check-todos.md +177 -0
  182. package/cap/workflows/cleanup.md +152 -0
  183. package/cap/workflows/complete-milestone.md +767 -0
  184. package/cap/workflows/diagnose-issues.md +231 -0
  185. package/cap/workflows/discovery-phase.md +289 -0
  186. package/cap/workflows/discuss-phase-assumptions.md +653 -0
  187. package/cap/workflows/discuss-phase.md +1049 -0
  188. package/cap/workflows/do.md +104 -0
  189. package/cap/workflows/execute-phase.md +846 -0
  190. package/cap/workflows/execute-plan.md +514 -0
  191. package/cap/workflows/fast.md +105 -0
  192. package/cap/workflows/forensics.md +265 -0
  193. package/cap/workflows/health.md +181 -0
  194. package/cap/workflows/help.md +660 -0
  195. package/cap/workflows/insert-phase.md +130 -0
  196. package/cap/workflows/list-phase-assumptions.md +178 -0
  197. package/cap/workflows/list-workspaces.md +56 -0
  198. package/cap/workflows/manager.md +362 -0
  199. package/cap/workflows/map-codebase.md +377 -0
  200. package/cap/workflows/milestone-summary.md +223 -0
  201. package/cap/workflows/new-milestone.md +486 -0
  202. package/cap/workflows/new-project.md +1250 -0
  203. package/cap/workflows/new-workspace.md +237 -0
  204. package/cap/workflows/next.md +97 -0
  205. package/cap/workflows/node-repair.md +92 -0
  206. package/cap/workflows/note.md +156 -0
  207. package/cap/workflows/pause-work.md +176 -0
  208. package/cap/workflows/plan-milestone-gaps.md +273 -0
  209. package/cap/workflows/plan-phase.md +857 -0
  210. package/cap/workflows/plant-seed.md +169 -0
  211. package/cap/workflows/pr-branch.md +129 -0
  212. package/cap/workflows/profile-user.md +449 -0
  213. package/cap/workflows/progress.md +507 -0
  214. package/cap/workflows/quick.md +757 -0
  215. package/cap/workflows/remove-phase.md +155 -0
  216. package/cap/workflows/remove-workspace.md +90 -0
  217. package/cap/workflows/research-phase.md +82 -0
  218. package/cap/workflows/resume-project.md +326 -0
  219. package/cap/workflows/review.md +228 -0
  220. package/cap/workflows/session-report.md +146 -0
  221. package/cap/workflows/settings.md +283 -0
  222. package/cap/workflows/ship.md +228 -0
  223. package/cap/workflows/stats.md +60 -0
  224. package/cap/workflows/transition.md +671 -0
  225. package/cap/workflows/ui-phase.md +298 -0
  226. package/cap/workflows/ui-review.md +161 -0
  227. package/cap/workflows/update.md +323 -0
  228. package/cap/workflows/validate-phase.md +170 -0
  229. package/cap/workflows/verify-phase.md +254 -0
  230. package/cap/workflows/verify-work.md +637 -0
  231. package/commands/cap/annotate.md +165 -0
  232. package/commands/cap/brainstorm.md +393 -0
  233. package/commands/cap/checkpoint.md +106 -0
  234. package/commands/cap/completeness.md +94 -0
  235. package/commands/cap/continue.md +72 -0
  236. package/commands/cap/debug.md +588 -0
  237. package/commands/cap/deps.md +169 -0
  238. package/commands/cap/design.md +479 -0
  239. package/commands/cap/init.md +354 -0
  240. package/commands/cap/iterate.md +249 -0
  241. package/commands/cap/learn.md +459 -0
  242. package/commands/cap/memory.md +275 -0
  243. package/commands/cap/migrate-feature-map.md +91 -0
  244. package/commands/cap/migrate-memory.md +108 -0
  245. package/commands/cap/migrate-tags.md +91 -0
  246. package/commands/cap/migrate.md +131 -0
  247. package/commands/cap/prototype.md +510 -0
  248. package/commands/cap/reconcile.md +121 -0
  249. package/commands/cap/review.md +360 -0
  250. package/commands/cap/save.md +72 -0
  251. package/commands/cap/scan.md +404 -0
  252. package/commands/cap/start.md +356 -0
  253. package/commands/cap/status.md +118 -0
  254. package/commands/cap/test-audit.md +262 -0
  255. package/commands/cap/test.md +394 -0
  256. package/commands/cap/trace.md +133 -0
  257. package/commands/cap/ui.md +167 -0
  258. package/hooks/dist/cap-check-update.js +115 -0
  259. package/hooks/dist/cap-context-monitor.js +185 -0
  260. package/hooks/dist/cap-learn-review-hook.js +114 -0
  261. package/hooks/dist/cap-learning-hook.js +192 -0
  262. package/hooks/dist/cap-memory.js +299 -0
  263. package/hooks/dist/cap-prompt-guard.js +97 -0
  264. package/hooks/dist/cap-statusline.js +157 -0
  265. package/hooks/dist/cap-tag-observer.js +115 -0
  266. package/hooks/dist/cap-version-check.js +112 -0
  267. package/hooks/dist/cap-workflow-guard.js +175 -0
  268. package/hooks/hooks.json +55 -0
  269. package/package.json +58 -0
  270. package/scripts/base64-scan.sh +262 -0
  271. package/scripts/build-hooks.js +93 -0
  272. package/scripts/cap-removal-checklist.md +202 -0
  273. package/scripts/prompt-injection-scan.sh +199 -0
  274. package/scripts/run-tests.cjs +181 -0
  275. package/scripts/secret-scan.sh +227 -0
@@ -0,0 +1,336 @@
1
+ // @cap-feature(feature:F-075, primary:true) Trust-Mode Configuration Slot — open-closed for future B/C activation.
2
+ // @cap-context Trust-Mode is the first-class config axis for V5-true Self-Learning. MVP only enables A.
3
+ // F-071 (Pattern Extraction), F-073 (Review Board), F-074 (Apply/Unlearn) will consume this helper
4
+ // via getTrustMode() + requireApproval() so B/C activation is a library-internal change only.
5
+ // @cap-decision(F-075/D1) .cap/config.json is source-of-truth for trustMode; SESSION.json mirrors per-session.
6
+ // On divergence, config wins and SESSION.json is aligned on next read.
7
+ // @cap-decision(F-075/D2) getTrustMode returns { mode, source, degraded? } for debug transparency.
8
+ // Shape is frozen from MVP onward — B/C activation MUST NOT change the return shape.
9
+ // @cap-decision(F-075/D3) requireApproval(projectRoot) is the open-closed extension point for B/C.
10
+ // MVP: always true. Future B: scope-aware. Future C: returns false.
11
+ // @cap-decision(F-075/D4) Non-A degradation warning rate-limited to 1× per process via in-memory Set-memo.
12
+ // Keyed by projectRoot + raw value so distinct degradations still log once each.
13
+ // @cap-constraint Zero external dependencies — uses only Node.js built-ins (fs, path, os, crypto).
14
+ // @cap-pattern All trust-mode reads/writes go through this module — no other code touches trustMode directly.
15
+
16
+ 'use strict';
17
+
18
+ const fs = require('node:fs');
19
+ const path = require('node:path');
20
+ const crypto = require('node:crypto');
21
+
22
+ // -----------------------------------------------------------------------------
23
+ // Constants
24
+ // -----------------------------------------------------------------------------
25
+
26
+ const CAP_DIR = '.cap';
27
+ const CONFIG_FILE = 'config.json';
28
+
29
+ // @cap-decision(F-075/D2) VALID_MODES is the single authoritative set. Adding D/E here
30
+ // is the ONLY code change needed when the mode axis ever extends.
31
+ const VALID_MODES = Object.freeze(['A', 'B', 'C']);
32
+ const DEFAULT_MODE = 'A';
33
+
34
+ // @cap-decision(F-075/D5) Every TrustModeResult returned to a caller is Object.freeze-d at the
35
+ // boundary. Consumers (F-071/F-073/F-074) must not be able to mutate the
36
+ // object in a way that alters later reads or a shared memo. Defense-in-depth
37
+ // for the open-closed contract: even buggy consumer code can't silently
38
+ // flip mode to 'C' mid-pipeline.
39
+ function _frozenResult(obj) {
40
+ return Object.freeze(obj);
41
+ }
42
+
43
+ // @cap-decision(F-075/D4) Warning rate-limit memo is process-local (not persisted).
44
+ // Each fresh node process logs once per (projectRoot, rawValue) combo.
45
+ const _warnOnceMemo = new Set();
46
+
47
+ const WARN_CODE = 'trust-mode-not-implemented';
48
+
49
+ // -----------------------------------------------------------------------------
50
+ // Types
51
+ // -----------------------------------------------------------------------------
52
+
53
+ /**
54
+ * @typedef {'A'|'B'|'C'} TrustMode
55
+ *
56
+ * @typedef {Object} TrustModeResult
57
+ * @property {TrustMode} mode - Effective trust mode (always 'A' in MVP).
58
+ * @property {'config'|'session'|'default'} source - Where the value came from.
59
+ * @property {boolean} [degraded] - Present and true when a non-A value was read and degraded to A.
60
+ * @property {*} [rawValue] - The raw value seen in config/session when degraded.
61
+ */
62
+
63
+ // -----------------------------------------------------------------------------
64
+ // Validation
65
+ // -----------------------------------------------------------------------------
66
+
67
+ // @cap-todo(ac:F-075/AC-3) Non-A degradation — isValidMode is the gate that triggers the downgrade branch.
68
+ /**
69
+ * Check whether `value` is one of the allowed trust-mode strings.
70
+ * Strict equality — no lowercasing, no trimming. 'a' is NOT valid, only 'A'.
71
+ * @param {*} value
72
+ * @returns {boolean}
73
+ */
74
+ function isValidMode(value) {
75
+ return typeof value === 'string' && VALID_MODES.includes(value);
76
+ }
77
+
78
+ // -----------------------------------------------------------------------------
79
+ // Config I/O (private)
80
+ // -----------------------------------------------------------------------------
81
+
82
+ /**
83
+ * Read .cap/config.json. Returns `{}` when missing or malformed (no throw).
84
+ * Mirrors cap-telemetry's readConfig pattern for behavioural consistency.
85
+ * @param {string} projectRoot
86
+ * @returns {object}
87
+ */
88
+ function _readConfig(projectRoot) {
89
+ const configPath = path.join(projectRoot, CAP_DIR, CONFIG_FILE);
90
+ try {
91
+ if (!fs.existsSync(configPath)) return {};
92
+ const raw = fs.readFileSync(configPath, 'utf8');
93
+ const parsed = JSON.parse(raw);
94
+ // @cap-decision(F-075/D1) Non-object roots (strings, arrays, null) normalise to {} so that
95
+ // cfg.trustMode cannot throw. Matches cap-telemetry D5.
96
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return {};
97
+ return parsed;
98
+ } catch (_e) {
99
+ return {};
100
+ }
101
+ }
102
+
103
+ // @cap-todo(ac:F-075/AC-2) Persist trustMode across sessions via atomic write to .cap/config.json.
104
+ /**
105
+ * Atomically write `config` to .cap/config.json. Creates .cap/ if needed.
106
+ * Uses temp+rename so a crashed write cannot leave a half-written JSON file.
107
+ * @param {string} projectRoot
108
+ * @param {object} config
109
+ */
110
+ function _writeConfig(projectRoot, config) {
111
+ const capDir = path.join(projectRoot, CAP_DIR);
112
+ if (!fs.existsSync(capDir)) fs.mkdirSync(capDir, { recursive: true });
113
+
114
+ const configPath = path.join(capDir, CONFIG_FILE);
115
+ // Unique tmp suffix avoids clashes under concurrent writes from the same process.
116
+ const tmpPath = path.join(
117
+ capDir,
118
+ `.${CONFIG_FILE}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.tmp`,
119
+ );
120
+ const payload = JSON.stringify(config, null, 2) + '\n';
121
+ fs.writeFileSync(tmpPath, payload, 'utf8');
122
+ fs.renameSync(tmpPath, configPath);
123
+ }
124
+
125
+ // -----------------------------------------------------------------------------
126
+ // Warning emission (private)
127
+ // -----------------------------------------------------------------------------
128
+
129
+ // @cap-todo(ac:F-075/AC-3) Warning rate-limit — one stderr line per (projectRoot, rawValue) per process.
130
+ /**
131
+ * Emit a single-line stderr warning for non-A degradation, rate-limited to once per
132
+ * (projectRoot, rawValue) combination per Node process.
133
+ * @param {string} projectRoot
134
+ * @param {*} rawValue
135
+ */
136
+ function _warnOnce(projectRoot, rawValue) {
137
+ // JSON.stringify is the cheapest stable key; handles objects, nulls, numbers, booleans.
138
+ let keyPart;
139
+ try {
140
+ keyPart = JSON.stringify(rawValue);
141
+ } catch (_e) {
142
+ keyPart = String(rawValue);
143
+ }
144
+ const memoKey = `${projectRoot}::${keyPart}`;
145
+ if (_warnOnceMemo.has(memoKey)) return;
146
+ _warnOnceMemo.add(memoKey);
147
+
148
+ const ts = new Date().toISOString();
149
+ const displayValue = keyPart === undefined ? 'undefined' : keyPart;
150
+ // Single line — grep-friendly. Code token is stable and documented.
151
+ // eslint-disable-next-line no-console
152
+ console.error(
153
+ `[${ts}] ${WARN_CODE} trustMode=${displayValue} — MVP only supports 'A'; degrading to 'A'.`,
154
+ );
155
+ }
156
+
157
+ /**
158
+ * Test-only hook to clear the warn-once memo so unit tests can assert rate-limit behaviour
159
+ * across multiple invocations without spawning child processes.
160
+ * @private
161
+ */
162
+ function _resetWarnOnceForTests() {
163
+ _warnOnceMemo.clear();
164
+ }
165
+
166
+ // -----------------------------------------------------------------------------
167
+ // Session sync (private)
168
+ // -----------------------------------------------------------------------------
169
+
170
+ // @cap-todo(ac:F-075/AC-1) SESSION.json must carry trustMode field; this helper aligns it with config.
171
+ /**
172
+ * Align SESSION.json.trustMode with the resolved mode, if SESSION.json exists.
173
+ * Failures are silent — SESSION.json is ephemeral and its absence/corruption must
174
+ * never break the read path. Uses cap-session via lazy require to avoid a hard
175
+ * cycle (cap-session re-exports getTrustMode for convenience).
176
+ * @param {string} projectRoot
177
+ * @param {TrustMode} resolvedMode
178
+ */
179
+ function _syncSession(projectRoot, resolvedMode) {
180
+ try {
181
+ // Lazy require — cap-session may later want to import cap-trust-mode; keep the
182
+ // edge loose so require-order never matters.
183
+ const session = require('./cap-session.cjs');
184
+ const current = session.loadSession(projectRoot);
185
+ if (current.trustMode !== resolvedMode) {
186
+ session.updateSession(projectRoot, { trustMode: resolvedMode });
187
+ }
188
+ } catch (_e) {
189
+ // Silent — SESSION mirror is a debug affordance, not a correctness contract.
190
+ }
191
+ }
192
+
193
+ // -----------------------------------------------------------------------------
194
+ // Public API
195
+ // -----------------------------------------------------------------------------
196
+
197
+ // @cap-todo(ac:F-075/AC-1) SESSION.json trustMode field — this helper is the read path.
198
+ // @cap-todo(ac:F-075/AC-2) .cap/config.json persistence — config is read as source-of-truth.
199
+ // @cap-todo(ac:F-075/AC-3) Non-A degradation — invalid values are coerced to 'A' with rate-limited warning.
200
+ // @cap-todo(ac:F-075/AC-5) Determinism — pure function of on-disk state; same inputs, same output.
201
+ // @cap-todo(ac:F-075/AC-6) Single helper exposed to all Learn-Pipeline consumers (F-071, F-073, F-074).
202
+ // @cap-todo(ac:F-075/AC-7) Open-closed — only the return value of this function changes for B/C activation;
203
+ // the shape stays frozen. Feature code MUST NOT read config/session directly.
204
+ /**
205
+ * Resolve the effective trust mode for `projectRoot`.
206
+ *
207
+ * Precedence:
208
+ * 1. .cap/config.json → `trustMode` (source-of-truth)
209
+ * 2. SESSION.json → `trustMode` (per-session cache)
210
+ * 3. DEFAULT_MODE ('A')
211
+ *
212
+ * Any non-valid mode is degraded to 'A' with `degraded:true` and a rate-limited
213
+ * stderr warning. In the MVP this always resolves to 'A'.
214
+ *
215
+ * @param {string} projectRoot - Absolute path to project root.
216
+ * @returns {TrustModeResult}
217
+ */
218
+ function getTrustMode(projectRoot) {
219
+ const cfg = _readConfig(projectRoot);
220
+
221
+ // 1. Config-sourced
222
+ if (Object.prototype.hasOwnProperty.call(cfg, 'trustMode')) {
223
+ const raw = cfg.trustMode;
224
+ if (isValidMode(raw)) {
225
+ // @cap-decision(F-075/D2) MVP hard-caps to A even when B/C is a syntactically valid mode,
226
+ // so accidental config edits do not silently unlock unsupported behaviour.
227
+ // When B/C is implemented, drop the cap but keep the shape.
228
+ if (raw === DEFAULT_MODE) {
229
+ _syncSession(projectRoot, DEFAULT_MODE);
230
+ return _frozenResult({ mode: DEFAULT_MODE, source: 'config' });
231
+ }
232
+ // raw is 'B' or 'C' — degrade with warning
233
+ _warnOnce(projectRoot, raw);
234
+ _syncSession(projectRoot, DEFAULT_MODE);
235
+ return _frozenResult({ mode: DEFAULT_MODE, source: 'config', degraded: true, rawValue: raw });
236
+ }
237
+ // Invalid (wrong type/case/etc.) — degrade with warning
238
+ _warnOnce(projectRoot, raw);
239
+ _syncSession(projectRoot, DEFAULT_MODE);
240
+ return _frozenResult({ mode: DEFAULT_MODE, source: 'config', degraded: true, rawValue: raw });
241
+ }
242
+
243
+ // 2. Session-sourced — only counts when SESSION.json actually exists on disk,
244
+ // otherwise loadSession() returns the default stub and we'd mis-attribute the source.
245
+ const sessionPath = path.join(projectRoot, CAP_DIR, 'SESSION.json');
246
+ if (fs.existsSync(sessionPath)) {
247
+ try {
248
+ const session = require('./cap-session.cjs');
249
+ const sess = session.loadSession(projectRoot);
250
+ if (Object.prototype.hasOwnProperty.call(sess, 'trustMode') && sess.trustMode != null) {
251
+ const raw = sess.trustMode;
252
+ if (isValidMode(raw) && raw === DEFAULT_MODE) {
253
+ return _frozenResult({ mode: DEFAULT_MODE, source: 'session' });
254
+ }
255
+ // Valid-but-unsupported (B/C) or plain invalid — both degrade with warning.
256
+ _warnOnce(projectRoot, raw);
257
+ _syncSession(projectRoot, DEFAULT_MODE);
258
+ return _frozenResult({ mode: DEFAULT_MODE, source: 'session', degraded: true, rawValue: raw });
259
+ }
260
+ } catch (_e) {
261
+ // cap-session unavailable — fall through to default.
262
+ }
263
+ }
264
+
265
+ // 3. Default
266
+ return _frozenResult({ mode: DEFAULT_MODE, source: 'default' });
267
+ }
268
+
269
+ // @cap-todo(ac:F-075/AC-2) Persistence writer — atomic write to .cap/config.json.
270
+ /**
271
+ * Persist `mode` into .cap/config.json (source-of-truth) and mirror into SESSION.json.
272
+ * Rejects unknown modes with a thrown Error — this is a developer-facing setter, not a
273
+ * silent coercion path. Use `getTrustMode` for the silent-degrade read path.
274
+ *
275
+ * @param {string} projectRoot
276
+ * @param {TrustMode} mode
277
+ * @returns {TrustModeResult}
278
+ */
279
+ function setTrustMode(projectRoot, mode) {
280
+ if (!isValidMode(mode)) {
281
+ throw new Error(
282
+ `setTrustMode: invalid mode ${JSON.stringify(mode)}. Expected one of ${VALID_MODES.join(', ')}.`,
283
+ );
284
+ }
285
+ const cfg = _readConfig(projectRoot);
286
+ cfg.trustMode = mode;
287
+ _writeConfig(projectRoot, cfg);
288
+ _syncSession(projectRoot, DEFAULT_MODE);
289
+ // Read-back through getTrustMode to get the same shape every caller sees,
290
+ // including the degraded flag for B/C in the MVP.
291
+ return getTrustMode(projectRoot);
292
+ }
293
+
294
+ // @cap-todo(ac:F-075/AC-4) Human-in-the-Loop approval gate — MVP always requires approval.
295
+ // @cap-todo(ac:F-075/AC-7) Open-closed extension point — B/C activation changes only this return.
296
+ /**
297
+ * Return true iff the caller must solicit a human approval before applying a
298
+ * Learn-Pipeline write (pattern extraction, patch apply, unlearn).
299
+ *
300
+ * MVP contract:
301
+ * - Mode A → true (always require approval)
302
+ * - Mode B → future: scope-aware; stub returns true (safe default)
303
+ * - Mode C → future: false (fully autonomous)
304
+ *
305
+ * Callers MUST NOT inspect `trustMode` directly. They ask this helper.
306
+ *
307
+ * @param {string} projectRoot
308
+ * @param {{ scope?: string }} [_opts] - Reserved for Mode B scope resolution.
309
+ * @returns {boolean}
310
+ */
311
+ function requireApproval(projectRoot, _opts) {
312
+ const { mode } = getTrustMode(projectRoot);
313
+ // MVP: every non-C mode is approval-required. Since mode is always 'A' today,
314
+ // this always returns true. The shape of the conditional is kept to make the
315
+ // future B/C activation a one-liner edit.
316
+ if (mode === 'C') return false;
317
+ return true;
318
+ }
319
+
320
+ // -----------------------------------------------------------------------------
321
+ // Exports
322
+ // -----------------------------------------------------------------------------
323
+
324
+ module.exports = {
325
+ // Public API
326
+ getTrustMode,
327
+ setTrustMode,
328
+ requireApproval,
329
+ isValidMode,
330
+ // Constants (read-only — frozen arrays)
331
+ VALID_MODES,
332
+ DEFAULT_MODE,
333
+ WARN_CODE,
334
+ // Test hooks (underscore-prefixed; not part of the stable API)
335
+ _resetWarnOnceForTests,
336
+ };