duaer-spec 0.24.1 → 0.25.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 (149) hide show
  1. package/.claude/rules/agents-workflow.md +2 -0
  2. package/.cursor/rules/agents-workflow.mdc +2 -0
  3. package/.cursor/rules/fde-8787-only.mdc +31 -0
  4. package/.duaer/active-job.json +5 -1
  5. package/.duaer/handoff.json +2 -2
  6. package/.duaer/memory/testing.md +17 -0
  7. package/.duaer/memory/verify.json +4 -0
  8. package/.duaer/specs/218-release-0241/delivery.json +10 -0
  9. package/.duaer/specs/218-release-0241/tasks.md +1 -1
  10. package/.duaer/specs/219-verify-gate/delivery.json +10 -0
  11. package/.duaer/specs/219-verify-gate/spec.md +26 -0
  12. package/.duaer/specs/219-verify-gate/tasks.md +5 -0
  13. package/.duaer/specs/220-project-activate-click/delivery.json +10 -0
  14. package/.duaer/specs/220-project-activate-click/spec.md +15 -0
  15. package/.duaer/specs/220-project-activate-click/tasks.md +4 -0
  16. package/.duaer/specs/221-wave-exit-preempt/delivery.json +14 -0
  17. package/.duaer/specs/221-wave-exit-preempt/spec.md +25 -0
  18. package/.duaer/specs/221-wave-exit-preempt/tasks.md +5 -0
  19. package/.duaer/specs/222-wave-exit-cat/delivery.json +10 -0
  20. package/.duaer/specs/222-wave-exit-cat/spec.md +14 -0
  21. package/.duaer/specs/222-wave-exit-cat/tasks.md +4 -0
  22. package/.duaer/specs/223-dispatch-center/delivery.json +10 -0
  23. package/.duaer/specs/223-dispatch-center/spec.md +16 -0
  24. package/.duaer/specs/223-dispatch-center/tasks.md +6 -0
  25. package/.duaer/specs/224-dispatch-center-page/delivery.json +10 -0
  26. package/.duaer/specs/224-dispatch-center-page/spec.md +15 -0
  27. package/.duaer/specs/224-dispatch-center-page/tasks.md +6 -0
  28. package/.duaer/specs/225-wave-retry/delivery.json +10 -0
  29. package/.duaer/specs/225-wave-retry/spec.md +15 -0
  30. package/.duaer/specs/225-wave-retry/tasks.md +5 -0
  31. package/.duaer/specs/226-progress-pollute/delivery.json +10 -0
  32. package/.duaer/specs/226-progress-pollute/spec.md +15 -0
  33. package/.duaer/specs/226-progress-pollute/tasks.md +5 -0
  34. package/.duaer/specs/227-desk-sqlite/delivery.json +14 -0
  35. package/.duaer/specs/227-desk-sqlite/spec.md +17 -0
  36. package/.duaer/specs/227-desk-sqlite/tasks.md +6 -0
  37. package/.duaer/specs/228-desk-sqlite-all/delivery.json +14 -0
  38. package/.duaer/specs/228-desk-sqlite-all/spec.md +30 -0
  39. package/.duaer/specs/228-desk-sqlite-all/tasks.md +6 -0
  40. package/.duaer/specs/229-task-decompose-graph/delivery.json +14 -0
  41. package/.duaer/specs/229-task-decompose-graph/spec.md +14 -0
  42. package/.duaer/specs/229-task-decompose-graph/tasks.md +6 -0
  43. package/.duaer/specs/230-graph-confirm-busy/delivery.json +15 -0
  44. package/.duaer/specs/230-graph-confirm-busy/spec.md +13 -0
  45. package/.duaer/specs/230-graph-confirm-busy/tasks.md +4 -0
  46. package/.duaer/specs/231-graph-edge-spacing/delivery.json +16 -0
  47. package/.duaer/specs/231-graph-edge-spacing/spec.md +12 -0
  48. package/.duaer/specs/231-graph-edge-spacing/tasks.md +6 -0
  49. package/.duaer/specs/232-fde-8787-only/delivery.json +15 -0
  50. package/.duaer/specs/232-fde-8787-only/spec.md +13 -0
  51. package/.duaer/specs/232-fde-8787-only/tasks.md +5 -0
  52. package/.duaer/specs/233-dispatch-graph-present/delivery.json +15 -0
  53. package/.duaer/specs/233-dispatch-graph-present/spec.md +13 -0
  54. package/.duaer/specs/233-dispatch-graph-present/tasks.md +5 -0
  55. package/.duaer/specs/234-dispatch-no-zoom/delivery.json +15 -0
  56. package/.duaer/specs/234-dispatch-no-zoom/spec.md +14 -0
  57. package/.duaer/specs/234-dispatch-no-zoom/tasks.md +5 -0
  58. package/.duaer/specs/235-dispatch-node-status/delivery.json +15 -0
  59. package/.duaer/specs/235-dispatch-node-status/spec.md +13 -0
  60. package/.duaer/specs/235-dispatch-node-status/tasks.md +5 -0
  61. package/.duaer/specs/236-open-external-8787/delivery.json +15 -0
  62. package/.duaer/specs/236-open-external-8787/spec.md +16 -0
  63. package/.duaer/specs/236-open-external-8787/tasks.md +5 -0
  64. package/.duaer/specs/237-graph-repo-evidence/delivery.json +15 -0
  65. package/.duaer/specs/237-graph-repo-evidence/spec.md +14 -0
  66. package/.duaer/specs/237-graph-repo-evidence/tasks.md +5 -0
  67. package/.duaer/specs/238-dispatch-stage/delivery.json +15 -0
  68. package/.duaer/specs/238-dispatch-stage/spec.md +14 -0
  69. package/.duaer/specs/238-dispatch-stage/tasks.md +5 -0
  70. package/.duaer/specs/239-bug-dispatch/delivery.json +13 -0
  71. package/.duaer/specs/239-bug-dispatch/spec.md +42 -0
  72. package/.duaer/specs/239-bug-dispatch/tasks.md +9 -0
  73. package/.duaer/specs/239-node-zoom/delivery.json +15 -0
  74. package/.duaer/specs/239-node-zoom/spec.md +12 -0
  75. package/.duaer/specs/239-node-zoom/tasks.md +4 -0
  76. package/.duaer/specs/240-dispatch-nav/delivery.json +15 -0
  77. package/.duaer/specs/240-dispatch-nav/spec.md +13 -0
  78. package/.duaer/specs/240-dispatch-nav/tasks.md +5 -0
  79. package/.duaer/specs/241-graph-wrap/delivery.json +15 -0
  80. package/.duaer/specs/241-graph-wrap/spec.md +15 -0
  81. package/.duaer/specs/241-graph-wrap/tasks.md +5 -0
  82. package/.duaer/specs/242-project-btn/delivery.json +14 -0
  83. package/.duaer/specs/242-project-btn/spec.md +13 -0
  84. package/.duaer/specs/242-project-btn/tasks.md +4 -0
  85. package/.duaer/specs/243-bug-kind-toggle/delivery.json +9 -0
  86. package/.duaer/specs/243-bug-kind-toggle/spec.md +12 -0
  87. package/.duaer/specs/243-bug-kind-toggle/tasks.md +6 -0
  88. package/.duaer/specs/243-graph-result-bar/delivery.json +11 -0
  89. package/.duaer/specs/243-graph-result-bar/spec.md +25 -0
  90. package/.duaer/specs/243-graph-result-bar/tasks.md +5 -0
  91. package/.duaer/specs/244-bug-btn-bottom/delivery.json +9 -0
  92. package/.duaer/specs/244-bug-btn-bottom/spec.md +12 -0
  93. package/.duaer/specs/244-bug-btn-bottom/tasks.md +5 -0
  94. package/.duaer/specs/245-preview-opaque/delivery.json +9 -0
  95. package/.duaer/specs/245-preview-opaque/spec.md +11 -0
  96. package/.duaer/specs/245-preview-opaque/tasks.md +4 -0
  97. package/.duaer/specs/246-bug-chat-autofill/delivery.json +14 -0
  98. package/.duaer/specs/246-bug-chat-autofill/spec.md +32 -0
  99. package/.duaer/specs/246-bug-chat-autofill/tasks.md +8 -0
  100. package/.duaer/specs/247-timeline-quicknav/delivery.json +13 -0
  101. package/.duaer/specs/247-timeline-quicknav/spec.md +23 -0
  102. package/.duaer/specs/247-timeline-quicknav/tasks.md +7 -0
  103. package/.duaer/specs/248-quicknav-visible/delivery.json +9 -0
  104. package/.duaer/specs/248-quicknav-visible/spec.md +17 -0
  105. package/.duaer/specs/248-quicknav-visible/tasks.md +4 -0
  106. package/.duaer/specs/249-result-cta-after-deploy/delivery.json +9 -0
  107. package/.duaer/specs/249-result-cta-after-deploy/spec.md +21 -0
  108. package/.duaer/specs/249-result-cta-after-deploy/tasks.md +4 -0
  109. package/.duaer/specs/250-release-025/spec.md +13 -0
  110. package/.duaer/specs/250-release-025/tasks.md +5 -0
  111. package/AGENTS.md +3 -0
  112. package/CHANGELOG.md +155 -0
  113. package/README.md +14 -2
  114. package/README.zh-CN.md +7 -2
  115. package/bin/duaer-live.mjs +684 -227
  116. package/bin/duaer.mjs +18 -9
  117. package/bin/live-archify.mjs +66 -0
  118. package/bin/live-bug-context.mjs +184 -0
  119. package/bin/live-deliverables.mjs +54 -36
  120. package/bin/live-desk-db.mjs +575 -0
  121. package/bin/live-modules.mjs +96 -0
  122. package/bin/live-orchestrate.mjs +69 -0
  123. package/bin/live-project-chat-key.mjs +13 -0
  124. package/bin/live-project-chat.mjs +68 -18
  125. package/bin/live-project-timeline.mjs +121 -0
  126. package/bin/live-verify-gate.mjs +378 -0
  127. package/bin/live-worktree-name.mjs +27 -11
  128. package/docs/agent/e2e-test-plan.md +64 -4
  129. package/docs/agent/live-desk.md +77 -20
  130. package/package.json +2 -2
  131. package/web/live-dev/app.js +904 -113
  132. package/web/live-dev/architecture-ir.mjs +15 -0
  133. package/web/live-dev/architecture-mount.mjs +103 -6
  134. package/web/live-dev/dispatch-center.html +102 -0
  135. package/web/live-dev/dispatch-center.js +351 -0
  136. package/web/live-dev/employee-catalog.mjs +1 -1
  137. package/web/live-dev/i18n-de.js +47 -3
  138. package/web/live-dev/i18n-es.js +46 -2
  139. package/web/live-dev/i18n-fr.js +47 -3
  140. package/web/live-dev/i18n-ja.js +47 -3
  141. package/web/live-dev/i18n-ko.js +46 -2
  142. package/web/live-dev/i18n-pt-BR.js +46 -2
  143. package/web/live-dev/i18n-ru.js +47 -3
  144. package/web/live-dev/i18n-vi.js +47 -3
  145. package/web/live-dev/i18n-zh-TW.js +46 -2
  146. package/web/live-dev/i18n.js +96 -4
  147. package/web/live-dev/index.html +107 -35
  148. package/web/live-dev/styles.css +308 -11
  149. package/web/live-dev/task-graph.mjs +318 -21
