duaer-spec 0.13.0 → 0.15.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 (86) hide show
  1. package/.duaer/active-job.json +4 -3
  2. package/.duaer/memory/testing.md +22 -8
  3. package/.duaer/specs/033-agent-detect/delivery.json +13 -0
  4. package/.duaer/specs/033-agent-detect/spec.md +24 -0
  5. package/.duaer/specs/033-agent-detect/tasks.md +4 -0
  6. package/.duaer/specs/034-stale-worktree/delivery.json +15 -0
  7. package/.duaer/specs/034-stale-worktree/spec.md +20 -0
  8. package/.duaer/specs/034-stale-worktree/tasks.md +5 -0
  9. package/.duaer/specs/035-revise-card-below/delivery.json +10 -0
  10. package/.duaer/specs/035-revise-card-below/spec.md +19 -0
  11. package/.duaer/specs/035-revise-card-below/tasks.md +7 -0
  12. package/.duaer/specs/036-progress-cta/delivery.json +10 -0
  13. package/.duaer/specs/036-progress-cta/spec.md +18 -0
  14. package/.duaer/specs/036-progress-cta/tasks.md +5 -0
  15. package/.duaer/specs/037-live-i18n/delivery.json +38 -0
  16. package/.duaer/specs/037-live-i18n/spec.md +43 -0
  17. package/.duaer/specs/037-live-i18n/tasks.md +13 -0
  18. package/.duaer/specs/038-revise-enqueue-style/delivery.json +18 -0
  19. package/.duaer/specs/038-revise-enqueue-style/spec.md +60 -0
  20. package/.duaer/specs/038-revise-enqueue-style/tasks.md +7 -0
  21. package/.duaer/specs/039-desk-scroll-sync/delivery.json +18 -0
  22. package/.duaer/specs/039-desk-scroll-sync/spec.md +65 -0
  23. package/.duaer/specs/039-desk-scroll-sync/tasks.md +6 -0
  24. package/.duaer/specs/040-terminal-queue-wait/active-job.json +12 -0
  25. package/.duaer/specs/040-terminal-queue-wait/delivery.json +26 -0
  26. package/.duaer/specs/040-terminal-queue-wait/spec.md +85 -0
  27. package/.duaer/specs/040-terminal-queue-wait/tasks.md +6 -0
  28. package/.duaer/specs/041-desk-wide-queue/delivery.json +18 -0
  29. package/.duaer/specs/041-desk-wide-queue/spec.md +74 -0
  30. package/.duaer/specs/041-desk-wide-queue/tasks.md +6 -0
  31. package/.duaer/specs/042-progress-style/delivery.json +17 -0
  32. package/.duaer/specs/042-progress-style/spec.md +48 -0
  33. package/.duaer/specs/042-progress-style/tasks.md +6 -0
  34. package/.duaer/specs/043-live-history/active-job.json +12 -0
  35. package/.duaer/specs/043-live-history/delivery.json +21 -0
  36. package/.duaer/specs/043-live-history/spec.md +41 -0
  37. package/.duaer/specs/043-live-history/tasks.md +7 -0
  38. package/.duaer/specs/044-desk-layout/delivery.json +18 -0
  39. package/.duaer/specs/044-desk-layout/spec.md +60 -0
  40. package/.duaer/specs/044-desk-layout/tasks.md +7 -0
  41. package/.duaer/specs/045-revise-hang/delivery.json +40 -0
  42. package/.duaer/specs/045-revise-hang/spec.md +50 -0
  43. package/.duaer/specs/045-revise-hang/tasks.md +10 -0
  44. package/.duaer/specs/046-revise-stuck/delivery.json +9 -0
  45. package/.duaer/specs/046-revise-stuck/spec.md +59 -0
  46. package/.duaer/specs/046-revise-stuck/tasks.md +9 -0
  47. package/.duaer/specs/047-revise-preempt-kill/active-job.json +6 -0
  48. package/.duaer/specs/047-revise-preempt-kill/delivery.json +13 -0
  49. package/.duaer/specs/047-revise-preempt-kill/spec.md +53 -0
  50. package/.duaer/specs/047-revise-preempt-kill/tasks.md +6 -0
  51. package/.duaer/specs/048-progress-below/active-job.json +6 -0
  52. package/.duaer/specs/048-progress-below/delivery.json +8 -0
  53. package/.duaer/specs/048-progress-below/spec.md +44 -0
  54. package/.duaer/specs/048-progress-below/tasks.md +8 -0
  55. package/.duaer/specs/049-progress-column/active-job.json +6 -0
  56. package/.duaer/specs/049-progress-column/delivery.json +8 -0
  57. package/.duaer/specs/049-progress-column/spec.md +50 -0
  58. package/.duaer/specs/049-progress-column/tasks.md +7 -0
  59. package/.duaer/specs/050-validate-before-confirm/delivery.json +8 -0
  60. package/.duaer/specs/050-validate-before-confirm/spec.md +64 -0
  61. package/.duaer/specs/050-validate-before-confirm/tasks.md +16 -0
  62. package/.duaer/specs/051-confirm-wide-structured/delivery.json +8 -0
  63. package/.duaer/specs/051-confirm-wide-structured/spec.md +49 -0
  64. package/.duaer/specs/051-confirm-wide-structured/tasks.md +11 -0
  65. package/.duaer/specs/052-desk-fullscreen/delivery.json +8 -0
  66. package/.duaer/specs/052-desk-fullscreen/spec.md +37 -0
  67. package/.duaer/specs/052-desk-fullscreen/tasks.md +9 -0
  68. package/.duaer/specs/053-release-014/delivery.json +8 -0
  69. package/.duaer/specs/053-release-014/spec.md +36 -0
  70. package/.duaer/specs/053-release-014/tasks.md +9 -0
  71. package/.duaer/specs/054-live-l3-smoke/delivery.json +9 -0
  72. package/.duaer/specs/054-live-l3-smoke/spec.md +41 -0
  73. package/.duaer/specs/054-live-l3-smoke/tasks.md +10 -0
  74. package/.duaer/specs/055-release-015/delivery.json +8 -0
  75. package/.duaer/specs/055-release-015/spec.md +16 -0
  76. package/.duaer/specs/055-release-015/tasks.md +4 -0
  77. package/CHANGELOG.md +43 -1
  78. package/README.md +12 -4
  79. package/README.zh-CN.md +9 -4
  80. package/bin/duaer-live.mjs +1166 -130
  81. package/docs/agent/e2e-test-plan.md +30 -0
  82. package/package.json +4 -2
  83. package/web/live-dev/app.js +1350 -306
  84. package/web/live-dev/i18n.js +545 -0
  85. package/web/live-dev/index.html +199 -133
  86. package/web/live-dev/styles.css +541 -103
