scene-capability-engine 3.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 (336) hide show
  1. package/CHANGELOG.md +2513 -0
  2. package/LICENSE +21 -0
  3. package/README.md +765 -0
  4. package/README.zh.md +630 -0
  5. package/bin/kiro-spec-engine.js +796 -0
  6. package/bin/kse.js +3 -0
  7. package/bin/sce.js +3 -0
  8. package/bin/sco.js +3 -0
  9. package/docs/331-poc-adaptation-roadmap.md +156 -0
  10. package/docs/331-poc-dual-track-integration-guide.md +120 -0
  11. package/docs/331-poc-weekly-delivery-checklist.md +52 -0
  12. package/docs/OFFLINE_INSTALL.md +96 -0
  13. package/docs/README.md +279 -0
  14. package/docs/adopt-migration-guide.md +599 -0
  15. package/docs/adoption-guide.md +616 -0
  16. package/docs/agent-hooks-analysis.md +815 -0
  17. package/docs/architecture.md +733 -0
  18. package/docs/articles/ai-driven-development-philosophy-and-practice-review.md +208 -0
  19. package/docs/articles/ai-driven-development-philosophy-and-practice.en.md +459 -0
  20. package/docs/articles/ai-driven-development-philosophy-and-practice.md +492 -0
  21. package/docs/autonomous-control-guide.md +851 -0
  22. package/docs/command-reference.md +1368 -0
  23. package/docs/community.md +115 -0
  24. package/docs/cross-tool-guide.md +555 -0
  25. package/docs/developer-guide.md +619 -0
  26. package/docs/document-governance.md +865 -0
  27. package/docs/environment-management-guide.md +526 -0
  28. package/docs/examples/add-export-command/design.md +194 -0
  29. package/docs/examples/add-export-command/requirements.md +110 -0
  30. package/docs/examples/add-export-command/tasks.md +88 -0
  31. package/docs/examples/add-rest-api/design.md +855 -0
  32. package/docs/examples/add-rest-api/requirements.md +323 -0
  33. package/docs/examples/add-rest-api/tasks.md +355 -0
  34. package/docs/examples/add-user-dashboard/design.md +192 -0
  35. package/docs/examples/add-user-dashboard/requirements.md +143 -0
  36. package/docs/examples/add-user-dashboard/tasks.md +91 -0
  37. package/docs/faq.md +697 -0
  38. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.json +156 -0
  39. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.md +24 -0
  40. package/docs/images/wechat-qr.png +0 -0
  41. package/docs/integration-modes.md +529 -0
  42. package/docs/integration-philosophy.md +313 -0
  43. package/docs/knowledge-management-guide.md +263 -0
  44. package/docs/manual-workflows-guide.md +418 -0
  45. package/docs/moqui-capability-matrix.md +73 -0
  46. package/docs/moqui-template-core-library-playbook.md +109 -0
  47. package/docs/multi-agent-coordination-guide.md +553 -0
  48. package/docs/multi-repo-management-guide.md +1344 -0
  49. package/docs/quick-start-with-ai-tools.md +375 -0
  50. package/docs/quick-start.md +146 -0
  51. package/docs/release-checklist.md +121 -0
  52. package/docs/releases/README.md +13 -0
  53. package/docs/releases/v1.46.2-validation.md +45 -0
  54. package/docs/releases/v1.46.2.md +50 -0
  55. package/docs/scene-runtime-guide.md +347 -0
  56. package/docs/spec-collaboration-guide.md +369 -0
  57. package/docs/spec-locking-guide.md +225 -0
  58. package/docs/spec-numbering-guide.md +348 -0
  59. package/docs/spec-workflow.md +519 -0
  60. package/docs/steering-strategy-guide.md +196 -0
  61. package/docs/team-collaboration-guide.md +465 -0
  62. package/docs/testing-strategy.md +272 -0
  63. package/docs/tools/claude-guide.md +654 -0
  64. package/docs/tools/cursor-guide.md +706 -0
  65. package/docs/tools/generic-guide.md +446 -0
  66. package/docs/tools/kiro-guide.md +308 -0
  67. package/docs/tools/vscode-guide.md +445 -0
  68. package/docs/tools/windsurf-guide.md +391 -0
  69. package/docs/troubleshooting.md +1135 -0
  70. package/docs/upgrade-guide.md +639 -0
  71. package/docs/value-observability-guide.md +127 -0
  72. package/docs/zh/README.md +341 -0
  73. package/docs/zh/quick-start.md +764 -0
  74. package/docs/zh/release-checklist.md +121 -0
  75. package/docs/zh/releases/README.md +13 -0
  76. package/docs/zh/releases/v1.46.2-validation.md +45 -0
  77. package/docs/zh/releases/v1.46.2.md +50 -0
  78. package/docs/zh/spec-numbering-guide.md +348 -0
  79. package/docs/zh/tools/claude-guide.md +349 -0
  80. package/docs/zh/tools/cursor-guide.md +281 -0
  81. package/docs/zh/tools/generic-guide.md +499 -0
  82. package/docs/zh/tools/kiro-guide.md +342 -0
  83. package/docs/zh/tools/vscode-guide.md +449 -0
  84. package/docs/zh/tools/windsurf-guide.md +378 -0
  85. package/docs/zh/value-observability-guide.md +127 -0
  86. package/docs//344/272/244/344/273/230/346/270/205/345/215/225.md +75 -0
  87. package/lib/adoption/adoption-logger.js +487 -0
  88. package/lib/adoption/adoption-strategy.js +538 -0
  89. package/lib/adoption/backup-manager.js +420 -0
  90. package/lib/adoption/conflict-resolver.js +410 -0
  91. package/lib/adoption/detection-engine.js +275 -0
  92. package/lib/adoption/diff-viewer.js +226 -0
  93. package/lib/adoption/error-formatter.js +509 -0
  94. package/lib/adoption/file-classifier.js +385 -0
  95. package/lib/adoption/progress-reporter.js +534 -0
  96. package/lib/adoption/smart-orchestrator.js +470 -0
  97. package/lib/adoption/strategy-selector.js +218 -0
  98. package/lib/adoption/summary-generator.js +493 -0
  99. package/lib/adoption/template-sync.js +605 -0
  100. package/lib/auto/autonomous-engine.js +485 -0
  101. package/lib/auto/checkpoint-manager.js +300 -0
  102. package/lib/auto/close-loop-runner.js +2476 -0
  103. package/lib/auto/config-schema.js +176 -0
  104. package/lib/auto/decision-engine.js +344 -0
  105. package/lib/auto/error-recovery-manager.js +580 -0
  106. package/lib/auto/goal-decomposer.js +278 -0
  107. package/lib/auto/progress-tracker.js +502 -0
  108. package/lib/auto/safety-manager.js +186 -0
  109. package/lib/auto/semantic-decomposer.js +137 -0
  110. package/lib/auto/state-manager.js +126 -0
  111. package/lib/auto/task-queue-manager.js +340 -0
  112. package/lib/backup/backup-system.js +372 -0
  113. package/lib/backup/selective-backup.js +207 -0
  114. package/lib/collab/agent-registry.js +240 -0
  115. package/lib/collab/collab-manager.js +285 -0
  116. package/lib/collab/contract-manager.js +320 -0
  117. package/lib/collab/coordinator.js +370 -0
  118. package/lib/collab/dependency-manager.js +280 -0
  119. package/lib/collab/index.js +20 -0
  120. package/lib/collab/integration-manager.js +202 -0
  121. package/lib/collab/merge-coordinator.js +252 -0
  122. package/lib/collab/metadata-manager.js +233 -0
  123. package/lib/collab/multi-agent-config.js +120 -0
  124. package/lib/collab/spec-lifecycle-manager.js +304 -0
  125. package/lib/collab/sync-barrier.js +88 -0
  126. package/lib/collab/visualizer.js +208 -0
  127. package/lib/commands/adopt.js +749 -0
  128. package/lib/commands/auto.js +19559 -0
  129. package/lib/commands/collab.js +275 -0
  130. package/lib/commands/context.js +99 -0
  131. package/lib/commands/docs.js +808 -0
  132. package/lib/commands/doctor.js +273 -0
  133. package/lib/commands/env.js +420 -0
  134. package/lib/commands/knowledge.js +309 -0
  135. package/lib/commands/lock.js +235 -0
  136. package/lib/commands/ops.js +409 -0
  137. package/lib/commands/orchestrate.js +446 -0
  138. package/lib/commands/prompt.js +105 -0
  139. package/lib/commands/repo.js +118 -0
  140. package/lib/commands/rollback.js +219 -0
  141. package/lib/commands/scene.js +15549 -0
  142. package/lib/commands/spec-bootstrap.js +147 -0
  143. package/lib/commands/spec-gate.js +157 -0
  144. package/lib/commands/spec-pipeline.js +205 -0
  145. package/lib/commands/status.js +321 -0
  146. package/lib/commands/task.js +199 -0
  147. package/lib/commands/templates.js +654 -0
  148. package/lib/commands/upgrade.js +231 -0
  149. package/lib/commands/value.js +569 -0
  150. package/lib/commands/watch.js +684 -0
  151. package/lib/commands/workflows.js +240 -0
  152. package/lib/commands/workspace-multi.js +325 -0
  153. package/lib/commands/workspace.js +189 -0
  154. package/lib/context/context-exporter.js +378 -0
  155. package/lib/context/prompt-generator.js +482 -0
  156. package/lib/data/moqui-capability-lexicon.json +45 -0
  157. package/lib/environment/backup-system.js +189 -0
  158. package/lib/environment/environment-manager.js +379 -0
  159. package/lib/environment/environment-registry.js +168 -0
  160. package/lib/gitignore/gitignore-backup.js +229 -0
  161. package/lib/gitignore/gitignore-detector.js +239 -0
  162. package/lib/gitignore/gitignore-integration.js +267 -0
  163. package/lib/gitignore/gitignore-transformer.js +193 -0
  164. package/lib/gitignore/layered-rules-template.js +42 -0
  165. package/lib/governance/archive-tool.js +284 -0
  166. package/lib/governance/cleanup-tool.js +237 -0
  167. package/lib/governance/config-manager.js +186 -0
  168. package/lib/governance/diagnostic-engine.js +271 -0
  169. package/lib/governance/doc-reference-checker.js +200 -0
  170. package/lib/governance/execution-logger.js +243 -0
  171. package/lib/governance/file-scanner.js +285 -0
  172. package/lib/governance/hooks-manager.js +333 -0
  173. package/lib/governance/reporter.js +337 -0
  174. package/lib/governance/validation-engine.js +181 -0
  175. package/lib/i18n.js +79 -0
  176. package/lib/knowledge/entry-manager.js +208 -0
  177. package/lib/knowledge/index-manager.js +261 -0
  178. package/lib/knowledge/knowledge-manager.js +273 -0
  179. package/lib/knowledge/template-manager.js +191 -0
  180. package/lib/lock/index.js +21 -0
  181. package/lib/lock/lock-file.js +192 -0
  182. package/lib/lock/lock-manager.js +321 -0
  183. package/lib/lock/machine-identifier.js +135 -0
  184. package/lib/lock/steering-file-lock.js +207 -0
  185. package/lib/lock/task-lock-manager.js +345 -0
  186. package/lib/operations/audit-logger.js +293 -0
  187. package/lib/operations/feedback-manager.js +1147 -0
  188. package/lib/operations/index.js +23 -0
  189. package/lib/operations/models/index.js +170 -0
  190. package/lib/operations/operations-manager.js +151 -0
  191. package/lib/operations/operations-validator.js +280 -0
  192. package/lib/operations/permission-manager.js +354 -0
  193. package/lib/operations/template-loader.js +143 -0
  194. package/lib/orchestrator/agent-spawner.js +629 -0
  195. package/lib/orchestrator/bootstrap-prompt-builder.js +236 -0
  196. package/lib/orchestrator/index.js +19 -0
  197. package/lib/orchestrator/orchestration-engine.js +1270 -0
  198. package/lib/orchestrator/orchestrator-config.js +173 -0
  199. package/lib/orchestrator/status-monitor.js +591 -0
  200. package/lib/python-checker.js +209 -0
  201. package/lib/repo/config-manager.js +580 -0
  202. package/lib/repo/errors/config-error.js +13 -0
  203. package/lib/repo/errors/git-error.js +15 -0
  204. package/lib/repo/errors/repo-error.js +14 -0
  205. package/lib/repo/git-operations.js +181 -0
  206. package/lib/repo/handlers/.gitkeep +1 -0
  207. package/lib/repo/handlers/exec-handler.js +155 -0
  208. package/lib/repo/handlers/health-handler.js +169 -0
  209. package/lib/repo/handlers/init-handler.js +197 -0
  210. package/lib/repo/handlers/status-handler.js +176 -0
  211. package/lib/repo/output-formatter.js +184 -0
  212. package/lib/repo/path-resolver.js +178 -0
  213. package/lib/repo/repo-manager.js +514 -0
  214. package/lib/scene-runtime/audit-emitter.js +59 -0
  215. package/lib/scene-runtime/binding-plugin-loader.js +351 -0
  216. package/lib/scene-runtime/binding-registry.js +349 -0
  217. package/lib/scene-runtime/eval-bridge.js +44 -0
  218. package/lib/scene-runtime/index.js +19 -0
  219. package/lib/scene-runtime/moqui-adapter.js +620 -0
  220. package/lib/scene-runtime/moqui-client.js +606 -0
  221. package/lib/scene-runtime/moqui-extractor.js +2029 -0
  222. package/lib/scene-runtime/plan-compiler.js +208 -0
  223. package/lib/scene-runtime/policy-gate.js +58 -0
  224. package/lib/scene-runtime/runtime-executor.js +358 -0
  225. package/lib/scene-runtime/scene-loader.js +96 -0
  226. package/lib/scene-runtime/scene-ontology.js +959 -0
  227. package/lib/scene-runtime/scene-template-linter.js +852 -0
  228. package/lib/scene-runtime/templates/scene-template-erp-query-v0.1.yaml +28 -0
  229. package/lib/scene-runtime/templates/scene-template-hybrid-shadow-v0.1.yaml +34 -0
  230. package/lib/spec/bootstrap/context-collector.js +48 -0
  231. package/lib/spec/bootstrap/draft-generator.js +158 -0
  232. package/lib/spec/bootstrap/questionnaire-engine.js +70 -0
  233. package/lib/spec/bootstrap/trace-emitter.js +59 -0
  234. package/lib/spec/multi-spec-orchestrate.js +93 -0
  235. package/lib/spec/pipeline/constants.js +6 -0
  236. package/lib/spec/pipeline/stage-adapters.js +118 -0
  237. package/lib/spec/pipeline/stage-runner.js +146 -0
  238. package/lib/spec/pipeline/state-store.js +119 -0
  239. package/lib/spec-gate/engine/gate-engine.js +165 -0
  240. package/lib/spec-gate/policy/default-policy.js +22 -0
  241. package/lib/spec-gate/policy/policy-loader.js +103 -0
  242. package/lib/spec-gate/result-emitter.js +81 -0
  243. package/lib/spec-gate/rules/default-rules.js +156 -0
  244. package/lib/spec-gate/rules/rule-registry.js +51 -0
  245. package/lib/steering/adoption-config.js +164 -0
  246. package/lib/steering/compliance-auto-fixer.js +204 -0
  247. package/lib/steering/compliance-cache.js +99 -0
  248. package/lib/steering/compliance-error-reporter.js +70 -0
  249. package/lib/steering/context-sync-manager.js +273 -0
  250. package/lib/steering/index.js +92 -0
  251. package/lib/steering/spec-steering.js +230 -0
  252. package/lib/steering/steering-compliance-checker.js +73 -0
  253. package/lib/steering/steering-loader.js +144 -0
  254. package/lib/steering/steering-manager.js +289 -0
  255. package/lib/task/index.js +12 -0
  256. package/lib/task/task-claimer.js +489 -0
  257. package/lib/task/task-status-store.js +418 -0
  258. package/lib/templates/cache-manager.js +440 -0
  259. package/lib/templates/content-generalizer.js +247 -0
  260. package/lib/templates/frontmatter-generator.js +128 -0
  261. package/lib/templates/git-handler.js +471 -0
  262. package/lib/templates/metadata-collector.js +328 -0
  263. package/lib/templates/path-utils.js +144 -0
  264. package/lib/templates/registry-parser.js +505 -0
  265. package/lib/templates/spec-reader.js +216 -0
  266. package/lib/templates/template-applicator.js +249 -0
  267. package/lib/templates/template-creator.js +256 -0
  268. package/lib/templates/template-error.js +143 -0
  269. package/lib/templates/template-exporter.js +502 -0
  270. package/lib/templates/template-manager.js +782 -0
  271. package/lib/templates/template-validator.js +361 -0
  272. package/lib/upgrade/migration-engine.js +382 -0
  273. package/lib/upgrade/migrations/.gitkeep +52 -0
  274. package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +78 -0
  275. package/lib/utils/file-diff.js +177 -0
  276. package/lib/utils/fs-utils.js +274 -0
  277. package/lib/utils/tool-detector.js +383 -0
  278. package/lib/utils/validation.js +324 -0
  279. package/lib/value/gate-summary-emitter.js +99 -0
  280. package/lib/value/metric-contract-loader.js +210 -0
  281. package/lib/value/risk-evaluator.js +117 -0
  282. package/lib/value/weekly-snapshot-builder.js +61 -0
  283. package/lib/version/version-checker.js +156 -0
  284. package/lib/version/version-manager.js +327 -0
  285. package/lib/watch/action-executor.js +458 -0
  286. package/lib/watch/event-debouncer.js +323 -0
  287. package/lib/watch/execution-logger.js +550 -0
  288. package/lib/watch/file-watcher.js +499 -0
  289. package/lib/watch/presets.js +266 -0
  290. package/lib/watch/watch-manager.js +533 -0
  291. package/lib/workspace/multi/global-config.js +150 -0
  292. package/lib/workspace/multi/index.js +22 -0
  293. package/lib/workspace/multi/path-utils.js +173 -0
  294. package/lib/workspace/multi/workspace-context-resolver.js +244 -0
  295. package/lib/workspace/multi/workspace-registry.js +196 -0
  296. package/lib/workspace/multi/workspace-state-manager.js +537 -0
  297. package/lib/workspace/multi/workspace.js +90 -0
  298. package/lib/workspace/workspace-manager.js +370 -0
  299. package/lib/workspace/workspace-sync.js +356 -0
  300. package/locales/en.json +114 -0
  301. package/locales/zh.json +114 -0
  302. package/package.json +102 -0
  303. package/template/.kiro/README.md +247 -0
  304. package/template/.kiro/hooks/check-spec-on-create.kiro.hook +17 -0
  305. package/template/.kiro/hooks/run-tests-on-save.kiro.hook +13 -0
  306. package/template/.kiro/hooks/sync-tasks-on-edit.kiro.hook +16 -0
  307. package/template/.kiro/specs/SPEC_WORKFLOW_GUIDE.md +134 -0
  308. package/template/.kiro/steering/CORE_PRINCIPLES.md +133 -0
  309. package/template/.kiro/steering/CURRENT_CONTEXT.md +30 -0
  310. package/template/.kiro/steering/ENVIRONMENT.md +35 -0
  311. package/template/.kiro/steering/RULES_GUIDE.md +46 -0
  312. package/template/.kiro/templates/operations/default/change-impact.md +112 -0
  313. package/template/.kiro/templates/operations/default/deployment.md +91 -0
  314. package/template/.kiro/templates/operations/default/feedback-response.md +269 -0
  315. package/template/.kiro/templates/operations/default/migration-plan.md +172 -0
  316. package/template/.kiro/templates/operations/default/monitoring.md +135 -0
  317. package/template/.kiro/templates/operations/default/operations.md +135 -0
  318. package/template/.kiro/templates/operations/default/rollback.md +143 -0
  319. package/template/.kiro/templates/operations/default/tools.yaml +364 -0
  320. package/template/.kiro/templates/operations/default/troubleshooting.md +123 -0
  321. package/template/.kiro/tools/backup_manager.py +295 -0
  322. package/template/.kiro/tools/configuration_manager.py +218 -0
  323. package/template/.kiro/tools/document_evaluator.py +550 -0
  324. package/template/.kiro/tools/enhancement_logger.py +168 -0
  325. package/template/.kiro/tools/error_handler.py +335 -0
  326. package/template/.kiro/tools/improvement_identifier.py +444 -0
  327. package/template/.kiro/tools/modification_applicator.py +737 -0
  328. package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
  329. package/template/.kiro/tools/quality_scorer.py +305 -0
  330. package/template/.kiro/tools/report_generator.py +154 -0
  331. package/template/.kiro/tools/ultrawork_enhancer.py +676 -0
  332. package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
  333. package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
  334. package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
  335. package/template/.kiro/tools/workflow_quality_gate.py +100 -0
  336. package/template/README.md +111 -0