@@ -45,6 +45,8 @@ cd .worktree/feat-login
45
45
  - Merge into **`develop`** when done (hotfix: **`main`**, then back-merge **`develop`**)
46
46
  - **Handoff (mandatory):** stop worktree-bound processes → remove `.worktree/<id>` →
47
47
  restart from primary checkout on `develop` (`duaer handoff [--run]`, `.duaer/handoff.json`)
48
+ - **FDE desk:** only `http://127.0.0.1:8787` (`com.duaer.live8787`). Never start
49
+ `duaer-live` on another port; do not open Cursor Browser just to check the desk
48
50
  - Push only when the user explicitly asks (a deploy/hosting ask authorizes
49
51
  the `gh` + push needed for that publish — see `docs/agent/deploy-github.md`)
50
52
  - Promote **`develop` → `main`** only when the user asks to go online
@@ -48,6 +48,8 @@ cd .worktree/feat-login
48
48
  - Merge into **`develop`** when done (hotfix: **`main`**, then back-merge **`develop`**)
49
49
  - **Handoff (mandatory):** stop worktree-bound processes → remove `.worktree/<id>` →
50
50
  restart from primary checkout on `develop` (`duaer handoff [--run]`, `.duaer/handoff.json`)
51
+ - **FDE desk:** only `http://127.0.0.1:8787` (`com.duaer.live8787`). Never start
52
+ `duaer-live` on another port; do not open Cursor Browser just to check the desk
51
53
  - Push only when the user explicitly asks (a deploy/hosting ask authorizes
52
54
  the `gh` + push needed for that publish — see `docs/agent/deploy-github.md`)