@@ -1,5 +1,6 @@
1
1
  {
2
- "specDir": ".duaer/specs/032-revise-ux",
3
- "branch": "fix/revise-ux",
4
- "startedAt": "2026-09-16T03:40:00.000Z"
2
+ "featureDir": ".duaer/specs/055-release-015",
3
+ "branch": "feat/release-015",
4
+ "worktree": ".worktree/feat-release-015",
5
+ "status": "active"
5
6
  }
@@ -1,21 +1,22 @@
1
1
  # Testing expectations (project contract)
2
2
 
3
3
  Agents **must** read this file before marking work verified or accepting a job.
4
- Replace command examples with the adopting project's real scripts. Keep the
5
- level model and risk rules unless an ADR documents a change.
4
+ This file is the **duaer-spec** repo contract. Adopters replace command examples
5
+ with their product scripts; keep the level model and risk rules unless an ADR
6
+ documents a change.
6
7
 
7
8
  ## Levels
8
9
 
9
- | Level | What | Typical commands (replace) | Default owner |
10
+ | Level | What | Typical commands (this repo) | Default owner |
10
11
  |---|---|---|---|
11
- | **L0** | Lint / typecheck / static | `npm run lint`, `tsc --noEmit` | After every implementation pass |
12
- | **L1** | Unit tests for changed modules | `npm test` (or package subset) | Default for code changes |
12
+ | **L0** | Lint / typecheck / static | `node --check bin/duaer-live.mjs` `node --check web/live-dev/app.js` | After every implementation pass |
13
+ | **L1** | Unit tests for changed modules | `npm test` when unit files exist under `test/` | Default for code changes |
13
14
  | **L2** | Integration / product acceptance at changed boundaries | project acceptance or integration suite | When boundaries or core paths change |
14
- | **L3** | Automated E2E / browser for user-visible paths | `npm run test:e2e` or equivalent **subset** | When UI / protocol UX changes |
15
+ | **L3** | Automated E2E / protocol smoke for user-visible paths | **`npm run test:live`** (live desk shell + `/api/validate` gate; mock LLM, no paid API) | When live desk UI / protocol UX changes |
15
16
  | **L4** | Manual account / device path when automation cannot cover auth or real data | documented steps in the Brief | When UI/auth and no automated substitute |
16
17
 
17
18
  IDE Browser / MCP exploration is **optional discovery**. It does **not** replace
18
- L3 automation when the project has an E2E suite.
19
+ L3 when `npm run test:live` covers the touched path.
19
20
 
20
21
  ## Risk table (what to run)
21
22
 
@@ -24,12 +25,25 @@ L3 automation when the project has an E2E suite.
24
25
  | Docs / comments only | None (note in Brief) |
25
26
  | Internal code, same behavior | L0 + L1 for affected packages |