@@ -0,0 +1,418 @@
1
+ /**
2
+ * Task Status Store
3
+ *
4
+ * Provides concurrency-safe reads and writes to tasks.md.
5
+ * In multi-Agent mode, uses file-level locks + exponential backoff retry
6
+ * to prevent concurrent write conflicts.
7
+ * In single-Agent mode, delegates directly to the existing TaskClaimer
8
+ * (no locks, no retries) for full backward compatibility.
9
+ *
10
+ * Requirements: 3.1-3.6
11
+ */
12
+
13
+ const fs = require('fs-extra');
14
+ const path = require('path');
15
+ const { MultiAgentConfig } = require('../collab/multi-agent-config');
16
+ const TaskClaimer = require('./task-claimer');
17
+ const fsUtils = require('../utils/fs-utils');
18
+
19
+ const MAX_RETRIES = 5;
20
+ const BASE_DELAY_MS = 100;
21
+ const STALE_LOCK_MS = 30000; // 30 seconds
22
+
23
+ class TaskStatusStore {
24
+ /**
25
+ * @param {string} workspaceRoot - Absolute path to the project root
26
+ */
27
+ constructor(workspaceRoot) {
28
+ this._workspaceRoot = workspaceRoot;
29
+ this._config = new MultiAgentConfig(workspaceRoot);
30
+ this._claimer = new TaskClaimer();
31
+ }
32
+
33
+ /**
34
+ * Whether multi-Agent mode is currently enabled.
35
+ * @returns {Promise<boolean>}
36
+ */
37
+ async isMultiAgentMode() {
38
+ return this._config.isEnabled();
39
+ }
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // Public API
43
+ // ---------------------------------------------------------------------------
44
+
45
+ /**
46
+ * Safely update a task's status in tasks.md.
47
+ *
48
+ * Multi-Agent mode: acquire file lock → verify line → write → release lock.
49
+ * Single-Agent mode: delegate to TaskClaimer.updateTaskStatus().
50
+ *
51
+ * @param {string} specName
52
+ * @param {string} taskId
53
+ * @param {string} status
54
+ * @param {object} [options]
55
+ * @returns {Promise<object>} UpdateResult
56
+ */
57
+ async updateStatus(specName, taskId, status, _options = {}) {
58
+ const multiAgent = await this.isMultiAgentMode();
59
+ if (!multiAgent) {
60
+ return this._claimer.updateTaskStatus(this._workspaceRoot, specName, taskId, status);
61
+ }
62
+
63
+ return this._withLockAndRetry(specName, async () => {
64
+ const tasksPath = this._tasksPath(specName);
65
+ const content = await fs.readFile(tasksPath, 'utf8');
66
+ const lines = content.split(/\r?\n/);
67
+ const tasks = await this._claimer.parseTasks(tasksPath);
68
+ const task = tasks.find(t => t.taskId === taskId);
69
+
70
+ if (!task) {
71
+ return { success: false, error: `Task not found: ${taskId}` };
72
+ }
73
+
74
+ // Verify target line hasn't been modified (Requirement 3.4)
75
+ if (lines[task.lineNumber] !== task.originalLine) {
76
+ throw new ConflictError(`Target line modified by another agent`);
77
+ }
78
+
79
+ // Build new line
80
+ const statusChar = this._claimer.statusToChar(status);
81
+ const optionalMarker = task.isOptional ? '*' : '';
82
+ const linePrefix = task.linePrefix || '- ';
83
+ let newLine = `${linePrefix}[${statusChar}]${optionalMarker} ${taskId} ${task.title}`;
84
+ if (task.claimedBy) {
85
+ newLine += ` [@${task.claimedBy}, claimed: ${task.claimedAt}]`;
86
+ }
87
+
88
+ lines[task.lineNumber] = newLine;
89
+ await fsUtils.atomicWrite(tasksPath, lines.join('\n'));
90
+
91
+ return { success: true, taskId, oldStatus: task.status, newStatus: status };
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Safely claim a task in tasks.md.
97
+ *
98
+ * Multi-Agent mode: acquire file lock → verify line → write → release lock.
99
+ * Single-Agent mode: delegate to TaskClaimer.claimTask().
100
+ *
101
+ * @param {string} specName
102
+ * @param {string} taskId
103
+ * @param {string} agentId
104
+ * @param {string} username
105
+ * @returns {Promise<object>} ClaimResult
106
+ */
107
+ async claimTask(specName, taskId, agentId, username) {
108
+ const multiAgent = await this.isMultiAgentMode();
109
+ if (!multiAgent) {
110
+ return this._claimer.claimTask(this._workspaceRoot, specName, taskId, username);
111
+ }
112
+
113
+ return this._withLockAndRetry(specName, async () => {
114
+ const tasksPath = this._tasksPath(specName);
115
+ const content = await fs.readFile(tasksPath, 'utf8');
116
+ const lines = content.split(/\r?\n/);
117
+ const tasks = await this._claimer.parseTasks(tasksPath);
118
+ const task = tasks.find(t => t.taskId === taskId);
119
+
120
+ if (!task) {
121
+ return { success: false, error: `Task not found: ${taskId}` };
122
+ }
123
+
124
+ // Verify target line hasn't been modified (Requirement 3.4)
125
+ if (lines[task.lineNumber] !== task.originalLine) {
126
+ throw new ConflictError(`Target line modified by another agent`);
127
+ }
128
+
129
+ // Check if already claimed by another user
130
+ if (task.claimedBy && task.claimedBy !== username) {
131
+ return {
132
+ success: false,
133
+ error: `Task already claimed by ${task.claimedBy}`,
134
+ currentClaim: {
135
+ username: task.claimedBy,
136
+ claimedAt: task.claimedAt,
137
+ },
138
+ };
139
+ }
140
+
141
+ // Build new line with claim info
142
+ const claimTimestamp = new Date().toISOString();
143
+ const statusChar = this._claimer.statusToChar('in-progress');
144
+ const optionalMarker = task.isOptional ? '*' : '';
145
+ const linePrefix = task.linePrefix || '- ';
146
+ const newLine = `${linePrefix}[${statusChar}]${optionalMarker} ${taskId} ${task.title} [@${username}, claimed: ${claimTimestamp}]`;
147
+
148
+ lines[task.lineNumber] = newLine;
149
+ await fsUtils.atomicWrite(tasksPath, lines.join('\n'));
150
+
151
+ return {
152
+ success: true,
153
+ taskId,
154
+ agentId,
155
+ username,
156
+ claimedAt: claimTimestamp,
157
+ previousClaim: task.claimedBy
158
+ ? { username: task.claimedBy, claimedAt: task.claimedAt }
159
+ : null,
160
+ };
161
+ });
162
+ }
163
+
164
+ /**
165
+ * Safely unclaim a task in tasks.md.
166
+ *
167
+ * Multi-Agent mode: acquire file lock → verify line → write → release lock.
168
+ * Single-Agent mode: delegate to TaskClaimer.unclaimTask().
169
+ *
170
+ * @param {string} specName
171
+ * @param {string} taskId
172
+ * @param {string} agentId
173
+ * @param {string} username
174
+ * @returns {Promise<object>} UnclaimResult
175
+ */
176
+ async unclaimTask(specName, taskId, agentId, username) {
177
+ const multiAgent = await this.isMultiAgentMode();
178
+ if (!multiAgent) {
179
+ return this._claimer.unclaimTask(this._workspaceRoot, specName, taskId, username);
180
+ }
181
+
182
+ return this._withLockAndRetry(specName, async () => {
183
+ const tasksPath = this._tasksPath(specName);
184
+ const content = await fs.readFile(tasksPath, 'utf8');
185
+ const lines = content.split(/\r?\n/);
186
+ const tasks = await this._claimer.parseTasks(tasksPath);
187
+ const task = tasks.find(t => t.taskId === taskId);
188
+
189
+ if (!task) {
190
+ return { success: false, error: `Task not found: ${taskId}` };
191
+ }
192
+
193
+ // Verify target line hasn't been modified (Requirement 3.4)
194
+ if (lines[task.lineNumber] !== task.originalLine) {
195
+ throw new ConflictError(`Target line modified by another agent`);
196
+ }
197
+
198
+ if (!task.claimedBy) {
199
+ return { success: false, error: 'Task is not claimed' };
200
+ }
201
+
202
+ if (task.claimedBy !== username) {
203
+ return { success: false, error: `Task is claimed by ${task.claimedBy}, not ${username}` };
204
+ }
205
+
206
+ // Build new line without claim info, reset status
207
+ const statusChar = this._claimer.statusToChar('not-started');
208
+ const optionalMarker = task.isOptional ? '*' : '';
209
+ const linePrefix = task.linePrefix || '- ';
210
+ const newLine = `${linePrefix}[${statusChar}]${optionalMarker} ${taskId} ${task.title}`;
211
+
212
+ lines[task.lineNumber] = newLine;
213
+ await fsUtils.atomicWrite(tasksPath, lines.join('\n'));
214
+
215
+ return {
216
+ success: true,
217
+ taskId,
218
+ agentId,
219
+ username,
220
+ unclaimedAt: new Date().toISOString(),
221
+ };
222
+ });
223
+ }
224
+
225
+
226
+ // ---------------------------------------------------------------------------
227
+ // Private helpers
228
+ // ---------------------------------------------------------------------------
229
+
230
+ /**
231
+ * Absolute path to tasks.md for a given spec.
232
+ * @param {string} specName
233
+ * @returns {string}
234
+ */
235
+ _tasksPath(specName) {
236
+ return path.join(this._workspaceRoot, '.kiro/specs', specName, 'tasks.md');
237
+ }
238
+
239
+ /**
240
+ * Absolute path to the file-level lock for tasks.md.
241
+ * @param {string} specName
242
+ * @returns {string}
243
+ */
244
+ _lockPath(specName) {
245
+ return path.join(this._workspaceRoot, '.kiro/specs', specName, 'tasks.md.lock');
246
+ }
247
+
248
+ // ---------------------------------------------------------------------------
249
+ // File lock primitives
250
+ // ---------------------------------------------------------------------------
251
+
252
+ /**
253
+ * Try to acquire the file-level lock.
254
+ * Uses `wx` (exclusive create) flag so that only one writer can succeed.
255
+ *
256
+ * @param {string} specName
257
+ * @returns {Promise<boolean>} true if lock acquired
258
+ */
259
+ async _acquireLock(specName) {
260
+ const lockFile = this._lockPath(specName);
261
+ const lockData = JSON.stringify({
262
+ acquiredAt: new Date().toISOString(),
263
+ });
264
+
265
+ try {
266
+ await fs.writeFile(lockFile, lockData, { flag: 'wx' });
267
+ return true;
268
+ } catch (err) {
269
+ if (err.code === 'EEXIST') {
270
+ // Lock held – check for staleness
271
+ return this._tryClaimStaleLock(lockFile, lockData);
272
+ }
273
+ throw err;
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Release the file-level lock by deleting the lock file.
279
+ * @param {string} specName
280
+ */
281
+ async _releaseLock(specName) {
282
+ const lockFile = this._lockPath(specName);
283
+ try {
284
+ await fs.unlink(lockFile);
285
+ } catch (err) {
286
+ // Ignore ENOENT – lock may have been cleaned up already
287
+ if (err.code !== 'ENOENT') {
288
+ throw err;
289
+ }
290
+ }
291
+ }
292
+
293
+ /**
294
+ * If the existing lock file is older than STALE_LOCK_MS, delete it and
295
+ * re-attempt acquisition.
296
+ *
297
+ * @param {string} lockFile
298
+ * @param {string} lockData
299
+ * @returns {Promise<boolean>}
300
+ */
301
+ async _tryClaimStaleLock(lockFile, lockData) {
302
+ try {
303
+ const stat = await fs.stat(lockFile);
304
+ const ageMs = Date.now() - stat.mtimeMs;
305
+ if (ageMs > STALE_LOCK_MS) {
306
+ // Stale lock – remove and retry
307
+ await fs.unlink(lockFile);
308
+ try {
309
+ await fs.writeFile(lockFile, lockData, { flag: 'wx' });
310
+ return true;
311
+ } catch (retryErr) {
312
+ // Another agent beat us to it
313
+ if (retryErr.code === 'EEXIST') return false;
314
+ throw retryErr;
315
+ }
316
+ }
317
+ } catch (statErr) {
318
+ // Lock file disappeared between our check and stat – try again
319
+ if (statErr.code === 'ENOENT') {
320
+ try {
321
+ await fs.writeFile(lockFile, lockData, { flag: 'wx' });
322
+ return true;
323
+ } catch (retryErr) {
324
+ if (retryErr.code === 'EEXIST') return false;
325
+ throw retryErr;
326
+ }
327
+ }
328
+ }
329
+ return false;
330
+ }
331
+
332
+ // ---------------------------------------------------------------------------
333
+ // Retry orchestration
334
+ // ---------------------------------------------------------------------------
335
+
336
+ /**
337
+ * Execute `fn` while holding the tasks.md file lock.
338
+ * On failure (lock contention or ConflictError), retries with exponential
339
+ * backoff up to MAX_RETRIES times.
340
+ *
341
+ * If `fn` returns a result with `success: false` that is NOT a conflict
342
+ * (e.g. "Task not found"), the result is returned immediately without retry.
343
+ *
344
+ * @param {string} specName
345
+ * @param {Function} fn - async () => result
346
+ * @returns {Promise<object>}
347
+ */
348
+ async _withLockAndRetry(specName, fn) {
349
+ let lastError = null;
350
+
351
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
352
+ // Back off before retrying (skip delay on first attempt)
353
+ if (attempt > 0) {
354
+ const delayMs = BASE_DELAY_MS * Math.pow(2, attempt - 1);
355
+ await this._sleep(delayMs);
356
+ }
357
+
358
+ let lockAcquired = false;
359
+ try {
360
+ lockAcquired = await this._acquireLock(specName);
361
+ if (!lockAcquired) {
362
+ lastError = new Error('Failed to acquire file lock');
363
+ continue; // retry
364
+ }
365
+
366
+ const result = await fn();
367
+
368
+ // Non-conflict failures (e.g. task not found) – return immediately
369
+ if (result && result.success === false) {
370
+ return result;
371
+ }
372
+
373
+ return result;
374
+ } catch (err) {
375
+ if (err instanceof ConflictError) {
376
+ lastError = err;
377
+ continue; // retry
378
+ }
379
+ // Unexpected error – propagate
380
+ throw err;
381
+ } finally {
382
+ if (lockAcquired) {
383
+ await this._releaseLock(specName);
384
+ }
385
+ }
386
+ }
387
+
388
+ // Retries exhausted – return conflict error (Requirement 3.5)
389
+ return {
390
+ success: false,
391
+ error: 'Conflict: retries exhausted, original file preserved',
392
+ conflict: true,
393
+ lastError: lastError ? lastError.message : 'unknown',
394
+ };
395
+ }
396
+
397
+ /**
398
+ * Promise-based sleep helper.
399
+ * @param {number} ms
400
+ * @returns {Promise<void>}
401
+ */
402
+ _sleep(ms) {
403
+ return new Promise(resolve => setTimeout(resolve, ms));
404
+ }
405
+ }
406
+
407
+ // ---------------------------------------------------------------------------
408
+ // Internal error type for conflict detection
409
+ // ---------------------------------------------------------------------------
410
+
411
+ class ConflictError extends Error {
412
+ constructor(message) {
413
+ super(message);
414
+ this.name = 'ConflictError';
415
+ }
416
+ }
417
+
418
+ module.exports = { TaskStatusStore, ConflictError };