53
55
  - Promote **`develop` → `main`** only when the user asks to go online
@@ -0,0 +1,31 @@
1
+ ---
2
+ description: FDE live desk is only http://127.0.0.1:8787 — never alternate ports
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # FDE desk port (8787 only)
7
+
8
+ The Duaer-spec FDE desk has **one** URL:
9
+
10
+ `http://127.0.0.1:8787`
11
+
12
+ Managed by LaunchAgent `com.duaer.live8787`.
13
+
14
+ ## Do
15
+
16
+ - Check / restart with `launchctl kickstart -k "gui/$(id -u)/com.duaer.live8787"`
17
+ - Verify with `curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8787/`
18
+ - After merge handoff, restart that LaunchAgent only (see `.duaer/handoff.json`)
19
+ - Open architecture / 派工图 via desk `POST /api/open-external` (OS browser on :8787)
20
+
21
+ ## Do not
22
+
23
+ - Start `node bin/duaer-live.mjs` on any port other than **8787**
24
+ - Start a second desk when 8787 is already listening
25
+ - Use Cursor Browser / Simple Browser / browser MCP to open the desk or
26
+ architecture present pages — Cursor proxies those as random high ports
27
+ (`:62489`, `:64074`, …). That is **not** a second FDE server.
28
+ - Treat product preview ports (3000, 8081, …) as the FDE desk
29
+
30
+ Interactive `duaer live` may still open the OS browser once; LaunchAgent sets
31
+ `DUAER_LIVE_NO_BROWSER=1` so kickstart does not spam tabs.
@@ -1 +1,5 @@
1
- {"feature":"218-release-0241","updatedAt":"2026-09-19T14:11:06.000Z"}
1
+ {
2
+ "feature": "250-release-025",
3
+ "specPath": ".duaer/specs/250-release-025/spec.md",
4
+ "source": "chat"
5
+ }
@@ -4,8 +4,8 @@
4
4
  "stop": "Stop any process whose working directory is under the request worktree (dev servers, file watchers, local APIs). Also stop the FDE desk if it was started from a worktree copy of duaer-live.",