26
27
  | API / data / core path | L0 + L1 + L2 (or documented equivalent) |
27
- | User-visible or protocol-visible UI/UX | L0 + L1 + **L3 subset for touched flows** + update E2E catalog |
28
+ | User-visible or protocol-visible UI/UX (live desk) | L0 + **`npm run test:live` (L3)** + update E2E catalog |
28
29
  | Auth / payments / irreversible ops | Above + L4 if L3 cannot cover |
29
30
 
30
31
  Prefer **targeted subsets** over full monorepo suites. Do not skip a required
31
32
  level without an explicit waiver in the feature docs (`tasks.md` or Spec).
32
33
 
34
+ ## Live desk L3 (`npm run test:live`)
35
+
36
+ Covers without a paid LLM:
37
+
38
+ 1. Desk shell HTML/CSS/JS markers (three columns, structured confirm fields,
39
+ `validationAllowsSend`)
40
+ 2. Local validate failures (empty / too-short card)
41
+ 3. `/api/validate` pass and `/api/validate/fix` via an in-process mock
42
+ OpenAI-compatible server
43
+
44
+ Does **not** cover: real model quality, Terminal CLI launch, or full revise
45
+ dispatch. Those stay manual E2E catalog rows or future suites.
46
+
33
47
  ## Opt-in (still need an explicit ask or Spec note)
34
48
 
35
49
  Mark these in the project table when they are expensive:
@@ -0,0 +1,13 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-16T04:15:00.000Z",
4
+ "verification": {
5
+ "levels": ["docs", "L0", "L3-catalog"],
6
+ "commands": [
7
+ "node --check bin/duaer-live.mjs",
8
+ "PATH=/usr/bin:/bin node -e detectAgents smoke"
9
+ ],
10
+ "result": "pass",
11
+ "e2e": ["E2E-034"]
12
+ }
13
+ }
@@ -0,0 +1,24 @@
1
+ # Feature Specification: Detect CLI agents under launchd PATH
2
+
3
+ **Feature Branch**: `fix/agent-detect`
4
+
5
+ **Created**: 2026-09-16
6
+
7
+ **Status**: Active
8
+
9
+ ## Goal
10
+
11
+ Live desk must detect installed Cursor Agent / Claude Code even when started
12
+ via LaunchAgent (minimal PATH).
13
+
14
+ ## Problem
15
+
16
+ LaunchAgent PATH is `/usr/bin:/bin:/usr/sbin:/sbin`, so `which agent|claude`
17
+ fails even though `~/.local/bin` and `/usr/local/bin` have the CLIs.
18
+
19
+ ## Acceptance
20
+
21
+ 1. `ensureCliSearchPath` prepends `~/.local/bin`, `/usr/local/bin`, `/opt/homebrew/bin`
22
+ 2. `whichCmd` falls back to those dirs if `which` misses
23
+ 3. `/api/agents` reports installed agents when binaries exist there
24
+ 4. LaunchAgent plist PATH updated (local handoff) after merge
@@ -0,0 +1,4 @@
1
+ # Tasks
2
+
3
+ - [x] T001 Expand PATH + whichCmd fallback
4
+ - [x] T002 Docs; accept; merge; reload LaunchAgent
@@ -0,0 +1,15 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-16T04:28:30.000Z",
4
+ "summary": "Live desk recovers Brief/status from product primary after worktree handoff cleanup; revise recreates worktree instead of failing.",
5
+ "verification": {
6
+ "levels": ["docs", "L0", "L1-smoke", "L3-catalog"],
7
+ "commands": [
8
+ "/usr/local/bin/node --check bin/duaer-live.mjs",
9
+ "curl -s 'http://127.0.0.1:8792/api/status?jobId=024-job-3l9xtu'"
10
+ ],
11
+ "result": "pass",
12
+ "e2e": ["E2E-035"],
13
+ "notes": "Smoke: status=accepted, delivery.status=accepted, canRevise=true, handoffCleaned=true, worktreeExists=false, featureDir=/Users/morgan/autotest/.duaer/specs/034-job-3l9xtu, resolvedFrom=primary. LaunchAgent 8787 reloaded after merge."
14
+ }
15
+ }
@@ -0,0 +1,20 @@
1
+ # Feature Specification: Survive worktree handoff cleanup
2
+
3
+ **Feature Branch**: `fix/stale-worktree`
4
+
5
+ **Created**: 2026-09-16
6
+
7
+ **Status**: Active
8
+
9
+ ## Goal
10
+
11
+ After the digital employee merges and removes the request worktree, the live
12
+ desk must still show accepted status / preview from the product repo, and
13
+ revise must recreate a worktree instead of erroring「worktree 已不存在」.
14
+
15
+ ## Acceptance
16
+
17
+ 1. Status resolves Brief/delivery from `repoPath` + `specDir` when worktree gone
18
+ 2. Preview/artifacts serve from primary checkout when needed
19
+ 3. Revise recreates `.worktree/feat-*-rN` from develop and copies Brief
20
+ 4. canRevise true when primary Brief exists
@@ -0,0 +1,5 @@
1
+ # Tasks
2
+
3
+ - [x] T001 resolveDispatchRoots + recreateWorktreeForRevise
4
+ - [x] T002 status/artifact/revise wiring
5
+ - [x] T003 verify against 024-job-3l9xtu; docs; merge; reload live
@@ -0,0 +1,10 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-16T04:40:00.000Z",
4
+ "verification": {
5
+ "levels": ["docs", "L0", "L3-catalog"],
6
+ "commands": ["node --check web/live-dev/app.js"],
7
+ "result": "pass",
8
+ "e2e": ["E2E-036"]
9
+ }
10
+ }
@@ -0,0 +1,19 @@
1
+ # Feature Specification: Revise card below confirm card
2
+
3
+ **Feature Branch**: `fix/revise-card-below`
4
+
5
+ **Created**: 2026-09-16
6
+
7
+ **Status**: Active
8
+
9
+ ## Goal
10
+
11
+ Keep the top confirm card unchanged. Put the revise card in the bottom panel
12
+ so users can read it under the dialogue flow without the top card becoming 改进卡.
13
+
14
+ ## Acceptance
15
+
16
+ 1. Confirm card title/labels/styles never switch to revise chrome
17
+ 2. Revise fields render in `#revisePanel` below preview
18
+ 3. Chat applyCard updates bottom revise fields in revise mode
19
+ 4. After revise lock, top still shows original requirements
@@ -0,0 +1,7 @@
1
+ # Tasks
2
+
3
+ - [x] T001 Keep top confirm card original; never switch to revise chrome
4
+ - [x] T002 Render revise fields in `#revisePanel` below preview
5
+ - [x] T003 Chat applyCard updates bottom revise fields in revise mode
6
+ - [x] T004 After revise lock, top still shows original requirements
7
+ - [x] T005 Docs E2E-036 + verify (`node --check`) + accept + merge + reload live
@@ -0,0 +1,10 @@
1
+ {
2
+ "status": "accepted",
3
+ "acceptedAt": "2026-09-16T04:45:00.000Z",
4
+ "verification": {
5
+ "levels": ["docs", "L0"],
6
+ "commands": ["node --check bin/duaer-live.mjs", "node --check web/live-dev/app.js"],
7
+ "result": "pass",
8
+ "e2e": ["E2E-037"]
9
+ }
10
+ }
@@ -0,0 +1,18 @@
1
+ # Feature Specification: Hide product/revise CTAs until accepted
2
+
3
+ **Feature Branch**: `fix/progress-cta`
4
+
5
+ **Created**: 2026-09-16
6
+
7
+ **Status**: Active
8
+
9
+ ## Goal
10
+
11
+ While progress shows in-progress (e.g. `0/3 · 进行中`), do not show 查看成品
12
+ or 再改一版 / 改进卡. Those appear only after delivery accepted.
13
+
14
+ ## Acceptance
15
+
16
+ 1. Status `dispatched` with open delivery → preview null, canRevise false
17
+ 2. Status accepted → preview + revise CTA available
18
+ 3. During revising round → preview may stay; revise panel as needed
@@ -0,0 +1,5 @@
1
+ # Tasks
2
+
3
+ - [x] T001 Gate showPreview/canRevise on accept
4
+ - [x] T002 UI hide preview/revise until productReady
5
+ - [x] T003 Docs; merge; reload live
@@ -0,0 +1,38 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "status": "accepted",
4
+ "feature": "037-live-i18n",
5
+ "acceptedAt": "2026-09-16T04:52:00.000Z",
6
+ "summary": "Live desk zh-CN/en i18n with language switcher, catalogs, DOM data-i18n, and dynamic sync.",
7
+ "verification": {
8
+ "commands": [
9
+ {
10
+ "command": "node --check web/live-dev/i18n.js",
11
+ "result": "pass — exit 0 (SYNTAX_OK) 2026-09-16"
12
+ },
13
+ {
14
+ "command": "node --check web/live-dev/app.js",
15
+ "result": "pass — exit 0 (SYNTAX_OK) 2026-09-16"
16
+ },
17
+ {
18
+ "command": "key parity: every t() key in app.js vs zhCN + en in i18n.js",
19
+ "result": "pass — no missing keys"
20
+ },
21
+ {
22
+ "command": "grep user-visible Chinese in app.js",
23
+ "result": "pass — only intentional LLM kickoff string + comments"
24
+ },
25
+ {
26
+ "command": "curl -sS -o /dev/null -w '%{http_code}' http://localhost:8787/",
27
+ "result": "pass — HTTP 200 (pre-merge; LaunchAgent still on primary develop)"
28
+ },
29
+ {
30
+ "command": "curl -sS -o /dev/null -w '%{http_code}' http://localhost:8787/i18n.js",
31
+ "result": "expected 404 pre-merge — i18n.js not on develop until merge + kickstart"
32
+ }
33
+ ],
34
+ "e2e": ["E2E-038"],
35
+ "notes": "T007 smoke: language switch + localStorage persistence covered by code path (initI18n/detectLocale/setLocale + #langSelect). Post-merge handoff: launchctl kickstart live8787 then re-curl /i18n.js."
36
+ },
37
+ "tasksCompleted": ["T001", "T002", "T003", "T004", "T005", "T006", "T007", "T008"]
38
+ }
@@ -0,0 +1,43 @@
1
+ # Feature Specification: Live desk i18n (zh-CN + en)
2
+
3
+ **Feature Branch**: `feat/live-i18n`
4
+
5
+ **Created**: 2026-09-16
6
+
7
+ **Status**: Active
8
+
9
+ ## Goal
10
+
11
+ Live desk UI supports multiple languages. Initial locales: **zh-CN** (default)
12
+ and **en**, with a visible language switcher. Preference persists.
13
+
14
+ ## In scope
15
+
16
+ - Message catalogs for static + dynamic live UI copy
17
+ - Header language switch (zh-CN / en)
18
+ - Persist locale (`localStorage` key `duaer.live.locale`)
19
+ - Browser language hint on first visit (zh* → zh-CN, else en)
20
+ - Confirm card / revise card / dispatch / preview / revise CTAs translated
21
+ - Do not change confirm-card layout; i18n only for strings
22
+
23
+ ## Out of scope
24
+
25
+ - Translating agent Terminal prompts (may stay bilingual later)
26
+ - Full localization of every server console log line
27
+ - Translating LLM system prompts used by `/api/chat`
28
+
29
+ ## Acceptance
30
+
31
+ 1. Switching to English updates visible desk copy (static `data-i18n` + dynamic
32
+ JS strings) without a layout break
33
+ 2. Reload keeps the chosen locale via `localStorage` (`duaer.live.locale`)
34
+ 3. zh-CN remains default when no preference and browser language is Chinese
35
+ 4. Language switcher (`#langSelect`) is visible in the header
36
+ 5. E2E catalog note (E2E-038)
37
+
38
+ ## Notes
39
+
40
+ - Catalogs live in `web/live-dev/i18n.js`; `app.js` imports `t` / `setLocale` /
41
+ `onLocaleChange`
42
+ - Static files under `web/live-dev/` are served as-is by `bin/duaer-live.mjs`
43
+ (no whitelist); `i18n.js` needs no server change
@@ -0,0 +1,13 @@
1
+ # Tasks: Live desk i18n
2
+
3
+ - [x] T001 Add `web/live-dev/i18n.js` with zh-CN + en catalogs, `t`, `setLocale`,
4
+ `applyDomI18n`, `initI18n`, `onLocaleChange`, `getLocale`
5
+ - [x] T002 Mark static copy in `index.html` with `data-i18n` / placeholders;
6
+ add `#langSelect` + `.lang-switch` styles
7
+ - [x] T003 Expand catalogs for every user-visible string set from `app.js`
8
+ - [x] T004 Wire `app.js`: import i18n, replace hardcoded UI strings with `t()`,
9
+ sync dynamic chrome on `onLocaleChange`, bind `#langSelect`
10
+ - [x] T005 Confirm `duaer-live` static serve covers `i18n.js` (directory serve)
11
+ - [x] T006 Spec acceptance + E2E-038 note
12
+ - [x] T007 Smoke-test: open live desk, switch zh-CN ↔ en, reload persists
13
+ - [x] T008 Stamp `delivery.json` accepted (parent verify / merge)
@@ -0,0 +1,18 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "status": "accepted",
4
+ "feature": "038-revise-enqueue-style",
5
+ "acceptedAt": "2026-09-16T05:22:00.000Z",
6
+ "summary": "Honest revise Terminal enqueue after accepted delivery (fresh session, no false PID reuse) and dark revise card matching panel plate/shadow.",
7
+ "verification": {
8
+ "commands": [
9
+ {
10
+ "command": "node --check bin/duaer-live.mjs",
11
+ "result": "pass — exit 0 (SYNTAX_OK) 2026-09-16"
12
+ }
13
+ ],
14
+ "e2e": ["E2E-039"],
15
+ "notes": "T004: syntax check on duaer-live.mjs; runner heartbeat + isTerminalRunnerHealthy reviewed; revise after accepted uses continueSession=false with Terminal queue; revise-panel CSS uses var(--plate)/var(--shadow) like .panel. Post-merge handoff: launchctl kickstart com.duaer.live8787."
16
+ },
17
+ "tasksCompleted": ["T001", "T002", "T003", "T004"]
18
+ }
@@ -0,0 +1,60 @@
1
+ # Feature Specification: Fix revise enqueue + dark revise card
2
+
3
+ **Feature Branch**: `fix/revise-enqueue-style`
4
+
5
+ **Created**: 2026-09-16
6
+
7
+ **Status**: Active
8
+
9
+ ## Goal
10
+
11
+ Make revise enqueue truthful: when the UI reports「已送入原 Terminal」, an agent
12
+ must actually start. Reuse the same Terminal window and queue when healthy; do
13
+ not treat a dead or stale runner as live (false PID reuse). Avoid `--continue`
14
+ against an ended session. Prefer a fresh agent prompt (no `--continue`) after
15
+ accepted delivery while still enqueueing through the Terminal queue. Restyle
16
+ the revise card to the desk dark plate/panel look (not translucent white).
17
+
18
+ ## Problem
19
+
20
+ 1. UI can show「已送入原 Terminal」while no agent runs
21
+ 2. False PID reuse / stale runner makes enqueue look successful
22
+ 3. `--continue` after an ended session fails to launch work
23
+ 4. Revise card chrome is translucent white and clashes with dark plate/panel desk
24
+
25
+ ## In scope
26
+
27
+ - Runner liveness: heartbeat (or equivalent) so dead PIDs are not treated as
28
+ active Terminal targets
29
+ - Enqueue must actually trigger the agent in the intended Terminal when the
30
+ runner is alive; same window OK
31
+ - After accepted delivery, prefer new agent prompt without `--continue`, still
32
+ via Terminal queue
33
+ - Do not use `--continue` when the prior session has ended
34
+ - Restyle revise card to dark plate/panel desk style
35
+
36
+ ## Out of scope
37
+
38
+ - Changing first-dispatch / non-revise launch flows except as needed for shared
39
+ runner health
40
+ - Redesigning left-chat revise dialogue copy beyond what enqueue honesty needs
41
+ - Remote push / promote to `main`
42
+
43
+ ## Acceptance
44
+
45
+ 1. When UI shows「已送入原 Terminal」after revise confirm, an agent process
46
+ actually starts (or clearly fails with honest UI — no false success)
47
+ 2. A dead/stale runner PID is not reused as a live Terminal target; heartbeat
48
+ (or equivalent) detects liveness before enqueue success
49
+ 3. Same Terminal window may be reused when the runner is alive; enqueue must
50
+ still trigger the agent
51
+ 4. After accepted delivery, revise relaunch prefers a new agent prompt without
52
+ `--continue`, while still using the Terminal queue
53
+ 5. `--continue` is not used against an ended session as the sole launch path
54
+ 6. Revise card matches dark plate/panel desk styling (no translucent white card)
55
+ 7. E2E catalog note (E2E-039)
56
+
57
+ ## Assumptions
58
+
59
+ - Terminal queue / desk runner already exists; this job hardens liveness and
60
+ post-accept launch policy, then restyles the revise card
@@ -0,0 +1,7 @@
1
+ # Tasks
2
+
3
+ - [x] T001 Harden revise enqueue: runner heartbeat/liveness, no false PID reuse,
4
+ no `--continue` after accepted delivery; still use Terminal queue
5
+ - [x] T002 Restyle revise card to dark plate/panel desk (not translucent white)
6
+ - [x] T003 Spec-sync / E2E-039 for honest enqueue + card style
7
+ - [x] T004 Risk-based verify, accept delivery, merge to develop, handoff
@@ -0,0 +1,18 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "status": "accepted",
4
+ "feature": "039-desk-scroll-sync",
5
+ "acceptedAt": "2026-09-16T05:30:00.000Z",
6
+ "summary": "Viewport-bound desk columns with independent scrolls; right panel auto-focuses active stage in sync with left chat.",
7
+ "verification": {
8
+ "commands": [
9
+ {
10
+ "command": "node --check web/live-dev/app.js",
11
+ "result": "ok (exit 0)"
12
+ }
13
+ ],
14
+ "e2e": ["E2E-040"],
15
+ "notes": "CSS (styles.css) not node-checkable. Layout height 100dvh-minus-header; both columns overflow independently. JS: focusRightPanel on bubbles, revise, dispatch, progress, preview."
16
+ },
17
+ "tasksCompleted": ["T001", "T002", "T003", "T004"]
18
+ }
@@ -0,0 +1,65 @@
1
+ # Feature Specification: Live desk left↔right scroll sync
2
+
3
+ **Feature Branch**: `feat/desk-scroll-sync`
4
+
5
+ **Created**: 2026-09-16
6
+
7
+ **Status**: Active
8
+
9
+ ## Goal
10
+
11
+ On the live desk, the right column grows long across the job lifecycle
12
+ (confirm → dispatch → progress → preview → revise) while the left chat stays
13
+ anchored at the top. Keep left↔right focus in sync so the content the user
14
+ needs on the right scrolls into view when chat activity or mode changes
15
+ advance the active stage.
16
+
17
+ ## Problem
18
+
19
+ 1. Right-column content stacks tall; the active section (confirm / progress /
20
+ preview / revise) can sit below the viewport while the user is still
21
+ talking on the left
22
+ 2. Left chat stays at the top by design; without coordinated scroll, users
23
+ miss the stage that chat just unlocked or updated
24
+ 3. Manual scrolling the whole page (or losing independent panel scroll) breaks
25
+ the two-column desk model
26
+
27
+ ## In scope
28
+
29
+ - Independent vertical scroll on the right panel (not whole-page scroll as the
30
+ primary mechanism)
31
+ - Auto-scroll the right panel so the **active section** enters view when chat
32
+ activity or mode/stage changes make that section the focus:
33
+ confirm, progress, preview, or revise
34
+ - Optionally scroll the left chat log to the bottom when new bubbles arrive
35
+ (keep composer / chat chrome usable)
36
+ - Preserve existing confirm-card field layout and copy — no redesign of
37
+ confirm fields
38
+
39
+ ## Out of scope
40
+
41
+ - Redesigning confirm-card fields, labels, or structure
42
+ - Redesigning progress / preview / revise card chrome beyond scroll/focus
43
+ - Changing dispatch, agent launch, or revise enqueue semantics
44
+ - Remote push / promote to `main`
45
+
46
+ ## Acceptance
47
+
48
+ 1. Right panel scrolls independently; tall stage stacks do not force the left
49
+ chat off-screen as the only way to see later stages
50
+ 2. When the active stage becomes confirm, progress, preview, or revise (via
51
+ chat activity or mode change), that section scrolls into view in the right
52
+ panel without requiring the user to hunt for it
53
+ 3. Left chat remains usable at the top of the desk layout; optional auto-scroll
54
+ of the left log to the latest bubble on new messages does not break typing
55
+ 4. Confirm-card fields are unchanged (same fields / structure; scroll-only UX)
56
+ 5. E2E catalog note for this behavior (E2E-040)
57
+
58
+ ## Assumptions
59
+
60
+ - Preferred approach: independent right-panel overflow + `scrollIntoView` (or
61
+ equivalent) on the active section; left log bottom-scroll on new bubbles is
62
+ optional polish if not already reliable
63
+ - Existing partial `scrollIntoView` for revise confirm may be extended, not
64
+ replaced wholesale
65
+ - Implementation deferred; this Brief + tasks only for now
@@ -0,0 +1,6 @@
1
+ # Tasks
2
+
3
+ - [x] T001 Right panel independent scroll + viewport-height desk columns
4
+ - [x] T002 Auto-focus active right stage on chat / mode / progress / preview
5
+ - [x] T003 E2E-040 + Spec acceptance
6
+ - [x] T004 Verify, accept, merge to develop, handoff
@@ -0,0 +1,12 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "feature": "040-terminal-queue-wait",
4
+ "featureDirectory": ".duaer/specs/040-terminal-queue-wait",
5
+ "specDir": ".duaer/specs/040-terminal-queue-wait",
6
+ "branch": "feat/terminal-queue-wait",
7
+ "worktree": ".worktree/feat-terminal-queue-wait",
8
+ "startedAt": "2026-09-16T05:40:00.000Z",
9
+ "updatedAt": "2026-09-16T05:46:00.000Z",
10
+ "source": "duaer-do",
11
+ "status": "accepted"
12
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "status": "accepted",
4
+ "feature": "040-terminal-queue-wait",
5
+ "acceptedAt": "2026-09-16T05:46:00.000Z",
6
+ "summary": "Honest Terminal queue wait while runner is busy: busy=running.cmd → queued/busy flags; UI i18n; E2E-041; never interrupt.",
7
+ "verification": {
8
+ "commands": [
9
+ {
10
+ "command": "node --check bin/duaer-live.mjs",
11
+ "result": "ok (exit 0)"
12
+ },
13
+ {
14
+ "command": "node --check web/live-dev/app.js",
15
+ "result": "ok (exit 0)"
16
+ },
17
+ {
18
+ "command": "node --check web/live-dev/i18n.js",
19
+ "result": "ok (exit 0)"
20
+ }
21
+ ],
22
+ "e2e": ["E2E-041"],
23
+ "notes": "T004: ESM syntax checks pass. Busy runner detected via running.cmd; API returns queued/busy without interrupt. Post-merge handoff: launchctl kickstart com.duaer.live8787."
24
+ },
25
+ "tasksCompleted": ["T001", "T002", "T003", "T004"]
26
+ }
@@ -0,0 +1,85 @@
1
+ # Feature Specification: Terminal busy-queue honesty
2
+
3
+ **Feature Branch**: `feat/terminal-queue-wait`
4
+
5
+ **Created**: 2026-09-16
6
+
7
+ **Status**: Active
8
+
9
+ ## Goal
10
+
11
+ When the user revises or re-dispatches while the Terminal runner is already
12
+ busy (`running.cmd` present and healthy), **wait for the current task to
13
+ finish** — do not interrupt it. Still enqueue via `pending.cmd`. Return
14
+ honest API flags (`queued` / `busy` / `reused` or equivalent) so the UI does
15
+ **not** claim the agent has already started. Show clear zh-CN / en copy that
16
+ the work is queued and will run after the current task ends.
17
+
18
+ ## Problem
19
+
20
+ 1. Busy-runner enqueue can look like an immediate start (e.g.「已送入原
21
+ Terminal」/「数字员工已启动」) even though the new prompt only sits in
22
+ `pending.cmd` until `running.cmd` finishes
23
+ 2. API reuse success (`reused: true` / `mode: "terminal-reuse"`) does not
24
+ distinguish idle reuse from busy queue-wait
25
+ 3. Users may think the revise already interrupted or replaced the in-flight
26
+ task
27
+
28
+ ## User decision (frozen)
29
+
30
+ - **Wait** for the current Terminal task to finish; **do not** interrupt
31
+ - Keep writing `pending.cmd` (queue)
32
+ - Make enqueue honesty explicit in API **and** UI
33
+
34
+ ## In scope
35
+
36
+ - Detect busy runner: `running.cmd` present **and** runner healthy
37
+ (heartbeat / PID liveness as already used for reuse)
38
+ - On revise / re-dispatch into that runner: write `pending.cmd`; do not kill
39
+ or replace `running.cmd`
40
+ - API response must expose queue honesty, e.g.:
41
+ - `queued: true`
42
+ - `busy: true`
43
+ - `reused: true` (same Terminal / runner)
44
+ - (or equivalent fields the UI can rely on without implying “started now”)
45
+ - Live desk UI: when those flags indicate busy-queue, do **not** use “agent
46
+ already started” / immediate-reuse success copy
47
+ - i18n zh-CN / en message along the lines of:
48
+ - zh-CN:「已排入原 Terminal,当前任务结束后自动跑」
49
+ - en: equivalent (“Queued in the existing Terminal; runs after the current
50
+ task finishes”)
51
+ - Preserve post-accept revise policy from prior jobs: **no false `--continue`**
52
+ after accepted delivery; this job is about **busy-queue honesty**, not
53
+ reopening the continue-session debate
54
+ - E2E catalog note **E2E-041**
55
+
56
+ ## Out of scope
57
+
58
+ - Interrupting, preempting, or cancelling the in-flight `running.cmd` task
59
+ - Changing first-dispatch / cold-open Terminal flows except shared response
60
+ fields needed for honesty
61
+ - Redesigning revise card chrome (038) or desk scroll (039)
62
+ - Remote push / promote to `main`
63
+
64
+ ## Acceptance
65
+
66
+ 1. When runner is healthy and `running.cmd` is present, revise / re-dispatch
67
+ writes `pending.cmd` and does **not** interrupt the current task
68
+ 2. API returns honesty flags such that `queued` + `busy` (and `reused` when
69
+ same Terminal) are true in that case — UI can tell “queued, waiting” from
70
+ “started now”
71
+ 3. UI does **not** claim the agent already started solely because reuse
72
+ succeeded; shows the queued/wait message (zh-CN + en)
73
+ 4. Idle healthy runner reuse (no busy `running.cmd`) remains truthful; still
74
+ may reuse the same Terminal without the busy-wait message
75
+ 5. After accepted delivery, revise still avoids false `--continue` (unchanged
76
+ policy; not regressed by this job)
77
+ 6. E2E-041 documents busy-queue wait + honest UI/API
78
+
79
+ ## Assumptions
80
+
81
+ - Runner already serializes: finish current → pick next `pending.cmd`; this
82
+ job makes that behavior **visible and honest**, not a new interrupt model
83
+ - Shared launch path in `bin/duaer-live.mjs` + `web/live-dev` i18n/UI are the
84
+ primary touch points
85
+ - Implementation deferred; Brief + tasks only for now
@@ -0,0 +1,6 @@
1
+ # Tasks
2
+
3
+ - [x] T001 API: detect busy runner (`running.cmd`), return `queued`/`busy`/`reused`; never interrupt
4
+ - [x] T002 UI + i18n honest「已排入原 Terminal,当前任务结束后自动跑」
5
+ - [x] T003 E2E-041
6
+ - [x] T004 Verify, accept, merge, handoff