5
5
  "restartFromPrimary": true,
6
6
  "commands": [
7
- "launchctl kickstart -k \"gui/$(id -u)/com.duaer.live8787\" 2>/dev/null || (lsof -t -iTCP:8787 -sTCP:LISTEN | xargs kill 2>/dev/null; sleep 1; node bin/duaer-live.mjs --port 8787)"
7
+ "launchctl kickstart -k \"gui/$(id -u)/com.duaer.live8787\" 2>/dev/null || (lsof -t -iTCP:8787 -sTCP:LISTEN | xargs kill 2>/dev/null; sleep 1; DUAER_LIVE_NO_BROWSER=1 node bin/duaer-live.mjs --port 8787)"
8
8
  ]
9
9
  },
10
- "notes": "After merge to develop: remove worktree, then restart Duaer-spec FDE (LaunchAgent com.duaer.live8787) so /api/projects and other new routes load from primary checkout."
10
+ "notes": "FDE desk is ONLY http://127.0.0.1:8787 (com.duaer.live8787). Never start duaer-live on another port. LaunchAgent sets DUAER_LIVE_NO_BROWSER=1 so kickstart does not open new browser tabs. After merge: remove worktree, then kickstart so routes load from primary checkout on develop."
11
11
  }
@@ -92,6 +92,23 @@ local **`develop`**:
92
92
  A waiver must state: which level, why it cannot run now, and the follow-up.
93
93
  “Too slow” alone is not enough if a subset command exists.
94
94
 
95
+ ## Machine accept gate (live desk)
96
+
97
+ Product repos that ship through Duaer-spec FDE keep a short contract at
98
+ `.duaer/memory/verify.json`:
99
+
100
+ ```json
101
+ { "commands": ["npm test"], "timeoutSec": 600 }
102
+ ```
103
+
104
+ When tasks are complete, or an employee writes `delivery.json` `accepted`, the
105
+ desk runs those commands in the worktree. A non-zero exit, or a missing /
106
+ invalid file, sets status back to `open` and stores the command, exit code, and
107
+ output tail on `delivery.verification`. Docs-only work with no commands uses
108
+ `{ "waiver": "docs-only" }`. The first command list is frozen for that dispatch
109
+ so a later waiver cannot replace it. `npx duaer-spec update` does not overwrite
110
+ an existing `verify.json`.
111
+
95
112
  ## Adopter profiles
96
113
 
97
114
  Thick product examples (e.g. `examples/dianwu-flow/testing.md`) may add L5+
@@ -0,0 +1,4 @@
1
+ {
2
+ "commands": ["npm test"],
3
+ "timeoutSec": 600
4
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-19T14:12:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1"],
6
+ "commands": ["npm test (177 pass)", "gh release v0.24.1", "npm view duaer-spec version → 0.24.1"],
7
+ "result": "pass"
8
+ },
9
+ "summary": "Released 0.24.1 (Functional regression employee docs) to GitHub and npm."
10
+ }
@@ -1,4 +1,4 @@
1
1
  # Tasks — 218
2
2
 
3
3
  - [x] T001 Bump to 0.24.1 and move CHANGELOG
4
- - [ ] T002 Promote develop → main, tag, GitHub Release, npm
4
+ - [x] T002 Promote develop → main, tag, GitHub Release, npm
@@ -0,0 +1,10 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-19T23:10:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1", "L3"],
6
+ "commands": ["node --check bin/duaer-live.mjs", "npm test (185 pass)"],
7
+ "result": "pass"
8
+ },
9
+ "summary": "Desk runs verify.json before a delivery stamp can stick. Failed or missing contracts reopen accepted."
10
+ }
@@ -0,0 +1,26 @@
1
+ # 219 — Machine verify gate
2
+
3
+ ## Goal
4
+
5
+ The live desk, not the digital employee, decides whether `delivery.json` may stay `accepted`.
6
+
7
+ ## In scope
8
+
9
+ - Read the product worktree `.duaer/memory/verify.json`.
10
+ - When every task is checked, or the employee writes `accepted`, run `commands` in the worktree.
11
+ - Non-zero exit: set status back to `open`, store command / exit / tail on `delivery.verification`, enqueue the log on the regression lane (last lane when N≥2). Do not nudge “stamp accept”.
12
+ - Exit 0: allow `accepted` and keep the same evidence.
13
+ - Missing or invalid contract: do not accept. Status tells the operator the contract is missing or unreadable.
14
+ - `{ "waiver": "docs-only" }` with no commands passes without a shell. If a command list was already seen, later edits cannot replace it with a waiver.
15
+ - `init` / `update` must not overwrite an existing product `verify.json`.
16
+
17
+ ## Out of scope
18
+
19
+ - Screen recordings, a second judge model, a new employee role, post-deploy browser checks.
20
+ - Parsing commands out of `testing.md` prose.
21
+
22
+ ## Acceptance
23
+
24
+ 1. A failing command cannot leave the job or deliverables page in the accepted state.
25
+ 2. A passing command records the command and exit code on `delivery.json`.
26
+ 3. The all-tasks-done “stamp accept” nudge is gone.
@@ -0,0 +1,5 @@
1
+ # Tasks — 219
2
+
3
+ - [x] T001 Desk reopens `accepted` when verify commands fail or the contract is missing
4
+ - [x] T002 Passing commands are stored on `delivery.verification`; stamp nudge is removed
5
+ - [x] T003 Docs, E2E row, and init keep an existing `verify.json`
@@ -0,0 +1,10 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T01:20:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1", "L3"],
6
+ "commands": ["npm test (185 pass)"],
7
+ "result": "pass"
8
+ },
9
+ "summary": "Project activate click shows drawer errors and the drawer scrolls."
10
+ }
@@ -0,0 +1,15 @@
1
+ # 220 — Project activate click shows feedback
2
+
3
+ ## Goal
4
+
5
+ Clicking「选为当前项目」always shows a result in the open project drawer.
6
+
7
+ ## Why
8
+
9
+ Validation only posted a chat bubble behind the drawer, so a missing name or description looked like a dead click. The drawer also clips overflow, so the button can sit below the fold.
10
+
11
+ ## Acceptance
12
+
13
+ 1. Missing path, name, or description shows the message in the drawer (`#historyErr`), not only in chat.
14
+ 2. While activate is in flight, the button is disabled.
15
+ 3. The project drawer scrolls so the button stays reachable.
@@ -0,0 +1,4 @@
1
+ # Tasks — 220
2
+
3
+ - [x] T001 Drawer shows activate errors; button disables while the request runs
4
+ - [x] T002 Project drawer scrolls; E2E row updated
@@ -0,0 +1,14 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T01:25:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1", "L3"],
6
+ "commands": [
7
+ "node --check bin/duaer-live.mjs",
8
+ "node --check bin/live-orchestrate.mjs",
9
+ "npm test (188 pass)"
10
+ ],
11
+ "result": "pass"
12
+ },
13
+ "summary": "A finished wave that still holds the Terminal is preempted once the next job is queued."
14
+ }
@@ -0,0 +1,25 @@
1
+ # 221 — Preempt a finished wave so the next job starts
2
+
3
+ ## Goal
4
+
5
+ After a wave's tasks are checked, the next queued `--continue` starts even if the CLI did not exit.
6
+
7
+ ## Why
8
+
9
+ The employee prints「本波完成,退出等编排器」and stays in the session. The desk already enqueued the next wave, then refuses to release that fingerprint again. The runner cannot dequeue until the leftover process dies, so the job looks stuck after the first wave.
10
+
11
+ ## In scope
12
+
13
+ - On status poll, if `running.cmd`'s wave ids are all `[x]` and `queueDepth > 0`, preempt that session.
14
+ - Do not preempt a session whose own wave is still open.
15
+
16
+ ## Out of scope
17
+
18
+ - Changing task assignment, verify.json, or the personal-intro product job.
19
+ - Killing the Terminal runner itself.
20
+
21
+ ## Acceptance
22
+
23
+ 1. A busy lane whose running script wave is fully checked, with a queued job, is preempted.
24
+ 2. A busy lane whose running script wave is still open is not preempted, even if an earlier wave is done and the queue is non-empty.
25
+ 3. The decision is covered by unit tests; live-desk docs and the E2E catalog record the behavior.
@@ -0,0 +1,5 @@
1
+ # Tasks
2
+
3
+ - [x] Preempt a finished wave when the next job is queued
4
+ - [x] Do not preempt a session whose own wave is still open
5
+ - [x] Record the behavior in live-desk docs and the E2E catalog
@@ -0,0 +1,10 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T01:32:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1", "L3"],
6
+ "commands": ["node --check bin/live-orchestrate.mjs", "npm test (188 pass)"],
7
+ "result": "pass"
8
+ },
9
+ "summary": "A queued continue preempts a leftover CLI even when running.cmd only cats the prompt file."
10
+ }
@@ -0,0 +1,14 @@
1
+ # 222 — Preempt when the Terminal script does not name its wave
2
+
3
+ ## Goal
4
+
5
+ A queued continue starts even when `running.cmd` only `cat`s the prompt file.
6
+
7
+ ## Why
8
+
9
+ The first preempt only fired when `running.cmd` contained `- T00N:` lines. Live jobs store the wave in a prompt file that the next enqueue overwrites, so the desk saw no wave ids and left the leftover CLI in place.
10
+
11
+ ## Acceptance
12
+
13
+ 1. Busy lane, `queueDepth > 0`, and no wave ids in the running script → preempt.
14
+ 2. Busy lane whose named wave is still open → do not preempt.
@@ -0,0 +1,4 @@
1
+ # Tasks
2
+
3
+ - [x] Preempt a waiting queue when the running script has no wave ids
4
+ - [x] Keep the named-open-wave exception
@@ -0,0 +1,10 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T01:45:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1", "L3"],
6
+ "commands": ["node --check web/live-dev/app.js", "npm test (188 pass)"],
7
+ "result": "pass"
8
+ },
9
+ "summary": "Dispatch center lists projects in a 100px rail and shows the selected project's dispatch graph."
10
+ }
@@ -0,0 +1,16 @@
1
+ # 223 — Dispatch center
2
+
3
+ ## Goal
4
+
5
+ Open a project's dispatch graph from a top control, not from hidden hint text.
6
+
7
+ ## Why
8
+
9
+ The line「与系统架构图同一排版…点击图可新页面全屏查看」is not visible on the desk. The graph needs a button and a place that stays on screen.
10
+
11
+ ## Acceptance
12
+
13
+ 1. The top bar has「调度中心」. Opening it hides the three-column desk and shows a two-pane view.
14
+ 2. The left pane is 100px wide and lists current projects. Selecting one shows that project's dispatch graph on the right.
15
+ 3. The dispatch card no longer shows that hint. A「查看派工图」button opens the center on the current project.
16
+ 4. A project with no confirmed tasks shows a short empty line, not the graph.
@@ -0,0 +1,6 @@
1
+ # Tasks
2
+
3
+ - [x] Top Dispatch center opens a two-pane view
4
+ - [x] Left rail is 100px and lists projects; the right pane shows that project's dispatch graph
5
+ - [x] Dispatch card hint is a button that opens the center on the current project
6
+ - [x] Empty projects show a short empty line
@@ -0,0 +1,10 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T02:05:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1", "L3"],
6
+ "commands": ["node --check web/live-dev/app.js", "node --check web/live-dev/dispatch-center.js", "npm test (188 pass)"],
7
+ "result": "pass"
8
+ },
9
+ "summary": "Dispatch center is a separate page with a 100px project rail and the selected project's graph."
10
+ }
@@ -0,0 +1,15 @@
1
+ # 224 — Dispatch center is its own page
2
+
3
+ ## Goal
4
+
5
+ Dispatch center opens a separate page, not a panel over the desk.
6
+
7
+ ## Why
8
+
9
+ The first version hid the three-column desk in place. It needs its own page.
10
+
11
+ ## Acceptance
12
+
13
+ 1. The top Dispatch center button and the dispatch-card button each open `/dispatch-center.html` in a new page.
14
+ 2. That page has a title, a 100px project rail, and the selected project's dispatch graph.
15
+ 3. The desk itself no longer hides behind an in-page overlay.
@@ -0,0 +1,6 @@
1
+ # Tasks
2
+
3
+ - [x] Dispatch center is a standalone page
4
+ - [x] Top button and card button open that page
5
+ - [x] Left rail is 100px; the right pane shows the selected project's graph
6
+ - [x] The desk no longer uses an in-page overlay
@@ -0,0 +1,10 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T02:20:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1", "L3"],
6
+ "commands": ["node --check bin/duaer-live.mjs", "npm test (190 pass)"],
7
+ "result": "pass"
8
+ },
9
+ "summary": "An unchecked released wave is enqueued again when the Terminal lane is idle."
10
+ }
@@ -0,0 +1,15 @@
1
+ # 225 — Requeue a lost wave
2
+
3
+ ## Goal
4
+
5
+ If a released wave is still unchecked and the Terminal lane is idle, enqueue it again.
6
+
7
+ ## Why
8
+
9
+ T007 was marked released, then the runner died and the queue emptied. The desk refused to release that fingerprint again, so the job looked in progress with nothing queued.
10
+
11
+ ## Acceptance
12
+
13
+ 1. An unchecked task-wave fingerprint is dropped when the lane is not busy and the queue is empty.
14
+ 2. A checked wave, and a non-task fingerprint, stay released.
15
+ 3. The drop happens before the next wave release, with a cooldown so a just-enqueued job is not duplicated.
@@ -0,0 +1,5 @@
1
+ # Tasks
2
+
3
+ - [x] Drop an unchecked released wave when the lane is idle
4
+ - [x] Keep checked waves and non-task fingerprints
5
+ - [x] Re-release only after a cooldown
@@ -0,0 +1,10 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T02:35:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1", "L3"],
6
+ "commands": ["node --check web/live-dev/app.js", "npm test (190 pass)"],
7
+ "result": "pass"
8
+ },
9
+ "summary": "Status poll ignores stale job responses so a new project does not keep the previous 派工进度."
10
+ }
@@ -0,0 +1,15 @@
1
+ # 226 — Status poll must not paint another project's progress
2
+
3
+ ## Goal
4
+
5
+ Switching or creating a project must not keep showing the previous project's 派工进度.
6
+
7
+ ## Why
8
+
9
+ `clearDeskWorkspace` clears the timeline, but an in-flight `/api/status` from the old `jobId` can finish afterward and call `renderProgress`, so the new desk looks polluted until refresh.
10
+
11
+ ## Acceptance
12
+
13
+ 1. After a status poll returns, if `state.jobId` is no longer the job that was requested, do not apply progress, preview, or accepted chat bubbles.
14
+ 2. Creating or switching to an empty project still clears the progress column; a stale poll cannot refill it.
15
+ 3. Covered by a source marker test; E2E catalog updated.
@@ -0,0 +1,5 @@
1
+ # Tasks
2
+
3
+ - [x] Ignore stale status-poll responses when jobId changed
4
+ - [x] Keep empty-project progress clear after switch
5
+ - [x] Update E2E catalog and tests
@@ -0,0 +1,14 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T02:30:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1"],
6
+ "commands": [
7
+ "node --check bin/live-desk-db.mjs",
8
+ "node --test test/live-desk-db.test.mjs test/live-project-chat.test.mjs (14 pass)",
9
+ "npm test (195 pass)"
10
+ ],
11
+ "result": "pass"
12
+ },
13
+ "summary": "Project desk sessions use ~/.duaer/live/desk.sqlite with automatic legacy JSON import and versioned schema migrations on open."
14
+ }
@@ -0,0 +1,17 @@
1
+ # 227 — Desk project sessions in SQLite
2
+
3
+ ## Goal
4
+
5
+ Store FDE project desk sessions in SQLite under `~/.duaer/live/desk.sqlite`, with automatic import of legacy JSON and a versioned schema for upgrades.
6
+
7
+ ## Why
8
+
9
+ Whole-file JSON rewrites for every persist are slow. Users who already have `project-chats/*.json` must keep their data when they install or upgrade duaer-spec.
10
+
11
+ ## Acceptance
12
+
13
+ 1. `readProjectChat` / `writeProjectChat` use SQLite; public API paths stay the same.
14
+ 2. On first open of a live root, existing `project-chats/*.json` sessions are imported; re-open does not duplicate.
15
+ 3. The database stores `schema_version`; opening a newer duaer-spec runs migrations in order.
16
+ 4. `engines.node` requires a Node that ships `node:sqlite`.
17
+ 5. Unit tests cover round-trip, JSON import, and schema version.
@@ -0,0 +1,6 @@
1
+ # Tasks
2
+
3
+ - [x] Add desk SQLite open + schema_version migrations
4
+ - [x] Import legacy project-chats JSON on first open
5
+ - [x] Route read/writeProjectChat through SQLite
6
+ - [x] ADR + engines bump + tests + docs
@@ -0,0 +1,14 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T02:50:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1"],
6
+ "commands": [
7
+ "node --check bin/live-desk-db.mjs bin/duaer-live.mjs",
8
+ "node --test test/live-desk-db.test.mjs (7 pass)",
9
+ "npm test (197 pass)"
10
+ ],
11
+ "result": "pass"
12
+ },
13
+ "summary": "Config, repos, and live jobs join project sessions in desk.sqlite with legacy import; live root remains per-machine and never packaged."
14
+ }
@@ -0,0 +1,30 @@
1
+ # 228 — Desk durable state fully in SQLite
2
+
3
+ ## Goal
4
+
5
+ Keep all durable FDE live data in `~/.duaer/live/desk.sqlite` (config, repos, jobs, sessions). Architecture HTML stays a regenerable disk cache. Per-machine `~/.duaer/live` is never packaged or published with duaer-spec.
6
+
7
+ ## Why
8
+
9
+ Sessions alone left config / repos / jobs on JSON and directories. Operators expect one database; install/upgrade must import legacy files automatically. Local desk data must not ship to other users.
10
+
11
+ ## In scope
12
+
13
+ 1. Schema migration v2: `desk_kv` (config, repos) + `jobs` (id + files map).
14
+ 2. On open: import legacy `config.json`, `repos.json`, and `jobs/*/…` when missing from the DB; do not delete legacy files.
15
+ 3. Runtime read/write for config, repos, and live jobs use SQLite as source of truth; job directories are materialized so existing Brief paths keep working.
16
+ 4. Docs/ADR/CHANGELOG/E2E note; unit tests for import + round-trip.
17
+
18
+ ## Out of scope
19
+
20
+ - Publishing or bundling any user's `~/.duaer/live`
21
+ - Moving `architecture/` HTML into SQLite (regenerable cache)
22
+ - Moving product-repo worktree Briefs (those stay in the product repo)
23
+
24
+ ## Acceptance
25
+
26
+ 1. Config and repos load/save via SQLite; legacy files import once.
27
+ 2. Live jobs import and round-trip (job.json / spec.md / tasks.md and other text files under the job dir).
28
+ 3. Opening a newer desk runs migration to `DESK_SCHEMA_VERSION` ≥ 2.
29
+ 4. Docs state that live data is local-only and never part of the npm package.
30
+ 5. Targeted unit tests pass; `npm test` passes.
@@ -0,0 +1,6 @@
1
+ # Tasks
2
+
3
+ - [x] Schema v2: desk_kv + jobs + legacy import for config/repos/jobs
4
+ - [x] Route config/repos/jobs IO through SQLite (materialize job dirs)
5
+ - [x] Update ADR 002, live-desk docs, CHANGELOG, E2E-172/173
6
+ - [x] Unit tests + npm test; stamp delivery accepted
@@ -0,0 +1,14 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T03:20:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1", "L3"],
6
+ "commands": [
7
+ "node --check web/live-dev/app.js web/live-dev/task-graph.mjs",
8
+ "npm test (200 pass)",
9
+ "npm run test:live (1 pass)"
10
+ ],
11
+ "result": "pass"
12
+ },
13
+ "summary": "After architecture confirm, decompose atomic tasks with parallel marks, recommend 1–4 workers, and generate the dispatch graph only after count confirm (revise same gate)."
14
+ }
@@ -0,0 +1,14 @@
1
+ # 229 — Task decompose → recommend workers → dispatch graph
2
+
3
+ ## Goal
4
+
5
+ After architecture confirm (and on revise re-confirm), decompose atomic tasks with parallel marks, recommend 1–4 digital employees, and generate the dispatch graph only after the operator confirms the count.
6
+
7
+ ## Acceptance
8
+
9
+ 1. Dispatch panel appears only after architecture is confirmed.
10
+ 2. Atomic task list is visible above Digital employees; parallelizable tasks are marked.
11
+ 3. System recommends worker count (1–4) from parallel width; user can change it.
12
+ 4. Confirm count generates the dispatch graph; graph is gated until then.
13
+ 5. Revise path uses the same gate after architecture re-confirm.
14
+ 6. Docs/E2E/i18n/tests updated; risk verification passes.
@@ -0,0 +1,6 @@
1
+ # Tasks
2
+
3
+ - [x] Worktree + Brief 229
4
+ - [x] Gate dispatch on arch confirm; decompose list + parallel chips
5
+ - [x] Recommend workers; confirm generates 派工图; revise path
6
+ - [x] i18n/docs/E2E/tests; stamp; merge develop; handoff
@@ -0,0 +1,15 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T03:20:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1", "L3"],
6
+ "commands": [
7
+ "node --check web/live-dev/app.js",
8
+ "node --test test/task-graph.test.mjs test/live-i18n-*.test.mjs (17 pass)",
9
+ "npm test",
10
+ "npm run test:live (1 pass)"
11
+ ],
12
+ "result": "pass"
13
+ },
14
+ "summary": "Confirm-workers button spins while Archify renders the dispatch graph; view enables only after success."
15
+ }
@@ -0,0 +1,13 @@
1
+ # 230 — Confirm workers graph button busy state
2
+
3
+ ## Goal
4
+
5
+ When the operator clicks「确认人数并生成派工图」, the button shows a spinning/busy state until the dispatch graph is rendered; only then can「查看派工图」be used.
6
+
7
+ ## Acceptance
8
+
9
+ 1. Click starts busy/spinning on the confirm button (disabled, busy label).
10
+ 2. Desk POSTs task IR to `/api/architecture/render` and waits for success.
11
+ 3. On success: busy ends, `dispatchGraphReady` is true, graph view is enabled.
12
+ 4. On failure: busy ends, error shown, graph stays gated.
13
+ 5. Tests/docs updated; verification passes.
@@ -0,0 +1,4 @@
1
+ # Tasks
2
+
3
+ - [x] Busy/spinning confirm button + async Archify render before graph ready
4
+ - [x] i18n + CSS + source test; stamp; merge develop; handoff
@@ -0,0 +1,16 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-20T03:25:00.000Z",
4
+ "verification": {
5
+ "levels": ["L0", "L1", "L3"],
6
+ "commands": [
7
+ "node --check web/live-dev/app.js",
8
+ "node --test test/task-graph.test.mjs (13 pass)",
9
+ "npm test (201 pass)",
10
+ "npm run test:live (1 pass)",
11
+ "POST /api/architecture/render with live 34-task pool → 200"
12
+ ],
13
+ "result": "pass"
14
+ },
15
+ "summary": "Task-pool IR spacing clears Archify 24px edge minimum; render failures show in chat."
16
+ }
@@ -0,0 +1,12 @@
1
+ # 231 — Fix dispatch graph Archify spacing
2
+
3
+ ## Goal
4
+
5
+ 「确认人数并生成派工图」must actually produce a viewable graph. Archify was rejecting task IRs because edges were shorter than 24px after label-driven component growth.
6
+
7
+ ## Acceptance
8
+
9
+ 1. Task-pool IR uses spacing that keeps Archify edge length ≥ 24px for real multi-task pools.
10
+ 2. Render failures show in chat (not only a fleeting button flash).
11
+ 3. Confirm succeeds against the live mypro session task pool; graph view enables.
12
+ 4. Tests cover spacing / render success; merge to develop.