182 Commits
Author SHA1 Message Date
Hare 159704dc6f fix: clear encrypted-only thinking snapshots 2026-06-25 20:28:51 +09:00
Hare 6492f10f42 test: update pod prompt expectations 2026-06-25 18:43:57 +09:00
Hare a525ba4d01 merge: plugin runtime implementation chain 2026-06-25 18:18:14 +09:00
Hare c261aea021 ticket: mark plugin pdk service events done 2026-06-25 16:57:31 +09:00
Hare 8d4fee231b merge: 00001KVXK0WEA plugin pdk service events 2026-06-25 16:55:35 +09:00
Hare 307d38453f ticket: approve plugin pdk service events 2026-06-25 16:55:35 +09:00
Hare d2a8a79ac6 ticket: record plugin pdk template fix 2026-06-25 16:53:19 +09:00
Hare 6c8998878d fix: declare plugin service websocket authority 2026-06-25 16:52:20 +09:00
Hare 41283c8dd9 ticket: request plugin pdk template fix 2026-06-25 16:44:57 +09:00
Hare fa29cc2c95 ticket: record plugin pdk implementation report 2026-06-25 16:38:53 +09:00
Hare 7a4fd97526 feat: update plugin service authoring templates 2026-06-25 16:37:37 +09:00
Hare 103077dfae ticket: record plugin pdk coder start 2026-06-25 16:09:47 +09:00
Hare cd2006305e ticket: accept plugin pdk service events task 2026-06-25 16:08:53 +09:00
Hare a453c6e2da ticket: route plugin pdk service events task 2026-06-25 16:08:34 +09:00
Hare 16247ce7c5 ticket: mark plugin websocket driver done 2026-06-25 16:06:42 +09:00
Hare b9e786e106 merge: 00001KVXK0WE4 plugin websocket driver 2026-06-25 16:03:57 +09:00
Hare ccd4d83d43 ticket: approve plugin websocket driver 2026-06-25 16:03:57 +09:00
Hare 3a9ac1b1b7 ticket: record plugin websocket implementation report 2026-06-25 15:56:44 +09:00
Hare f2c51ffe39 feat: add plugin websocket service driver 2026-06-25 15:54:36 +09:00
Hare ef17955369 ticket: record plugin websocket coder start 2026-06-25 15:24:27 +09:00
Hare 62e467c035 ticket: accept plugin websocket driver task 2026-06-25 15:23:25 +09:00
Hare 4950749c5d ticket: route plugin websocket driver task 2026-06-25 15:22:55 +09:00
Hare dc2f8b409d ticket: mark plugin output commands done 2026-06-25 15:20:33 +09:00
Hare d2aa92a729 merge: 00001KVXK0WDX plugin output commands 2026-06-25 15:18:43 +09:00
Hare 86017a5abc ticket: approve plugin output commands 2026-06-25 15:18:43 +09:00
Hare 799998639a ticket: record plugin output implementation report 2026-06-25 15:13:08 +09:00
Hare 755d460f0d feat: add plugin service output commands 2026-06-25 15:11:30 +09:00
Hare 07f9793bc6 ticket: record plugin output coder start 2026-06-25 14:47:52 +09:00
Hare 89a40db79e ticket: accept plugin output command task 2026-06-25 14:46:54 +09:00
Hare 84a8423611 ticket: route plugin output command task 2026-06-25 14:46:20 +09:00
Hare 8e79c1dc96 ticket: mark plugin service lifecycle done 2026-06-25 06:51:18 +09:00
Hare 000afbbe19 merge: 00001KVXK0WDQ plugin service lifecycle 2026-06-25 06:49:43 +09:00
Hare db1a2f567b ticket: approve plugin service lifecycle 2026-06-25 06:49:43 +09:00
Hare bdd05dce4d ticket: record plugin service implementation report 2026-06-25 06:44:06 +09:00
Hare 4e138b7e36 feat: add plugin service ingress queue 2026-06-25 06:42:56 +09:00
Hare 1839acb3d0 ticket: record plugin service coder start 2026-06-25 06:24:15 +09:00
Hare f26c7e0d09 ticket: accept plugin service lifecycle task 2026-06-25 06:23:22 +09:00
Hare 437ef5b56b ticket: route plugin service lifecycle task 2026-06-25 06:23:06 +09:00
Hare 7d64b443f2 ticket: mark plugin manifest rejection done 2026-06-25 06:20:51 +09:00
Hare 449745ee24 merge: 00001KVXK0WDH plugin manifest rejection 2026-06-25 06:19:01 +09:00
Hare e66efc746f ticket: approve plugin manifest rejection 2026-06-25 06:19:01 +09:00
Hare 436bcc812d ticket: record plugin manifest rejection report 2026-06-25 06:12:05 +09:00
Hare 6086099fe4 feat: reject legacy plugin runtime manifests 2026-06-25 06:11:03 +09:00
Hare 390f468471 ticket: record plugin manifest coder start 2026-06-25 05:54:51 +09:00
Hare ef1d8d9af2 ticket: accept plugin manifest rejection task 2026-06-25 05:53:53 +09:00
Hare ceb7b95096 ticket: route plugin manifest rejection 2026-06-25 05:53:14 +09:00
Hare 237c985f2c ticket: correct legacy wasm merge record 2026-06-25 05:51:08 +09:00
Hare 66c5be16f8 ticket: mark legacy wasm removal done 2026-06-25 05:50:51 +09:00
Hare bedbb670e4 merge: 00001KVXK0WD3 legacy wasm removal 2026-06-25 05:49:01 +09:00
Hare 0591fd528c ticket: approve legacy wasm removal 2026-06-25 05:49:01 +09:00
Hare 19ff3724ed ticket: record legacy wasm removal report 2026-06-25 05:43:58 +09:00
Hare 741d71327a refactor: remove legacy wasm plugin runtime 2026-06-25 05:42:52 +09:00
Hare 27117f3246 ticket: record legacy wasm coder start 2026-06-25 05:15:02 +09:00
Hare 1b5a39dbc9 ticket: accept legacy wasm removal task 2026-06-25 05:14:13 +09:00
Hare f65f0e3b8f ticket: route plugin runtime cleanup chain 2026-06-25 05:13:59 +09:00
Hare e2df9f9493 ticket: queue 00001KVXK0WEA 2026-06-25 05:12:05 +09:00
Hare 959b497135 ticket: queue 00001KVXK0WE4 2026-06-25 05:12:03 +09:00
Hare ef08b873ce ticket: queue 00001KVXK0WDX 2026-06-25 05:12:02 +09:00
Hare 2f975808bb ticket: queue 00001KVXK0WDQ 2026-06-25 05:12:00 +09:00
Hare 273f10e954 ticket: queue 00001KVXK0WDH 2026-06-25 05:11:58 +09:00
Hare f349738257 ticket: queue 00001KVXK0WD3 2026-06-25 05:11:56 +09:00
Hare 577bf75051 ticket: split plugin runtime redesign 2026-06-25 04:56:03 +09:00
Hare 79df31ccf6 ticket: return plugin runtime redesign to planning 2026-06-25 04:40:59 +09:00
Hare 72812878c0 ticket: queue 00001KVXHVCR5 2026-06-25 04:39:36 +09:00
Hare cea115ecd9 merge: sync orchestration before queue 00001KVXHVCR5 2026-06-25 04:39:36 +09:00
Hare 4c3b15d8d6 ticket: plan plugin wasm runtime redesign 2026-06-25 04:39:33 +09:00
Hare 45d21ac032 ticket: mark backend orchestrator design done 2026-06-25 04:15:48 +09:00
Hare 4e713fce19 merge: 00001KVWSQM22 backend orchestrator design 2026-06-25 04:15:14 +09:00
Hare 428b7d0fef ticket: approve backend orchestrator design 2026-06-25 04:15:14 +09:00
Hare 5ddc8dceac ticket: record backend orchestrator design report 2026-06-25 04:12:38 +09:00
Hare f901b9bee3 docs: design backend internal orchestrator runtime 2026-06-25 04:11:47 +09:00
Hare 2f9604a12f ticket: record backend orchestrator coder start 2026-06-25 04:08:16 +09:00
Hare 893122781d ticket: accept backend orchestrator runtime plan 2026-06-25 04:07:16 +09:00
Hare f367d73231 ticket: queue 00001KVWSQM22 2026-06-25 04:04:55 +09:00
Hare 5fa480904d merge: sync orchestration before queue 00001KVWSQM22 2026-06-25 04:04:55 +09:00
Hare 0618de21eb ticket: plan backend internal orchestrator 2026-06-25 03:49:58 +09:00
Hare d5012d3e16 ticket: mark cleanup cli done 2026-06-24 21:36:19 +09:00
Hare 28d53aadf2 nix: update yoi cleanup cargo hash 2026-06-24 21:35:28 +09:00
Hare 4fb75ec324 merge: 00001KVWPVHFJ storage cleanup cli
# Conflicts:
#	.yoi/tickets/00001KVWPVHFJ/item.md
#	.yoi/tickets/00001KVWPVHFJ/thread.md
2026-06-24 21:30:57 +09:00
Hare 091ee764a4 ticket: approve cleanup cli implementation 2026-06-24 21:30:49 +09:00
Hare 439f967cb8 chore: record cleanup cli implementation report 2026-06-24 21:22:58 +09:00
Hare 80d6861aba feat: add pod and session cleanup CLI 2026-06-24 21:22:19 +09:00
Hare d2ec533585 ticket: mark thinking grouping done 2026-06-24 21:20:15 +09:00
Hare b52b7c963c merge: 00001KVWPW3KX thinking grouping
# Conflicts:
#	.yoi/tickets/00001KVWPW3KX/item.md
#	.yoi/tickets/00001KVWPW3KX/thread.md
2026-06-24 21:19:16 +09:00
Hare 89910a1a29 ticket: approve thinking grouping implementation 2026-06-24 21:18:56 +09:00
Hare 7ee2b78bbb ticket: report thinking grouping implementation 2026-06-24 21:14:43 +09:00
Hare 0b2ce6ca1f tui: group consecutive thinking blocks 2026-06-24 21:14:06 +09:00
Hare bca8ba6ed9 ticket: record ui and cleanup coder starts 2026-06-24 21:05:18 +09:00
Hare 5c9331e848 ticket: accept queued ui and cleanup tasks 2026-06-24 21:04:13 +09:00
Hare bb6bc9f6a1 ticket: queue 00001KVWPVHFJ 2026-06-24 21:01:42 +09:00
Hare 845817d3bf ticket: queue 00001KVWPW3KX 2026-06-24 21:01:41 +09:00
Hare 1d98921a45 merge: sync orchestration before queue 00001KVWPW3KX 2026-06-24 21:01:41 +09:00
Hare 7ede927d5a ticket: add pod cleanup and thinking aggregation plans 2026-06-24 20:57:23 +09:00
Hare 6081448a7e fix: trace codex requests and clear reasoning snapshots 2026-06-24 20:37:42 +09:00
Hare 70162d5001 ticket: mark worker registry done 2026-06-24 20:15:18 +09:00
Hare b975812c18 nix: update yoi cargo hash 2026-06-24 20:14:41 +09:00
Hare 1251c0ca70 merge: 00001KVWECEQG worker runtime registry 2026-06-24 20:11:00 +09:00
Hare dd40c41f45 ticket: approve worker registry implementation 2026-06-24 20:11:00 +09:00
Hare 428d255b27 ticket: record worker registry fixes 2026-06-24 20:09:08 +09:00
Hare 38d25582b2 fix: make worker runtime ids resolvable 2026-06-24 20:08:23 +09:00
Hare cdc0a5da33 ticket: request worker registry changes 2026-06-24 20:01:52 +09:00
Hare c96cc49d0a ticket: record worker registry implementation report 2026-06-24 19:57:10 +09:00
Hare 9bd1550715 feat: add worker runtime registry overview 2026-06-24 19:56:08 +09:00
Hare 81fa035a1c ticket: record worker registry orchestration start 2026-06-24 19:38:55 +09:00
Hare 371fd7c6e5 ticket: accept worker runtime registry task 2026-06-24 19:37:58 +09:00
Hare d231f41300 ticket: mark worker runtime spawn done 2026-06-24 19:35:06 +09:00
Hare 97555bb5a1 merge: 00001KVTNAY20 worker runtime spawn 2026-06-24 19:33:35 +09:00
Hare d62ab6e1de docs: record worker runtime implementation report 2026-06-24 19:26:14 +09:00
Hare 217a4828d7 feat: abstract worker runtime spawn boundary 2026-06-24 19:24:18 +09:00
Hare bc2b8513f7 ticket: defer worker runtime registry routing 2026-06-24 18:24:57 +09:00
Hare 73122c10fd ticket: queue 00001KVWECEQG 2026-06-24 18:22:55 +09:00
Hare b84db7fac7 merge: orchestration branch 2026-06-24 18:21:37 +09:00
Hare 911df3df77 ticket: mark worker runtime registry ready 2026-06-24 18:21:37 +09:00
Hare acf1f5fb53 ticket: plan worker runtime registry 2026-06-24 18:12:39 +09:00
Hare 1044b39c3f ticket: plan ticket management api ui 2026-06-24 04:42:37 +09:00
Hare a729d68600 ticket: record coder startup blocker 2026-06-24 04:33:52 +09:00
Hare b83886554f ticket: record worker runtime orchestration start 2026-06-24 04:32:02 +09:00
Hare 5a8bcebdf4 ticket: accept worker runtime spawn task 2026-06-24 04:27:45 +09:00
Hare a479d3e32d ticket: queue 00001KVTNAY20 2026-06-24 04:25:09 +09:00
Hare f399d7383c ticket: mark worker spawn abstraction ready 2026-06-24 04:25:09 +09:00
Hare e8e019eb76 fix: keep initialized ticket directory 2026-06-24 03:26:22 +09:00
Hare 3c2fd5d760 ticket: separate pod launch from orchestration 2026-06-24 03:15:07 +09:00
Hare 1ca5663298 fix: harden StopPod registry cleanup 2026-06-24 00:23:10 +09:00
Hare b28d64c3c6 ticket: close 00001KVSMJJNV 2026-06-23 22:56:21 +09:00
Hare 76c800542c merge: 00001KVSMJJNV paused ctrl-x cancel 2026-06-23 22:54:38 +09:00
Hare cca073f8aa ticket: approve 00001KVSMJJNV implementation 2026-06-23 22:53:20 +09:00
Hare c0f4c184ca ticket: report 00001KVSMJJNV review fix 2026-06-23 22:49:40 +09:00
Hare 8c8fb01426 fix: log paused cancel lifecycle explicitly 2026-06-23 22:48:21 +09:00
Hare 052408392d ticket: update 00001KVSMJJNV review metadata 2026-06-23 22:43:05 +09:00
Hare cfb215ab0c ticket: review 00001KVSMJJNV changes 2026-06-23 22:42:52 +09:00
Hare 3501e0dffe ticket: update 00001KVSMJJNV metadata 2026-06-23 22:38:06 +09:00
Hare af683af2d2 ticket: report 00001KVSMJJNV implementation 2026-06-23 22:38:01 +09:00
Hare 90b1a1fccb tui: cancel paused turns with ctrl-x 2026-06-23 22:37:40 +09:00
Hare 5954021cc5 ticket: accept 00001KVSMJJNV 2026-06-23 22:21:56 +09:00
Hare 108088d811 ticket: queue 00001KVSMJJNV 2026-06-23 17:41:00 +09:00
Hare ca2ad18ded merge: sync orchestration before queue 00001KVSMJJNV 2026-06-23 17:41:00 +09:00
Hare d2e6a2a1a7 ui: simplify workspace sidebar identity 2026-06-23 16:56:00 +09:00
Hare e275a80f4c ticket: close workspace identity 2026-06-23 16:39:16 +09:00
Hare 2745f3d516 merge: workspace identity persistence 2026-06-23 16:35:17 +09:00
Hare 8f7b87a29e ticket: record workspace identity approval 2026-06-23 16:35:13 +09:00
Hare 2a7b659be4 ticket: restart workspace identity review 2026-06-23 16:31:38 +09:00
Hare 3dd680683c ticket: record workspace identity fix 2026-06-23 16:30:50 +09:00
Hare 49c9e19074 fix: return persisted workspace identity 2026-06-23 16:29:48 +09:00
Hare ddd2f86e26 ticket: reroute workspace identity fixes 2026-06-23 16:25:39 +09:00
Hare fac79dc962 ticket: request workspace identity changes 2026-06-23 16:25:03 +09:00
Hare 71a39002fb ticket: close dashboard selection 2026-06-23 16:24:24 +09:00
Hare 58904c441a merge: dashboard no auto selection 2026-06-23 16:23:16 +09:00
Hare 1e812b793b ticket: record dashboard selection review 2026-06-23 16:23:11 +09:00
Hare ff8aa6bcbf ticket: start dashboard selection review 2026-06-23 16:16:42 +09:00
Hare 2b213a8add ticket: record dashboard selection implementation 2026-06-23 16:15:59 +09:00
Hare 021aca8d5e ticket: start workspace identity review 2026-06-23 16:14:48 +09:00
Hare 21b14ba440 ticket: record workspace identity implementation 2026-06-23 16:13:56 +09:00
Hare 5c242d9620 fix: keep dashboard row selection explicit 2026-06-23 16:13:37 +09:00
Hare 31565c9b9e feat: persist workspace identity 2026-06-23 16:12:39 +09:00
Hare 99f31e0055 ticket: start workspace identity and selection work 2026-06-23 15:51:53 +09:00
Hare 4cda83b748 ticket: accept workspace identity and selection work 2026-06-23 15:50:36 +09:00
Hare 13e76d3544 ticket: queue 00001KVSKGDYS 2026-06-23 15:47:18 +09:00
Hare 85b6d9f027 ticket: queue 00001KVSKJ0EA 2026-06-23 15:47:17 +09:00
Hare 8daf9eacb7 merge: sync orchestration before queue 00001KVSKJ0EA 2026-06-23 15:47:17 +09:00
Hare 48672e4317 fix: canonicalize workspace server root 2026-06-23 15:47:11 +09:00
Hare eb998f0ad4 ticket: add workspace identity initialization 2026-06-23 15:44:29 +09:00
Hare 9cbb5b9b71 ticket: record dashboard hint cleanup cleanup 2026-06-23 15:33:30 +09:00
Hare 108666664a ticket: complete dashboard hint cleanup 2026-06-23 15:32:57 +09:00
Hare 5abf16f9e6 merge: dashboard hint cleanup 2026-06-23 15:31:31 +09:00
Hare 78c98a34d1 ticket: approve dashboard hint cleanup 2026-06-23 15:31:31 +09:00
Hare 68f1ddbd5a ticket: start dashboard hint cleanup review 2026-06-23 15:29:26 +09:00
Hare 71284cdc92 ticket: record dashboard hint cleanup implementation 2026-06-23 15:28:24 +09:00
Hare 03ad525fcc tui: trim dashboard redundant hints 2026-06-23 15:27:04 +09:00
Hare af9e940873 ticket: record protocol ts cleanup 2026-06-23 15:22:07 +09:00
Hare b547203fde ticket: complete protocol ts generation 2026-06-23 15:21:30 +09:00
Hare 9728b533b4 merge: protocol typescript generation 2026-06-23 15:17:09 +09:00
Hare 9c4d1559fd ticket: approve protocol ts generation 2026-06-23 15:17:09 +09:00
Hare e0ec8ce78a ticket: record kanban lazy rows cleanup 2026-06-23 15:16:19 +09:00
Hare 9de04f7266 ticket: complete kanban lazy rows 2026-06-23 15:15:33 +09:00
Hare eea26f9174 merge: kanban lazy rows 2026-06-23 15:13:43 +09:00
Hare 0b1e9fdc5b ticket: approve kanban lazy rows 2026-06-23 15:13:43 +09:00
Hare 277af3dec3 ticket: start dashboard hint cleanup 2026-06-23 15:13:01 +09:00
Hare 3bfd1ca07d ticket: accept dashboard hint cleanup 2026-06-23 15:11:48 +09:00
Hare 0e635ba6b4 ticket: start protocol ts review 2026-06-23 15:10:38 +09:00
Hare 017ac70876 ticket: record protocol ts generation 2026-06-23 15:09:50 +09:00
Hare a13fb6933b protocol: generate workspace TypeScript types 2026-06-23 15:08:22 +09:00
Hare 6f68bb8d95 web: group repository ticket kanban rows 2026-06-23 15:04:03 +09:00
148 changed files with 15250 additions and 2955 deletions
+34 -15
View File
@@ -2,8 +2,8 @@
title: "Plugin platform roadmap"
state: "active"
created_at: "2026-06-19T13:18:58Z"
updated_at: "2026-06-19T13:18:58Z"
linked_tickets: ["00001KV5R5V2S", "00001KV5W3PHA", "00001KV5W3PHW", "00001KV5W3PJ3", "00001KVFD3YSV", "00001KVFDX9AF", "00001KVFDX9AY", "00001KVG0HR96"]
updated_at: "2026-06-24T19:55:00Z"
linked_tickets: ["00001KV5R5V2S", "00001KV5W3PHA", "00001KV5W3PHW", "00001KV5W3PJ3", "00001KVFD3YSV", "00001KVFDX9AF", "00001KVFDX9AY", "00001KVG0HR96", "00001KVXHVCR5", "00001KVXK0WD3", "00001KVXK0WDH", "00001KVXK0WDQ", "00001KVXK0WDX", "00001KVXK0WE4", "00001KVXK0WEA"]
---
## Goal
@@ -42,10 +42,10 @@ Research of common Wasm extension systems points to the same pattern: mature sys
- Package presence never registers a Tool/Hook, executes Wasm, starts a Service, reads files, opens network, or injects context.
- Explicit enablement and Plugin grants are required before registration/execution/host API use.
- Tool calls/results continue through ordinary ToolRegistry and Worker history paths.
- Treat Component Model as the preferred future Plugin runtime shape.
- Treat Component Model as the active Plugin runtime shape before public release.
- New typed Plugin host APIs should be designed in WIT-compatible terms.
- `runtime.kind = "wasm-component"` should become the preferred runtime once implemented.
- Current `yoi-plugin-wasm-1` raw core-Wasm ABI remains a compatibility / migration bridge until Component Model execution and authoring are validated.
- `runtime.kind = "wasm-component"` is the current Plugin runtime authority for new work.
- The earlier `yoi-plugin-wasm-1` raw core-Wasm compatibility bridge is retired from the active roadmap because Plugin has not been publicly released and compatibility would preserve the wrong boundary.
- Sequence the platform in usable layers:
1. Package discovery / explicit enablement / digest-pinned restore. Completed foundation.
2. Tool surface registration. Completed foundation.
@@ -53,30 +53,49 @@ Research of common Wasm extension systems points to the same pattern: mature sys
4. Permission grants. Completed foundation.
5. Read-only Plugin CLI inspection (`yoi plugin list/show`) for debugging discovery/enablement/grants/runtime metadata.
6. `https` and `fs` host APIs for Tool Plugins, grant-gated and WIT-compatible.
7. Component Model runtime migration and authoring model.
8. Guest SDK/PDK, examples, `check`/`pack`/`new` authoring tooling.
9. Service / Ingress / WebSocket or inbound HTTP only after Tool + host API foundations are stable.
7. Remove the raw core-Wasm compatibility bridge and reject legacy runtime manifests.
8. Component Model runtime and authoring model become the only active Plugin runtime path.
9. Guest SDK/PDK, examples, `check`/`pack`/`new` authoring tooling target Component Model only.
10. Service / Ingress runtime is developed as host-managed lifecycle, event queue, output command, and diagnostics slices.
11. WebSocket support for long-lived integrations is host-owned connection driver + ingress event delivery + output command, not Plugin-owned polling with `recv(timeout)`.
- Keep Discord-style bridge goals split into two stages.
- Outbound Discord/webhook Tool is possible after `https`.
- Bidirectional Discord bridge requires Service + Ingress + WebSocket or inbound HTTP and host routing policy.
## Current implementation split
The broad Plugin runtime redesign is tracked by `00001KVXHVCR5` as context only; implementation should proceed through concrete Tickets instead of routing that umbrella as a single coding task.
1. `00001KVXK0WD3` Remove legacy raw WASM Plugin runtime.
- Deletes the active `LegacyToolAdapter` / raw-Wasm execution path.
2. `00001KVXK0WDH` Reject legacy Plugin runtime in manifest and CLI diagnostics.
- Makes `plugin.toml`, `yoi plugin check/list/show`, docs, and fixtures reflect Component Model only runtime authority.
3. `00001KVXK0WDQ` Define Plugin Service lifecycle and ingress queue runtime.
- Adds host-managed lifecycle, bounded queue, serial dispatch, backpressure, timeout, and diagnostics.
4. `00001KVXK0WDX` Add Plugin service output command model.
- Lets service handlers request side effects as grant-checked commands rather than ambient authority.
5. `00001KVXK0WE4` Add host-owned WebSocket driver for Plugin services.
- Converts incoming WS frames into ingress events and sends outbound frames through output commands.
6. `00001KVXK0WEA` Update Plugin WIT PDK templates for service event runtime.
- Aligns authoring API, WIT, PDK, templates, and docs with the new event/command execution model.
## Success criteria / exit conditions
- Users can inspect Plugin discovery/enablement/grant/runtime state through a read-only CLI without executing Plugin code.
- Plugin authors can build a Tool Plugin without writing raw memory/pointer ABI plumbing.
- Tool Plugins can safely call grant-gated `https` and `fs` host APIs.
- Component Model support is available or a documented migration path is active, with WIT-compatible host API types and measured packaging/runtime impact.
- Component Model is the only active Plugin runtime path, with WIT-compatible host API types and measured packaging/runtime impact.
- Plugin grants remain authoritative over registration, execution, and host API calls.
- Plugin diagnostics explain missing package, invalid manifest, digest/version mismatch, missing grant, rejected schema, runtime mismatch, and unsupported host API cases safely.
- Existing raw core-Wasm Plugin tests either remain passing or have an explicit compatibility/deprecation decision.
- Documentation covers package format, runtime kinds, Component Model direction, host API authority, authoring SDK/templates, and operational debugging.
- Service/Ingress work starts only after Tool Plugin + host API + diagnostics foundations are usable.
- Plugin diagnostics explain missing package, invalid manifest, digest/version mismatch, missing grant, rejected schema, runtime mismatch, legacy runtime rejection, and unsupported host API cases safely.
- Raw core-Wasm Plugin compatibility is removed before public release; tests and docs no longer treat it as a current runtime.
- Documentation covers package format, Component Model runtime, host API authority, authoring SDK/templates, Service/Ingress event runtime, and operational debugging.
- Service/Ingress work is host-managed: services have lifecycle/status, ingress uses bounded queues, side effects are output commands, and WebSocket integrations use host-owned connection drivers.
## Decision context
- This Objective is roadmap context, not Ticket authority. Implementation still requires reading concrete Ticket bodies, threads, artifacts, and relations.
- Component Model direction supersedes making Yoi's custom raw ABI the long-term authoring interface, but does not require an immediate flag-day rewrite.
- Component Model direction supersedes Yoi's custom raw ABI as both long-term and current active Plugin runtime authority before public release.
- `https` / `fs` work should avoid choices that conflict with later WIT typed interfaces.
- Guest SDK work should either target Component Model directly or keep the raw ABI wrapper clearly transitional.
- Guest SDK work targets Component Model directly; raw ABI wrappers are not a supported transitional authoring path.
- Plugin and MCP remain separate. Component Model adoption for Plugin does not imply MCP server execution, MCP prompt/resource injection, or MCP trust policy changes.
- Plugin surfaces remain Tool / Hook / Service / Ingress; outbound side effects are Tool metadata and host API grants, not a separate surface.
+2 -2
View File
@@ -1,8 +1,8 @@
---
title: 'Generate Workspace web TypeScript types from protocol crate'
state: 'inprogress'
state: 'closed'
created_at: '2026-06-23T05:13:22Z'
updated_at: '2026-06-23T05:42:14Z'
updated_at: '2026-06-23T06:22:01Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-23T05:40:01Z'
+29
View File
@@ -0,0 +1,29 @@
Protocol crate 由来の Workspace web TypeScript type generation を実装し、Orchestrator worktree の `orchestration` branch に統合した。
主な成果:
- `crates/protocol``stream` module / tokio dependency を default `stream` feature に分離し、DTO-only build で tokio を不要にした。
- Optional `typescript` feature と `ts-rs` による TypeScript export を追加。
- Protocol DTOs に `cfg_attr(feature = "typescript", derive(ts_rs::TS))` を追加。
- Deterministic generator を追加:
- `crates/protocol/src/typescript.rs`
- `crates/protocol/examples/generate_typescript.rs`
- Drift check を追加:
- `cargo test -p protocol --features typescript generated_protocol_types_are_current`
- Generated artifact を追加:
- `web/workspace/src/lib/generated/protocol.ts`
- Workspace web が generated root protocol types を re-export:
- `PodProtocolMethod`
- `PodProtocolEvent`
- `PodProtocolSegment`
- Workspace backend extension-point notes に、browser が Pod Unix socket に直接接続せず、将来の backend proxy が Worker identity と method allow/block boundary を enforce する方針を記録。
- `Cargo.lock``package.nix` cargo hash を更新。
統合・検証:
- Merge commit: `9728b533 merge: protocol typescript generation`
- Implementation commit: `a13fb693 protocol: generate workspace TypeScript types`
- Reviewer final verdict: approve
- Validation passed: `cargo fmt --check`, `git diff --check HEAD^1..HEAD`, `cargo test -p protocol`, `cargo test -p protocol --features typescript generated_protocol_types_are_current`, `cargo test -p protocol --features typescript`, `cargo check -p protocol --target wasm32-unknown-unknown --no-default-features`, `deno task check`, `deno task build`, `cargo run -p yoi -- ticket doctor`, and `nix build .#yoi --no-link`
既知の非ブロッキング事項:
- `ts-rs``#[serde(other)]` on `Segment::Unknown` に warning を出すが、generated artifact には `{ "kind": "unknown" }` が含まれ、current validation は pass。
- 一部 `Option<T>` + `skip_serializing_if` fields は TS で optional field ではなく required nullable に出る。将来 UI が該当 field を使う際は注意。
+264
View File
@@ -126,3 +126,267 @@ Next action:
- Wait for Coder implementation report, then spawn Reviewer read-only for the implementation diff。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T06:09:42Z -->
## Implementation report
Coder implementation report received from `yoi-coder-00001KVSEBF56`.
Implementation commit:
- `a13fb693 protocol: generate workspace TypeScript types`
Changed areas:
- `crates/protocol`
- Made `stream` optional behind default feature `stream`
- Added optional `typescript` feature using `ts-rs`
- Added `cfg_attr(feature = "typescript", derive(ts_rs::TS))` to protocol DTOs。
- Added deterministic generator module and example:
- `crates/protocol/src/typescript.rs`
- `crates/protocol/examples/generate_typescript.rs`
- Added drift check:
- `cargo test -p protocol --features typescript generated_protocol_types_are_current`
- `web/workspace`
- Added generated TypeScript artifact:
- `web/workspace/src/lib/generated/protocol.ts`
- Re-exported generated root protocol types from workspace sidebar types:
- `PodProtocolMethod`
- `PodProtocolEvent`
- `PodProtocolSegment`
- `crates/workspace-server`
- Updated extension-point notes to explicitly preserve no direct browser-to-Pod socket exposure and require future Workspace-server proxy enforcement of Worker identity / method allow-block boundaries。
- Packaging/dependencies:
- Updated `Cargo.lock`
- Updated `package.nix` `cargoHash`
Coder validation reported:
- `cargo fmt --check`: passed
- `cargo test -p protocol`: passed
- `cargo test -p protocol --features typescript generated_protocol_types_are_current`: passed
- `cargo test -p protocol --features typescript`: passed
- `cargo check -p protocol --target wasm32-unknown-unknown --no-default-features`: passed
- `cd web/workspace && deno task check && deno task build`: passed
- `cargo run -p yoi -- ticket doctor`: passed
- `git diff --check`: passed
- `nix build .#yoi --no-link`: passed
Generated artifact cleanup:
- Only intended generated TypeScript import artifact is tracked。
- Orchestrator removed ignored `web/workspace/node_modules/` before Reviewer routing; no ignored frontend artifacts remain under `web/workspace`
Orchestrator evidence checked:
- Worktree clean on `impl/00001KVSEBF56-protocol-typescript-types` at `a13fb693`
- Diff `746c51c7..HEAD` touches expected protocol/frontend/workspace-server/package files only。
- `git diff --check 746c51c7..HEAD`: passed。
Next action:
- Route to read-only Reviewer Pod for acceptance review。
---
<!-- event: comment author: yoi-orchestrator at: 2026-06-23T06:10:31Z -->
## Comment
Review routing:
Spawned read-only Reviewer Pod:
- `yoi-reviewer-00001KVSEBF56-r1`
Review target:
- Worktree: `/home/hare/Projects/yoi/.worktree/00001KVSEBF56-protocol-typescript-types`
- Branch: `impl/00001KVSEBF56-protocol-typescript-types`
- Baseline: `746c51c7`
- Implementation commit: `a13fb693 protocol: generate workspace TypeScript types`
- Diff: `746c51c7..a13fb693`
Reviewer scope:
- read-only `/home/hare/Projects/yoi`
Reviewer focus:
- Generated TS shape vs serde wire shape。
- `Method` / `Event` / `Segment` coverage。
- Deterministic drift check。
- `stream` feature gating and wasm/no-default protocol compile。
- No direct browser-to-Pod socket implication。
- Package/Nix updates。
Orchestrator will wait for reviewer verdict before integration。
---
<!-- event: review author: yoi-reviewer-00001KVSEBF56-r1 at: 2026-06-23T06:16:54Z status: approve -->
## Review: approve
Verdict: `approve`
確認範囲:
- Ticket authority:
- `.yoi/tickets/00001KVSEBF56/item.md`
- `.yoi/tickets/00001KVSEBF56/thread.md`
- Diff `746c51c7..a13fb693`:
- `crates/protocol/Cargo.toml`
- `crates/protocol/src/lib.rs`
- `crates/protocol/src/typescript.rs`
- `crates/protocol/examples/generate_typescript.rs`
- `web/workspace/src/lib/generated/protocol.ts`
- `web/workspace/src/lib/workspace-sidebar/types.ts`
- `crates/workspace-server/src/server.rs`
- `Cargo.lock`
- `package.nix`
Blocking issues:
- None。
Non-blocking concerns:
- `cargo test -p protocol --features typescript` emits `ts-rs` warnings for `#[serde(other)]` on `Segment::Unknown`: `ts-rs failed to parse this attribute. It will be ignored.` The generated artifact still includes `{ "kind": "unknown" }`, and this does not break current validation, but TS generator is not fully understanding serde fallback semantics。
- Some `Option<T>` fields with `skip_serializing_if = "Option::is_none"` are generated as required nullable rather than optional nullable, e.g. `RewindTarget.disabled_reason: string | null` and `warning: string | null`. Acceptable for this initial generated-types ticket, but future UI code should be careful because runtime JSON may omit those fields。
Review findings:
- `crates/protocol` remains Rust DTO authority。
- `stream` is now an optional default feature:
- `default = ["stream"]`
- `stream = ["dep:tokio"]`
- `tokio` is optional。
- DTO-only/no-default protocol build compiles for wasm without `tokio`
- `typescript` feature is optional and uses `ts-rs`
- Generated TS root includes `Method`, `Event`, `Segment`, plus related DTOs such as `PodEvent`, `ScopeRule`, `Greeting`, `InFlight*`, rewind DTOs, completion DTOs, status/result/error enums。
- Generated TS file is committed at `web/workspace/src/lib/generated/protocol.ts`
- Workspace web re-exports generated root protocol types as `PodProtocolMethod`, `PodProtocolEvent`, and `PodProtocolSegment`
- Generated TS tagged enums and casing match observed serde wire shape for critical roots:
- `Method`: `{ "method": "...", "params": ... }`
- `Event`: `{ "event": "...", "data": ... }`
- `Segment` / `PodEvent` / `InFlightBlock`: internal `"kind"` tags with snake_case variants。
- Drift check `generated_protocol_types_are_current` compares committed generated artifact to deterministic generator output。
- Backend proxy stance is represented in Workspace API extension-point notes and does not add direct browser-to-Pod socket access or broad Worker operation UI。
Reviewer validation:
- `cargo fmt --check`: passed
- `git diff --check 746c51c7..HEAD`: passed
- `cargo test -p protocol`: passed (`40 passed`)
- `cargo test -p protocol --features typescript generated_protocol_types_are_current`: passed; emitted non-blocking `ts-rs` warning for `serde(other)`
- `cargo test -p protocol --features typescript`: passed (`41 passed`); emitted same non-blocking warning。
- `cargo check -p protocol --target wasm32-unknown-unknown --no-default-features`: passed
- `cd web/workspace && deno task check && deno task build`: passed (`svelte-check found 0 errors and 0 warnings`; build succeeded)
- `cargo run -p yoi -- ticket doctor`: passed (`doctor: ok`)
- `nix build .#yoi --no-link`: passed
Generated artifact status:
- `web/workspace/src/lib/generated/protocol.ts` remained unchanged after validation。
- SHA-256 after validation: `3984a043ded1179545656e5402171c49b2d22b5529aa17f016b675e394b7e553`
Worktree status:
- Clean on `impl/00001KVSEBF56-protocol-typescript-types` at `a13fb693`
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T06:20:49Z -->
## Implementation report
Reviewer r1 approvalを受けて、実装ブランチを Orchestrator worktree の `orchestration` branch に統合した。
統合:
- Merge commit: `9728b533 merge: protocol typescript generation`
- Merged branch: `impl/00001KVSEBF56-protocol-typescript-types`
- Implementation commit:
- `a13fb693 protocol: generate workspace TypeScript types`
Orchestrator worktree validation:
- `cargo fmt --check`: passed
- `git diff --check HEAD^1..HEAD`: passed
- `cargo test -p protocol`: passed
- `cargo test -p protocol --features typescript generated_protocol_types_are_current`: passed
- `cargo test -p protocol --features typescript`: passed
- Non-blocking `ts-rs` warning for `#[serde(other)]` on `Segment::Unknown` remains as reviewer noted。
- `cargo check -p protocol --target wasm32-unknown-unknown --no-default-features`: passed
- `cd web/workspace && rm -rf node_modules .svelte-kit build && deno task check`: passed (`svelte-check found 0 errors and 0 warnings`)
- `cd web/workspace && deno task build`: passed
- `cargo run -p yoi -- ticket doctor`: passed (`doctor: ok`)
- `nix build .#yoi --no-link`: passed
Validation generated ignored frontend artifacts in Orchestrator worktree:
- `web/workspace/node_modules/`
- `web/workspace/.svelte-kit/`
- `web/workspace/build/`
These were removed after validation. Final Orchestrator worktree status after validation cleanup is clean on `orchestration` at `9728b533`
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-23T06:21:01Z from: inprogress to: done reason: implementation_merged_and_validated field: state -->
## State changed
Reviewer approval、Orchestrator worktree への統合、protocol tests/drift check/wasm no-default check、Deno check/build、Ticket doctor、Nix build が完了したため `done` に遷移する。
---
<!-- event: state_changed author: hare at: 2026-06-23T06:21:19Z from: done to: closed reason: closed field: state -->
## State changed
Ticket を closed にしました。
---
<!-- event: close author: hare at: 2026-06-23T06:21:19Z status: closed -->
## 完了
Protocol crate 由来の Workspace web TypeScript type generation を実装し、Orchestrator worktree の `orchestration` branch に統合した。
主な成果:
- `crates/protocol``stream` module / tokio dependency を default `stream` feature に分離し、DTO-only build で tokio を不要にした。
- Optional `typescript` feature と `ts-rs` による TypeScript export を追加。
- Protocol DTOs に `cfg_attr(feature = "typescript", derive(ts_rs::TS))` を追加。
- Deterministic generator を追加:
- `crates/protocol/src/typescript.rs`
- `crates/protocol/examples/generate_typescript.rs`
- Drift check を追加:
- `cargo test -p protocol --features typescript generated_protocol_types_are_current`
- Generated artifact を追加:
- `web/workspace/src/lib/generated/protocol.ts`
- Workspace web が generated root protocol types を re-export:
- `PodProtocolMethod`
- `PodProtocolEvent`
- `PodProtocolSegment`
- Workspace backend extension-point notes に、browser が Pod Unix socket に直接接続せず、将来の backend proxy が Worker identity と method allow/block boundary を enforce する方針を記録。
- `Cargo.lock``package.nix` cargo hash を更新。
統合・検証:
- Merge commit: `9728b533 merge: protocol typescript generation`
- Implementation commit: `a13fb693 protocol: generate workspace TypeScript types`
- Reviewer final verdict: approve
- Validation passed: `cargo fmt --check`, `git diff --check HEAD^1..HEAD`, `cargo test -p protocol`, `cargo test -p protocol --features typescript generated_protocol_types_are_current`, `cargo test -p protocol --features typescript`, `cargo check -p protocol --target wasm32-unknown-unknown --no-default-features`, `deno task check`, `deno task build`, `cargo run -p yoi -- ticket doctor`, and `nix build .#yoi --no-link`
既知の非ブロッキング事項:
- `ts-rs``#[serde(other)]` on `Segment::Unknown` に warning を出すが、generated artifact には `{ "kind": "unknown" }` が含まれ、current validation は pass。
- 一部 `Option<T>` + `skip_serializing_if` fields は TS で optional field ではなく required nullable に出る。将来 UI が該当 field を使う際は注意。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T06:22:01Z -->
## Implementation report
Post-close cleanup completed。
- Stopped child Pods and reclaimed scope:
- `yoi-coder-00001KVSEBF56`
- `yoi-reviewer-00001KVSEBF56-r1`
- Removed ignored frontend validation artifacts from child worktree before worktree removal if present:
- `web/workspace/node_modules/`
- `web/workspace/.svelte-kit/`
- `web/workspace/build/`
- Removed implementation worktree:
- `/home/hare/Projects/yoi/.worktree/00001KVSEBF56-protocol-typescript-types`
- Deleted implementation branch:
- `impl/00001KVSEBF56-protocol-typescript-types`
- Orchestrator worktree remains clean on `orchestration` at `b547203f`
Root/original workspace was not used for merge/validation/cleanup operations。
---
@@ -0,0 +1 @@
{"id":"orch-plan-20260623-061132-1","ticket_id":"00001KVSFXY88","kind":"accepted_plan","accepted_plan":{"summary":"Remove redundant Workspace Dashboard title/actionbar key hints and selected-row textual status display while preserving row selection markers and Dashboard keyboard/action semantics, updating render tests accordingly.","branch":"impl/00001KVSFXY88-dashboard-hint-cleanup","worktree":"/home/hare/Projects/yoi/.worktree/00001KVSFXY88-dashboard-hint-cleanup","role_plan":"Orchestrator creates a dedicated child worktree and spawns a narrow-scope Coder. Reviewer will be spawned read-only after Coder reports implementation commit(s). After approval, Orchestrator integrates into `orchestration`, validates TUI tests, records closure, and cleans only the child worktree/branch."},"author":"yoi-orchestrator","at":"2026-06-23T06:11:32Z"}
+2 -2
View File
@@ -1,8 +1,8 @@
---
title: 'TUI Dashboard の冗長な key hints と selected-row 状態表示を削る'
state: 'queued'
state: 'closed'
created_at: '2026-06-23T05:40:56Z'
updated_at: '2026-06-23T06:08:42Z'
updated_at: '2026-06-23T06:33:21Z'
assignee: null
readiness: 'implementation_ready'
risk_flags: ['tui-ux', 'terminal-layout']
+20
View File
@@ -0,0 +1,20 @@
TUI Dashboard の冗長な key hints と selected-row textual status display を削除し、Orchestrator worktree の `orchestration` branch に統合した。
主な成果:
- Top title line から `Row selection`, `blank Enter`, `Tab target` などの key hint guidance を削除。
- Selected Ticket / Pod / Intake / no-row selected textual status line を blank にした。
- Composer bottom actionbar を always-on key hints ではなく notices / diagnostics only に最小化。
- Row marker / highlighting rendering は維持。
- Keyboard/action behavior は変更していない。
- Unused selected-row display-status helper を削除。
- Dashboard render/unit tests を新 display contract に更新し、selected row marker の visibility assertion は維持。
統合・検証:
- Merge commit: `5abf16f9 merge: dashboard hint cleanup`
- Implementation commit: `03ad525f tui: trim dashboard redundant hints`
- Reviewer final verdict: approve
- Validation passed: `cargo fmt --check`, `git diff --check HEAD^1..HEAD`, `cargo test -p tui dashboard --lib`, `cargo test -p tui workspace_panel --lib`, and `cargo run -p yoi -- ticket doctor`
範囲外:
- Console / single-Pod TUI hints は変更していない。
- Manual/PTY `yoi panel` visual check は実施していない。Focused render/unit tests を主 validation とした。
+290
View File
@@ -13,4 +13,294 @@ LocalTicketBackend によって作成されました。
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-23T06:11:11Z -->
## Decision
Routing decision: `implementation_ready_parallel`
Reason:
- Ticket body has concrete Dashboard render targets and acceptance criteria。
- No relations / blockers / orchestration plan records exist。
- Active work `00001KVSEBF56` (protocol TS generation) and `00001KVSGFM65` (Workspace web Kanban review) are separate from TUI Dashboard render code。
- Orchestrator worktree is clean on `orchestration` at `0e635ba6`; target worktree / branch is not present。
- Bounded code map confirms relevant functions in `crates/tui/src/dashboard/render.rs`: `draw_title`, `target_status_line`, `actionbar_left_text`, `actionbar_right_text`, `draw_actionbar`, and tests in `crates/tui/src/dashboard/tests.rs`
IntentPacket:
Intent:
- Remove redundant Dashboard key hints and selected-row textual status while preserving actual row selection visibility and keyboard behavior。
Binding decisions / invariants:
- This Ticket only changes Dashboard display cleanup。
- Console / single-Pod TUI key hints are out of scope。
- Do not remove list row selection markers / highlighting / keyboard navigation visibility。
- Do not reintroduce direct selected-Pod send。
- Companion lifecycle / Orchestrator lifecycle / Ticket workflow semantics must not change。
- Existing Dashboard actions must remain: blank Enter row action, text Enter composer-target action, Tab target switching, Esc clear selection, Pod open/attach, Ticket Intake/Queue flows。
Requirements / acceptance criteria:
- Top title line no longer shows key hint guidance such as `Row selection`, `blank Enter`, `Tab target`
- Composer bottom actionbar no longer shows always-on key hint groups, or is minimized to non-redundant notices only。
- Selected Ticket / selected Pod / selected Intake Pod / no row selected textual status line is removed or no longer recognized as redundant selected-row status。
- List still visibly indicates current selected row。
- Related render/unit tests are updated to the new display specification。
Implementation latitude:
- `target_status_line` layout row may be removed, made empty/minimal, or repurposed only if it does not show redundant selected-row status。
- Actionbar may be fully removed, notice-only, or otherwise minimized depending on layout/tests。
- Keep row markers like `▶` and selected styling unless explicitly escalated。
- If layout row removal creates resize/hitbox complications, choose minimal empty/notice behavior and document the tradeoff。
Escalate if:
- Removing status/actionbar rows makes current composer target/action ambiguous enough to require alternative UI。
- Row selection marker/visibility also needs removal。
- Layout changes cause terminal resize / hitbox / mouse selection side effects that require broader Dashboard layout refactor。
Validation plan:
- `cargo fmt --check`
- `cargo test -p tui dashboard --lib`
- `cargo test -p tui workspace_panel --lib` if affected by shared tests/helpers。
- `git diff --check`
- `cargo run -p yoi -- ticket doctor`
- Manual/PTY `yoi panel` check if practical, but focused render tests are primary。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-23T06:11:22Z from: queued to: inprogress reason: human_authorized_unblocked_dashboard_hint_cleanup field: state -->
## State changed
Human authorized queue routing from Workspace Dashboard. Ticket has concrete Dashboard render acceptance criteria and no recorded blockers, so Orchestrator accepts implementation.
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T06:12:36Z -->
## Implementation report
Implementation start report:
- Created child implementation worktree:
- `/home/hare/Projects/yoi/.worktree/00001KVSFXY88-dashboard-hint-cleanup`
- Created branch:
- `impl/00001KVSFXY88-dashboard-hint-cleanup`
- Base commit:
- `3bfd1ca0 ticket: accept dashboard hint cleanup`
- Confirmed tracked Ticket project records are visible in the child worktree。
- Confirmed `.yoi/memory` has no tracked/untracked entries in the child worktree check。
- Spawned sibling Coder Pod:
- `yoi-coder-00001KVSFXY88`
- Coder scope:
- read: `/home/hare/Projects/yoi`
- write: `/home/hare/Projects/yoi/.worktree/00001KVSFXY88-dashboard-hint-cleanup`
Parallelization note:
- Active Workspace web / protocol work is separate from TUI Dashboard rendering. This Ticket should stay limited to `crates/tui/src/dashboard/*` unless tests reveal a narrow shared helper impact。
Next action:
- Wait for Coder implementation report, then spawn Reviewer read-only for the implementation diff。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T06:28:12Z -->
## Implementation report
Coder implementation report received from `yoi-coder-00001KVSFXY88`.
Implementation commit:
- `03ad525f tui: trim dashboard redundant hints`
Changed areas:
- `crates/tui/src/dashboard/render.rs`
- Removed top title key-hint guidance。
- Made selected-row/composer target status line blank。
- Minimized actionbar to notices/diagnostic-only text。
- Preserved row marker/highlighting rendering。
- `crates/tui/src/dashboard/mod.rs`
- Removed now-unused selected-row display-status helper。
- `crates/tui/src/dashboard/tests.rs`
- Updated render/unit expectations for no redundant hints/status。
- Added title guidance cleanup assertion。
- Kept assertions that selected rows remain visibly marked。
Coder validation reported:
- `cargo fmt --check`: passed
- `cargo test -p tui dashboard --lib`: passed
- `cargo test -p tui workspace_panel --lib`: passed
- `git diff --check`: passed
- `cargo run -p yoi -- ticket doctor`: passed
Manual/PTY `yoi panel` check:
- Not run; focused render/unit tests were used as primary validation。
Orchestrator evidence checked:
- Worktree clean on `impl/00001KVSFXY88-dashboard-hint-cleanup`
- HEAD is `03ad525f`
- Diff `3bfd1ca0..HEAD` touches expected dashboard render/module/tests files only。
- `git diff --check 3bfd1ca0..HEAD`: passed。
Next action:
- Route to read-only Reviewer Pod for acceptance review。
---
<!-- event: comment author: yoi-orchestrator at: 2026-06-23T06:28:54Z -->
## Comment
Review routing:
Spawned read-only Reviewer Pod:
- `yoi-reviewer-00001KVSFXY88-r1`
Review target:
- Worktree: `/home/hare/Projects/yoi/.worktree/00001KVSFXY88-dashboard-hint-cleanup`
- Branch: `impl/00001KVSFXY88-dashboard-hint-cleanup`
- Baseline: `3bfd1ca0`
- Implementation commit: `03ad525f tui: trim dashboard redundant hints`
- Diff: `3bfd1ca0..03ad525f`
Reviewer scope:
- read-only `/home/hare/Projects/yoi`
Reviewer focus:
- Removal of redundant title/actionbar key hints and selected-row textual status。
- Preservation of visible row selection marker/highlighting。
- No keyboard behavior/action semantics changes。
- Dashboard render tests updated to new display contract。
Orchestrator will wait for reviewer verdict before integration。
---
<!-- event: review author: yoi-reviewer-00001KVSFXY88-r1 at: 2026-06-23T06:31:21Z status: approve -->
## Review: approve
Verdict: `approve`
確認範囲:
- Ticket authority:
- `.yoi/tickets/00001KVSFXY88/item.md`
- `.yoi/tickets/00001KVSFXY88/thread.md`
- Diff `3bfd1ca0..03ad525f`:
- `crates/tui/src/dashboard/render.rs`
- `crates/tui/src/dashboard/mod.rs`
- `crates/tui/src/dashboard/tests.rs`
Evidence:
- Top title guidance was removed; `title_line` now keeps only `workspace dashboard` plus companion/orchestrator status。
- `target_status_line` now renders blank, removing selected Ticket / Pod / Intake / no-row textual status。
- Actionbar is reduced to sending/refreshing/notices/diagnostic text; always-on key hint groups are gone。
- Row marker/highlighting paths remain; tests assert selected row marker strings such as `▶ ready`, `▶ queued`, and ` ▶ live`
- Keyboard/action behavior was not altered in the diff; removed helpers were display-only and no remaining references exist。
- No Console/single-Pod TUI files changed。
- No direct selected-Pod send was reintroduced; existing dashboard behavior tests still pass。
Blocking issues:
- None。
Non-blocking concerns:
- Manual `yoi panel` PTY visual check was not run; focused render/unit coverage was used as primary validation, as suggested。
Reviewer validation:
- `cargo fmt --check`: passed
- `git diff --check 3bfd1ca0..HEAD`: passed
- `cargo test -p tui dashboard --lib`: passed (`107 passed`)
- `cargo test -p tui workspace_panel --lib`: passed (`23 passed`)
- `cargo run -p yoi -- ticket doctor`: passed (`doctor: ok`)
Worktree status:
- Clean on `impl/00001KVSFXY88-dashboard-hint-cleanup` at `03ad525f`
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T06:32:25Z -->
## Implementation report
Reviewer r1 approvalを受けて、実装ブランチを Orchestrator worktree の `orchestration` branch に統合した。
統合:
- Merge commit: `5abf16f9 merge: dashboard hint cleanup`
- Merged branch: `impl/00001KVSFXY88-dashboard-hint-cleanup`
- Implementation commit:
- `03ad525f tui: trim dashboard redundant hints`
Orchestrator worktree validation:
- `cargo fmt --check`: passed
- `git diff --check HEAD^1..HEAD`: passed
- `cargo test -p tui dashboard --lib`: passed (`107 passed`)
- `cargo test -p tui workspace_panel --lib`: passed (`23 passed`)
- `cargo run -p yoi -- ticket doctor`: passed (`doctor: ok`)
Final Orchestrator worktree status after validation is clean on `orchestration` at `5abf16f9`
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-23T06:32:37Z from: inprogress to: done reason: implementation_merged_and_validated field: state -->
## State changed
Reviewer approval、Orchestrator worktree への統合、focused Dashboard / workspace_panel tests、Ticket doctor が完了したため `done` に遷移する。
---
<!-- event: state_changed author: hare at: 2026-06-23T06:32:49Z from: done to: closed reason: closed field: state -->
## State changed
Ticket を closed にしました。
---
<!-- event: close author: hare at: 2026-06-23T06:32:49Z status: closed -->
## 完了
TUI Dashboard の冗長な key hints と selected-row textual status display を削除し、Orchestrator worktree の `orchestration` branch に統合した。
主な成果:
- Top title line から `Row selection`, `blank Enter`, `Tab target` などの key hint guidance を削除。
- Selected Ticket / Pod / Intake / no-row selected textual status line を blank にした。
- Composer bottom actionbar を always-on key hints ではなく notices / diagnostics only に最小化。
- Row marker / highlighting rendering は維持。
- Keyboard/action behavior は変更していない。
- Unused selected-row display-status helper を削除。
- Dashboard render/unit tests を新 display contract に更新し、selected row marker の visibility assertion は維持。
統合・検証:
- Merge commit: `5abf16f9 merge: dashboard hint cleanup`
- Implementation commit: `03ad525f tui: trim dashboard redundant hints`
- Reviewer final verdict: approve
- Validation passed: `cargo fmt --check`, `git diff --check HEAD^1..HEAD`, `cargo test -p tui dashboard --lib`, `cargo test -p tui workspace_panel --lib`, and `cargo run -p yoi -- ticket doctor`
範囲外:
- Console / single-Pod TUI hints は変更していない。
- Manual/PTY `yoi panel` visual check は実施していない。Focused render/unit tests を主 validation とした。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T06:33:21Z -->
## Implementation report
Post-close cleanup completed。
- Stopped child Pods and reclaimed scope:
- `yoi-coder-00001KVSFXY88`
- `yoi-reviewer-00001KVSFXY88-r1`
- Removed implementation worktree:
- `/home/hare/Projects/yoi/.worktree/00001KVSFXY88-dashboard-hint-cleanup`
- Deleted implementation branch:
- `impl/00001KVSFXY88-dashboard-hint-cleanup`
- Orchestrator worktree remains clean on `orchestration` at `10866666`
Root/original workspace was not used for merge/validation/cleanup operations。
---
+2 -2
View File
@@ -1,8 +1,8 @@
---
title: 'Improve Workspace web ticket Kanban grouping and lazy rows'
state: 'inprogress'
state: 'closed'
created_at: '2026-06-23T05:50:36Z'
updated_at: '2026-06-23T06:06:45Z'
updated_at: '2026-06-23T06:16:10Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-23T05:53:22Z'
+22
View File
@@ -0,0 +1,22 @@
Workspace web Repository Ticket Kanban の grouping / lazy rows 改善を統合した。
主な成果:
- Repository Ticket Kanban を `RepositoryTicketKanban.svelte` component に分離。
- `planning` + `ready` を display-only group とし、`ready``planning` より上に表示。
- `queued` + `inprogress` を display-only group とし、`inprogress``queued` より上に表示。
- `done`, `closed`, `other` は独立 group として維持。
- 各 row に original Ticket state を表示。
- 各 group の初期表示行数を 30 に cap。
- 各 group に独立 scroll area と independent lazy visible count を実装。
- High-volume `closed` group が page height を無制限に伸ばさないようにした。
- `WorkspacePage.svelte` から inline Kanban logic/markup を削減。
- Styling は existing design tokens を使い、backend/API/Ticket lifecycle semantics は変更していない。
統合・検証:
- Merge commit: `eea26f91 merge: kanban lazy rows`
- Implementation commit: `6f68bb8d web: group repository ticket kanban rows`
- Reviewer final verdict: approve
- Validation passed: `git diff --check HEAD^1..HEAD`, `deno task check`, `deno task build`, `cargo run -p yoi -- ticket doctor`, and `nix build .#yoi --no-link`
範囲外:
- Backend pagination、Ticket state mutation UI、drag/drop Kanban、browser/manual scroll E2E tests は追加していない。
+164
View File
@@ -206,3 +206,167 @@ Reviewer focus:
Orchestrator will wait for reviewer verdict before integration。
---
<!-- event: review author: yoi-reviewer-00001KVSGFM65-r1 at: 2026-06-23T06:13:34Z status: approve -->
## Review: approve
Verdict: `approve`
確認範囲:
- Ticket authority:
- `.yoi/tickets/00001KVSGFM65/item.md`
- `.yoi/tickets/00001KVSGFM65/thread.md`
- Diff `a6f9019e..6f68bb8d`:
- `web/workspace/src/lib/workspace-pages/RepositoryTicketKanban.svelte`
- `web/workspace/src/lib/workspace-pages/WorkspacePage.svelte`
- `web/workspace/src/app.css`
Review evidence:
- Grouping/sort:
- `planning` + `ready` are grouped as `ready-planning`, labelled `Ready / Planning`
- `queued` + `inprogress` are grouped as `inprogress-queued`, labelled `In progress / Queued`
- `statePriority()` places `ready` before `planning`, and `inprogress` before `queued`
- Other states:
- `done`, `closed`, and `other` remain separate group keys via `state:${state}` / `state:other`
- Per-group lazy state:
- `visibleRowsByGroup` is keyed by group key。
- Initial visible rows are `INITIAL_VISIBLE_ROWS = 30`
- Scroll handling updates only `[group.key]`
- Near-bottom threshold is `NEAR_BOTTOM_PX = 96`
- High-volume containment:
- Each non-empty group row list has its own `.ticket-list-scroll`
- `.ticket-list-scroll` uses `max-height: 34rem; overflow-y: auto;`, so `closed` cannot expand page vertically without bound。
- Original row state:
- Each ticket row renders `<span class="ticket-state">{ticket.state}</span>`
- Component boundary:
- Kanban logic and row rendering moved into `RepositoryTicketKanban.svelte`
- `WorkspacePage.svelte` now delegates with `<RepositoryTicketKanban tickets={repositoryTickets} />`
- Design / boundary:
- Styling uses existing CSS tokens such as `var(--space-*)`, `var(--line)`, `var(--text-muted)`, `var(--text-faint)`, `var(--text-strong)`
- No backend/API/protocol TypeScript generation scope touched。
- No mutation UI added。
Blocking issues:
- None found。
Non-blocking concerns:
- No browser/manual scroll interaction test was run; validation is by code inspection plus Svelte check/build. Given the tickets frontend-only scope and current test tooling, this is acceptable。
Reviewer validation:
- `git diff --check a6f9019e..6f68bb8d`: passed
- `git diff --check a6f9019e..HEAD`: passed
- `cd web/workspace && rm -rf node_modules .svelte-kit build && deno task check && deno task build`: passed (`svelte-check found 0 errors and 0 warnings`; static build completed)
- `cargo run -p yoi -- ticket doctor`: passed (`doctor: ok`)
- `nix build .#yoi --no-link`: passed
- `cargo test -p yoi-workspace-server`: not run; no backend/API changes were made。
Generated artifacts:
- Ignored validation artifacts present after review:
- `target/`
- `web/workspace/.svelte-kit/`
- `web/workspace/build/`
- `web/workspace/node_modules/`
- No generated artifacts are tracked。
Worktree status:
- Clean tracked state on `impl/00001KVSGFM65-kanban-lazy-rows` at `6f68bb8d`
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T06:15:01Z -->
## Implementation report
Reviewer r1 approvalを受けて、実装ブランチを Orchestrator worktree の `orchestration` branch に統合した。
統合:
- Merge commit: `eea26f91 merge: kanban lazy rows`
- Merged branch: `impl/00001KVSGFM65-kanban-lazy-rows`
- Implementation commit:
- `6f68bb8d web: group repository ticket kanban rows`
Orchestrator worktree validation:
- `git diff --check HEAD^1..HEAD`: passed
- `cd web/workspace && rm -rf node_modules .svelte-kit build && deno task check`: passed (`svelte-check found 0 errors and 0 warnings`)
- `cd web/workspace && deno task build`: passed
- `cargo run -p yoi -- ticket doctor`: passed (`doctor: ok`)
- `nix build .#yoi --no-link`: passed
Validation generated ignored frontend artifacts in Orchestrator worktree:
- `web/workspace/node_modules/`
- `web/workspace/.svelte-kit/`
- `web/workspace/build/`
These were removed after validation. Final Orchestrator worktree status after validation cleanup is clean on `orchestration` at `eea26f91`
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-23T06:15:09Z from: inprogress to: done reason: implementation_merged_and_validated field: state -->
## State changed
Reviewer approval、Orchestrator worktree への統合、Deno check/build、Ticket doctor、Nix build が完了したため `done` に遷移する。
---
<!-- event: state_changed author: hare at: 2026-06-23T06:15:22Z from: done to: closed reason: closed field: state -->
## State changed
Ticket を closed にしました。
---
<!-- event: close author: hare at: 2026-06-23T06:15:22Z status: closed -->
## 完了
Workspace web Repository Ticket Kanban の grouping / lazy rows 改善を統合した。
主な成果:
- Repository Ticket Kanban を `RepositoryTicketKanban.svelte` component に分離。
- `planning` + `ready` を display-only group とし、`ready``planning` より上に表示。
- `queued` + `inprogress` を display-only group とし、`inprogress``queued` より上に表示。
- `done`, `closed`, `other` は独立 group として維持。
- 各 row に original Ticket state を表示。
- 各 group の初期表示行数を 30 に cap。
- 各 group に独立 scroll area と independent lazy visible count を実装。
- High-volume `closed` group が page height を無制限に伸ばさないようにした。
- `WorkspacePage.svelte` から inline Kanban logic/markup を削減。
- Styling は existing design tokens を使い、backend/API/Ticket lifecycle semantics は変更していない。
統合・検証:
- Merge commit: `eea26f91 merge: kanban lazy rows`
- Implementation commit: `6f68bb8d web: group repository ticket kanban rows`
- Reviewer final verdict: approve
- Validation passed: `git diff --check HEAD^1..HEAD`, `deno task check`, `deno task build`, `cargo run -p yoi -- ticket doctor`, and `nix build .#yoi --no-link`
範囲外:
- Backend pagination、Ticket state mutation UI、drag/drop Kanban、browser/manual scroll E2E tests は追加していない。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T06:16:10Z -->
## Implementation report
Post-close cleanup completed。
- Stopped child Pods and reclaimed scope:
- `yoi-coder-00001KVSGFM65`
- `yoi-reviewer-00001KVSGFM65-r1`
- Removed ignored frontend validation artifacts from child worktree before worktree removal if present:
- `web/workspace/node_modules/`
- `web/workspace/.svelte-kit/`
- `web/workspace/build/`
- Removed implementation worktree:
- `/home/hare/Projects/yoi/.worktree/00001KVSGFM65-kanban-lazy-rows`
- Deleted implementation branch:
- `impl/00001KVSGFM65-kanban-lazy-rows`
- Orchestrator worktree remains clean on `orchestration` at `9de04f72`
Root/original workspace was not used for merge/validation/cleanup operations。
---
@@ -0,0 +1 @@
{"id":"orch-plan-20260623-065020-1","ticket_id":"00001KVSKGDYS","kind":"accepted_plan","accepted_plan":{"summary":"Add tracked `.yoi/workspace.toml` schema/loader/create-if-missing path for safe local Workspace identity and use persisted workspace id in Workspace APIs/repository/host id derivation, with tests for missing/existing/invalid/no-path-leak behavior.","branch":"impl/00001KVSKGDYS-workspace-identity","worktree":"/home/hare/Projects/yoi/.worktree/00001KVSKGDYS-workspace-identity","role_plan":"Orchestrator creates a dedicated child worktree and spawns a narrow-scope Workspace backend Coder. Reviewer will be spawned read-only after Coder reports implementation commit(s). After approval, Orchestrator integrates into `orchestration`, validates workspace-server/frontend/Nix as needed, records closure, and cleans only the child worktree/branch."},"author":"yoi-orchestrator","at":"2026-06-23T06:50:20Z"}
+119
View File
@@ -0,0 +1,119 @@
---
title: 'Persist local Workspace identity in .yoi/workspace.toml'
state: 'closed'
created_at: '2026-06-23T06:43:28Z'
updated_at: '2026-06-23T07:39:10Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-23T06:47:18Z'
---
## 背景
Workspace server の local dev bootstrap は現在、`workspace_id``local:{display}` のように workspace root の basename から導出している。これは暫定 namespace としては分かりやすいが、DB の primary key / Workspace identity としては不適切である。
問題:
- `workspace_id` が表示名 / path basename から予測可能に作られている。
- 同じ basename の workspace が複数あると衝突しうる。
- workspace root の rename / move で identity が変わる。
- `local:` namespace と display label が DB PK に混ざっている。
- `--workspace .` のような相対 path では display fallback に落ちるなど、初期化責務が `ServerConfig::local_dev` に寄りすぎている。
Workspace identity は `.yoi/workspace.toml` を local Workspace record として永続化し、未作成なら Workspace backend の初期化処理で払い出す。これにより、DB を再作成しても Workspace identity は維持され、将来の DB authority / hosted Workspace / migration でも扱いやすくなる。
## 方針
- `.yoi/workspace.toml` を local Workspace identity の authority とする。
- `workspace_id` は path / display name から導出しない。
- 新規 local Workspace 初期化時に UUIDv7 を発行して `workspace_id` として保存する。
- `display_name` は identity ではなく UI label として保存する。
- 初期値は canonicalized workspace root の basename でよい。
- 将来的に rename 可能な field として扱う。
- `ServerConfig::local_dev` は Workspace ID を生成しない。
- CLI/bootstrap 層で workspace root canonicalization と `.yoi/workspace.toml` load-or-init を行い、結果を `ServerConfig` に渡す。
- `.yoi/workspace.toml` が壊れている場合は勝手に上書きせず、明示的な error で止める。
- 既存 `.yoi/workspace.db` 内の `local:*` workspace row については、初期実装では destructive migration を必須にしない。
- 新しい UUIDv7 workspace row を upsert し、古い row は残してよい。
- 必要なら後続で cleanup/migration policy を設計する。
## `.yoi/workspace.toml` v0
最小 schema:
```toml
workspace_id = "0197..." # UUIDv7 canonical string
created_at = "2026-06-23T...Z"
display_name = "yoi"
```
Validation:
- `workspace_id` は UUID として parse できること。
- 可能なら UUID version 7 であることを検証する。
- `created_at` は RFC3339 UTC timestamp として parse できること。
- `display_name` は空文字列でないこと。
- unknown fields の扱いは将来拡張を考え、明示的に決める。
- v0 では deny でも preserve でもよいが、選択理由を実装に残す。
## 初期化フロー
`yoi-workspace-server serve --workspace <path>` の local bootstrap は将来的に以下の責務分離にする。
1. `--workspace` を canonicalize する。
2. workspace root 配下の `.yoi/` を確認し、なければ作成する。
3. `.yoi/workspace.toml` を読む。
4. 存在しない場合:
- UUIDv7 `workspace_id` を発行する。
- `created_at` を現在 UTC にする。
- `display_name` を canonicalized workspace root basename から作る。
- `.yoi/workspace.toml` を atomic-ish に書き込む。
5. 存在する場合:
- parse / validate する。
- 壊れていたら停止し、上書きしない。
6. `ServerConfig` には canonicalized workspace root と Workspace identity を渡す。
7. SQLite `workspaces` row は `.yoi/workspace.toml``workspace_id` / `display_name` で upsert する。
8. API `/api/workspace` は DB row / identity record を元に stable id と display name を返す。
## 実装候補
- `crates/workspace-server` に Workspace identity loader を追加する。
- 例: `workspace_identity.rs` または `identity.rs`
- 型例:
```rust
struct WorkspaceIdentity {
workspace_id: Uuid,
created_at: DateTime<Utc>,
display_name: String,
}
impl WorkspaceIdentity {
fn load_or_init(root: &Path) -> Result<Self, WorkspaceIdentityError>;
}
```
- `uuid` crate の UUIDv7 support を確認し、必要なら依存 feature を追加する。
- TOML parse/write には既存 dependency を優先し、なければ最小 dependency を検討する。
- `ServerConfig::local_dev``workspace_id` / `display_name` を引数または identity object で受け取る形に寄せる。
## Non-goals
- Hosted / multi-user Workspace identity の完全設計。
- Workspace rename UI。
- 既存 `.yoi/workspace.db` の destructive migration。
- Ticket / Objective storage の DB authority migration。
- `.yoi/workspace.toml` を全 Workspace settings store に拡張すること。
## 受け入れ条件
- local Workspace identity が `.yoi/workspace.toml` に永続化される。
- `.yoi/workspace.toml` 未作成時、Workspace backend 初期化時に UUIDv7 `workspace_id` が払い出される。
- `workspace_id` が path basename / display name から導出されなくなる。
- `display_name``.yoi/workspace.toml` の field として扱われ、identity と分離されている。
- `--workspace .` でも canonicalized workspace root をもとに初期化される。
- `.yoi/workspace.toml` が壊れている場合、勝手に再生成/上書きせず error で停止する。
- `ServerConfig::local_dev` または同等の bootstrap helper が workspace id を自前生成しない。
- SQLite `workspaces` row は `.yoi/workspace.toml` の identity を使って upsert される。
- 既存 `local:{display}` 形式は新規 workspace id として生成されない。
- `cargo test -p yoi-workspace-server``git diff --check``nix build .#yoi --no-link` が通る。
+38
View File
@@ -0,0 +1,38 @@
Local Workspace identity を tracked `.yoi/workspace.toml` に永続化する実装を完了した。
完了内容:
- `.yoi/workspace.toml` を local Workspace identity の authority として追加。
- v0 schema は `workspace_id`, `created_at`, `display_name` のみ。
- `workspace_id` は UUIDv7 canonical string として検証し、path / display basename から導出しない。
- `created_at` は UTC RFC3339 timestamp として検証。
- `display_name` は空文字列を拒否。
- unknown fields は v0 では明示的に deny。
- invalid existing file は fail closed し、自動上書きしない。
- missing file 初期化では `OpenOptions::create_new(true)` を使い、既存 file が race で作られた場合は persisted identity を read/parse して返す。
- fixed temp file / `path.exists()` + `rename` finalization は使わない。
- `ServerConfig::local_dev``local:{display}` を生成せず、persisted `WorkspaceIdentity` を受け取る。
- SQLite `workspaces` upsert、Workspace API、repository ids、host ids は persisted Workspace id を使う。
- legacy `/api/repositories/local` alias は維持。
- 既存 `local:*` DB rows の destructive migration はしない。
- `.yoi/workspace.toml` は絶対 path / secret / socket / runtime data を含まない安全な project identity record として維持。
統合:
- Implementation: `31565c9b feat: persist workspace identity`
- Fix: `49c9e190 fix: return persisted workspace identity`
- Merge: `2745f3d5 merge: workspace identity persistence`
検証:
- Reviewer approval: `yoi-reviewer-00001KVSKGDYS-r1`
- `cargo fmt --check`: passed
- `git diff --check HEAD^1..HEAD`: passed
- `cargo test -p yoi-workspace-server`: passed
- `cargo check -p yoi-workspace-server`: passed
- `cd web/workspace && deno task check && deno task build`: passed
- `cargo run -q -p yoi -- ticket doctor`: passed (`doctor: ok`)
- `nix build .#yoi --no-link`: passed
生成物 cleanup:
- orchestration worktree の `target/`, `web/workspace/node_modules/`, `web/workspace/.svelte-kit/`, `web/workspace/build/` を削除済み。
残作業:
- なし。将来必要なら legacy `local:*` row cleanup / migration policy は別 Ticket で扱う。
+536
View File
@@ -0,0 +1,536 @@
<!-- event: create author: "yoi ticket" at: 2026-06-23T06:43:28Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: hare at: 2026-06-23T06:44:19Z -->
## Intake summary
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-23T06:44:19Z from: planning to: ready reason: cli_state field: state -->
## State changed
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: workspace-panel at: 2026-06-23T06:47:18Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-23T06:49:54Z -->
## Decision
Routing decision: `implementation_ready_parallel`
Reason:
- Ticket body has concrete `workspace.toml` semantics, backend/API consistency requirements, migration-safe behavior, tests, and validation criteria。
- No relations / blockers / orchestration plan records exist。
- Active Dashboard no-auto-selection work is TUI-only and separate from Workspace backend identity persistence, so parallel implementation is acceptable。
- Orchestrator worktree is clean on `orchestration` at `13e76d35`; target worktree / branch is not present。
- Current Workspace backend has ad-hoc/stable-ish IDs; Ticket asks for tracked local identity at `.yoi/workspace.toml`
IntentPacket:
Intent:
- Persist local Workspace identity in tracked `.yoi/workspace.toml` and use it as the stable local Workspace id across backend APIs, repository IDs, host ID derivation, and frontend display where applicable。
Binding decisions / invariants:
- `.yoi/workspace.toml` is tracked project record, not local runtime/secret file。
- It should contain only safe project identity fields, e.g. `workspace_id`, `display_name`, `created_at` or equivalent; no absolute paths, user names, socket paths, data-dir paths, tokens, or runtime secrets。
- Existing checkouts without the file must remain usable with safe auto-create or fallback behavior。
- Workspace id should not change on process restart, repo path move, or sibling worktree checkout when the file is present。
- Avoid changing Ticket/Objectives canonical authority。
- Do not confuse this with Profile/manifest/override runtime config。
- Handle invalid/corrupt `workspace.toml` fail-closed with clear diagnostic; do not silently generate a different id over a bad tracked file unless explicitly safe。
Requirements / acceptance criteria:
- Define `.yoi/workspace.toml` schema and parser/loader.
- Add create-if-missing behavior for local workspace server/bootstrap path, or a documented CLI/tool path if auto-create is unsuitable.
- Use persisted workspace id in Workspace API responses and any Repository/Host ids derived from workspace id.
- Ensure generated file is safe to commit and has no local absolute paths/secrets.
- Existing tests updated to deterministic temp workspace identity behavior。
- Add tests for missing file creation/fallback, existing stable id, invalid file error, and no local path leakage。
- Validation includes workspace-server tests, Deno check/build if frontend output changes, git diff check, Ticket doctor, and Nix build if package/source behavior changes。
Implementation latitude:
- Put parser in `workspace-server` crate if currently only the web backend needs it, or a small shared crate if needed; avoid broad architecture churn。
- Workspace id can reuse project-record id allocator if suitable, or a stable slug/uuid/base32 type if already used。
- If auto-writing tracked file during server startup is risky, implement explicit ensure function used by tests/bootstrap and document behavior; but Ticket prefers tracked persistence。
- Frontend can just display the id returned by existing `/api/workspace` if backend response changes。
Escalate if:
- Creating tracked `.yoi/workspace.toml` from a server process violates current project-record write boundaries。
- Workspace id generation requires global registry/coordination beyond local checkout。
- Existing code strongly assumes workspace id is derived from path and changing it would break multiple APIs unexpectedly。
- Nix/source filtering excludes `.yoi/workspace.toml` unexpectedly and package behavior needs product decision。
Validation plan:
- `cargo fmt --check`
- `cargo test -p yoi-workspace-server`
- `cargo check -p yoi-workspace-server`
- `cd web/workspace && deno task check && deno task build` if frontend-visible changes occur。
- `git diff --check`
- `cargo run -p yoi -- ticket doctor`
- `nix build .#yoi --no-link` if package/source filtering or tracked file behavior changed。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-23T06:50:08Z from: queued to: inprogress reason: human_authorized_unblocked_workspace_identity_persistence field: state -->
## State changed
Human authorized queue routing from Workspace Dashboard. Ticket has concrete local workspace identity requirements and no recorded blockers, so Orchestrator accepts implementation.
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T06:51:44Z -->
## Implementation report
Implementation start report:
- Created child implementation worktree:
- `/home/hare/Projects/yoi/.worktree/00001KVSKGDYS-workspace-identity`
- Created branch:
- `impl/00001KVSKGDYS-workspace-identity`
- Base commit:
- `4cda83b7 ticket: accept workspace identity and selection work`
- Confirmed tracked Ticket project records are visible in the child worktree。
- Confirmed `.yoi/memory` has no tracked/untracked entries in the child worktree check。
- Spawned sibling Coder Pod:
- `yoi-coder-00001KVSKGDYS`
- Coder scope:
- read: `/home/hare/Projects/yoi`
- write: `/home/hare/Projects/yoi/.worktree/00001KVSKGDYS-workspace-identity`
Parallelization note:
- `00001KVSKJ0EA` is active separately and targets TUI Dashboard selection semantics. This Ticket should stay limited to Workspace backend identity persistence and safe project record behavior。
Next action:
- Wait for Coder implementation report, then spawn Reviewer read-only for the implementation diff。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T07:13:47Z -->
## Implementation report
Coder implementation report received from `yoi-coder-00001KVSKGDYS`.
Implementation commit:
- `31565c9b feat: persist workspace identity`
Changed areas:
- Added tracked project identity record:
- `.yoi/workspace.toml`
- current contents are safe project identity fields only: `workspace_id`, `created_at`, `display_name`
- Added Workspace identity schema/loader:
- `crates/workspace-server/src/identity.rs`
- strict TOML parser with `workspace_id`, `created_at`, `display_name`
- UUIDv7 validation。
- UTC RFC3339 `created_at` validation。
- unknown fields rejected。
- create-if-missing behavior。
- invalid existing file fails closed without rewrite。
- tests for missing creation, stable existing identity, invalid file, no path leakage。
- Integrated persisted identity into workspace server/bootstrap:
- `crates/workspace-server/src/main.rs`
- `crates/workspace-server/src/server.rs`
- workspace API uses persisted `workspace_id` / display metadata。
- Updated repository ID behavior:
- `crates/workspace-server/src/repositories.rs`
- local repository ids derive from persisted Workspace id。
- legacy `/api/repositories/local` remains accepted as an alias。
- Host ID derivation now receives persisted Workspace id through existing API flow。
- Added dependencies / packaging update:
- `crates/workspace-server/Cargo.toml`
- `Cargo.lock`
- `package.nix` cargo hash。
Coder validation reported:
- `cargo fmt --check`: passed
- `cargo test -p yoi-workspace-server`: passed
- `cargo check -p yoi-workspace-server`: passed
- `cd web/workspace && deno task check && deno task build`: passed
- `git diff --check`: passed
- `git diff --cached --check`: passed
- `cargo run -p yoi -- ticket doctor`: passed
- `nix build .#yoi --no-link`: passed
Generated artifact cleanup:
- Coder removed generated frontend artifacts:
- `web/workspace/.svelte-kit`
- `web/workspace/build`
- `web/workspace/node_modules`
- Coder removed generated Rust build cache:
- `target`
Orchestrator evidence checked:
- Worktree clean on `impl/00001KVSKGDYS-workspace-identity` at `31565c9b`
- Diff `4cda83b7..HEAD` touches expected Workspace identity/backend/package files and adds `.yoi/workspace.toml`
- `git diff --check 4cda83b7..HEAD`: passed。
Next action:
- Route to read-only Reviewer Pod for acceptance review。
---
<!-- event: comment author: yoi-orchestrator at: 2026-06-23T07:14:37Z -->
## Comment
Review routing:
Spawned read-only Reviewer Pod:
- `yoi-reviewer-00001KVSKGDYS-r1`
Review target:
- Worktree: `/home/hare/Projects/yoi/.worktree/00001KVSKGDYS-workspace-identity`
- Branch: `impl/00001KVSKGDYS-workspace-identity`
- Baseline: `4cda83b7`
- Implementation commit: `31565c9b feat: persist workspace identity`
- Diff: `4cda83b7..31565c9b`
Reviewer scope:
- read-only `/home/hare/Projects/yoi`
Reviewer focus:
- Safety and tracked-project-record suitability of `.yoi/workspace.toml`
- Stable persisted id usage in Workspace API / repository / host ids。
- Missing/existing/invalid file behavior。
- No local path/secret leakage。
- Package/Nix updates。
Orchestrator will wait for reviewer verdict before integration。
---
<!-- event: review author: yoi-orchestrator at: 2026-06-23T07:24:58Z status: request_changes -->
## Review: request changes
Reviewer Pod `yoi-reviewer-00001KVSKGDYS-r1` requested changes.
Reviewed target:
- Worktree: `/home/hare/Projects/yoi/.worktree/00001KVSKGDYS-workspace-identity`
- Branch: `impl/00001KVSKGDYS-workspace-identity`
- Implementation: `31565c9b feat: persist workspace identity`
- Diff: `4cda83b7..31565c9b`
Blocking issue:
1. Missing-file create path can return/use an identity that is not the persisted `.yoi/workspace.toml` identity under a local bootstrap race。
Evidence:
- `crates/workspace-server/src/identity.rs` generates a new `WorkspaceIdentity`, calls `identity.write_new(path)?`, and then returns the generated identity。
- In `write_new`, if another process creates `workspace.toml` after the initial missing-file path, the code validates the existing file but discards the parsed identity:
```rust
if path.exists() {
let _ = fs::remove_file(&tmp);
let raw = fs::read_to_string(path)?;
return Self::parse_str(&raw, path).map(|_| ());
}
```
- The caller still returns the newly generated identity, so a server instance can upsert SQLite / serve Workspace API / repository / host ids using an id that is not the tracked file and will not survive restart。
- Related TOCTOU risk: `path.exists()` followed by `fs::rename(tmp, path)` can overwrite a concurrently created file on Unix, and fixed temp path `workspace.toml.tmp` can be clobbered by concurrent creators。
Required fix direction:
- Ensure the returned `WorkspaceIdentity` is always the identity actually persisted or read from `.yoi/workspace.toml`
- Use a no-overwrite / create-new style finalization or equivalent safe retry/read behavior for local bootstrap races。
- Add focused test coverage for the race/return-authority behavior if feasible。
Non-blocking reviewer findings:
- `.yoi/workspace.toml` contents look safe to track。
- Unknown fields are intentionally denied and documented/tested。
- `ServerConfig::local_dev` no longer derives `local:{display}` internally。
- Workspace API/store/repository/host plumbing appears correctly based on persisted identity once loaded。
- Existing `local:*` rows are not destructively migrated。
Reviewer validation all passed before request_changes:
- `cargo fmt --check`
- `git diff --check 4cda83b7..HEAD`
- `cargo test -p yoi-workspace-server` (`15 passed`)
- `cargo check -p yoi-workspace-server`
- `cd web/workspace && deno task check && deno task build`
- `cargo run -p yoi -- ticket doctor`
- `nix build .#yoi --no-link`
Reviewer notes generated ignored artifacts from validation remained in worktree:
- `target/`
- `web/workspace/node_modules/`
- `web/workspace/.svelte-kit/`
- `web/workspace/build/`
---
<!-- event: comment author: yoi-orchestrator at: 2026-06-23T07:25:27Z -->
## Comment
Rerouted request-changes feedback to Coder Pod `yoi-coder-00001KVSKGDYS`.
Requested fix:
- `WorkspaceIdentity::load_or_init` must return the identity actually persisted/read from `.yoi/workspace.toml`
- Missing-file creation must handle local bootstrap races without returning an unpersisted generated identity。
- Avoid or safely handle TOCTOU / fixed temp-file clobbering around `workspace.toml.tmp`
- Add focused deterministic test coverage if feasible。
Validation requested after fix:
- `cargo fmt --check`
- `cargo test -p yoi-workspace-server`
- `cargo check -p yoi-workspace-server`
- `cd web/workspace && deno task check && deno task build`
- `git diff --check`
- `git diff --cached --check`
- `cargo run -p yoi -- ticket doctor`
- `nix build .#yoi --no-link`
Coder was also asked to remove ignored validation artifacts before reporting。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T07:30:46Z -->
## Implementation report
Coder fix report received from `yoi-coder-00001KVSKGDYS` after request_changes。
New commit:
- `49c9e190 fix: return persisted workspace identity`
Fix summary:
- Replaced temp-file + `rename` finalization in `crates/workspace-server/src/identity.rs`
- New creation path uses `OpenOptions::create_new(true)` directly on `.yoi/workspace.toml`
- If creation succeeds, generated identity is written and returned。
- If creation fails because the file already exists from a concurrent bootstrap race, the existing file is read/parsed and that persisted identity is returned。
- Removed fixed `workspace.toml.tmp` path and the `path.exists()` + `rename` TOCTOU overwrite risk。
Test coverage:
- Added deterministic unit test `create_new_race_returns_existing_persisted_identity`
- Test verifies an already-persisted identity wins over a generated candidate, returned identity matches the file, and the file is not rewritten。
- Existing tests for missing creation, stable existing identity, invalid fail-closed behavior, no path leakage, and unknown field rejection remain。
Coder validation reported:
- `cargo fmt --check`: passed
- `cargo test -p yoi-workspace-server`: passed
- `cargo check -p yoi-workspace-server`: passed
- `cd web/workspace && deno task check && deno task build`: passed
- `git diff --check`: passed
- `git diff --cached --check`: passed
- `cargo run -p yoi -- ticket doctor`: passed
- `nix build .#yoi --no-link`: passed
Cleanup reported:
- Removed ignored validation artifacts:
- `target/`
- `web/workspace/node_modules/`
- `web/workspace/.svelte-kit/`
- `web/workspace/build/`
Orchestrator evidence checked:
- Worktree clean on `impl/00001KVSKGDYS-workspace-identity` at `49c9e190`
- Diff `4cda83b7..HEAD` still touches expected Workspace identity/backend/package files。
- Latest fix commit changes only `crates/workspace-server/src/identity.rs`
- `git diff --check 4cda83b7..HEAD`: passed。
- Spot-check of `identity.rs` confirmed `write_new_or_read_existing` returns parsed persisted identity on `AlreadyExists` and no longer uses fixed temp file / rename finalization。
Next action:
- Route back to Reviewer Pod for re-review of request_changes fix。
---
<!-- event: comment author: yoi-orchestrator at: 2026-06-23T07:31:34Z -->
## Comment
Re-review routing:
Sent the request_changes fix back to Reviewer Pod:
- `yoi-reviewer-00001KVSKGDYS-r1`
Review target:
- Worktree: `/home/hare/Projects/yoi/.worktree/00001KVSKGDYS-workspace-identity`
- Branch: `impl/00001KVSKGDYS-workspace-identity`
- Baseline: `4cda83b7`
- Previous implementation: `31565c9b feat: persist workspace identity`
- Fix commit: `49c9e190 fix: return persisted workspace identity`
- Main review diff: `4cda83b7..49c9e190`
- Focus diff: `31565c9b..49c9e190`
Reviewer was asked to verify the prior blocking issue is fixed and re-run validation including workspace-server tests/checks, web check/build, TicketDoctor, and Nix build。
---
<!-- event: review author: yoi-orchestrator at: 2026-06-23T07:35:05Z status: approve -->
## Review: approve
Reviewer Pod `yoi-reviewer-00001KVSKGDYS-r1` approved the request_changes fix and the full implementation.
Reviewed target:
- Worktree: `/home/hare/Projects/yoi/.worktree/00001KVSKGDYS-workspace-identity`
- Branch: `impl/00001KVSKGDYS-workspace-identity`
- Final HEAD: `49c9e190 fix: return persisted workspace identity`
- Full diff: `4cda83b7..49c9e190`
- Fix diff: `31565c9b..49c9e190`
Reviewer result:
- Prior blocker is fixed。
- `write_new_or_read_existing` now uses `OpenOptions::new().write(true).create_new(true).open(path)` for no-overwrite creation。
- On successful create, generated identity is written/synced and returned。
- On `AlreadyExists`, existing `.yoi/workspace.toml` is read/parsed and the persisted identity is returned。
- Fixed temp path and `path.exists() + rename` finalization are gone。
- This satisfies the invariant that the returned identity is the persisted/read authority under a local bootstrap race。
Blocking issues:
- なし。
Non-blocking concern:
- Direct final-file `create_new` write can expose an empty/partial file to a concurrent reader during the short write window or after crash/write failure, but this fails closed rather than returning the wrong identity and was not considered blocking for local bootstrap scope。
Requirement checks:
- Missing-file creation writes minimal v0 TOML schema。
- Invalid existing file fails closed without overwrite。
- Unknown fields remain denied and tested。
- UUIDv7 validation is real: UUID parse + version check + canonical lowercase hyphenated form check。
- `ServerConfig::local_dev` no longer constructs `local:{display}`
- SQLite `workspaces` upsert uses `.yoi/workspace.toml` identity values。
- Repository ids and host ids derive from persisted workspace id。
- Legacy `/api/repositories/local` alias remains accepted。
- Existing `local:*` DB rows are not destructively migrated。
- `.yoi/workspace.toml` remains safe to track and contains only `workspace_id`, `created_at`, `display_name`
Reviewer validation:
- `cargo fmt --check`: passed
- `git diff --check 4cda83b7..HEAD`: passed
- `cargo test -p yoi-workspace-server`: passed (`16 passed`)
- `cargo check -p yoi-workspace-server`: passed
- `cd web/workspace && deno task check && deno task build`: passed
- `cargo run -p yoi -- ticket doctor`: passed (`doctor: ok`)
- `nix build .#yoi --no-link`: passed
Worktree status:
- clean for tracked/untracked files at `49c9e190`
- Reviewer validation left only ignored generated artifacts in the child worktree。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T07:38:38Z -->
## Implementation report
Merge and validation completed.
Merged implementation:
- Merge commit: `2745f3d5 merge: workspace identity persistence`
- Implementation commits:
- `31565c9b feat: persist workspace identity`
- `49c9e190 fix: return persisted workspace identity`
Post-merge validation in orchestration worktree:
- `cargo fmt --check`: passed
- `git diff --check HEAD^1..HEAD`: passed
- `cargo test -p yoi-workspace-server`: passed
- `cargo check -p yoi-workspace-server`: passed
- `cd web/workspace && deno task check && deno task build`: passed (`svelte-check` 0 errors / 0 warnings; build completed)
- `cargo run -q -p yoi -- ticket doctor`: passed (`doctor: ok`)
- `nix build .#yoi --no-link`: passed
Generated artifact cleanup:
- Removed validation artifacts from orchestration worktree:
- `target/`
- `web/workspace/node_modules/`
- `web/workspace/.svelte-kit/`
- `web/workspace/build/`
- `git status --short --branch --ignored` shows only ignored `.yoi/tickets/.ticket-backend.lock`
Outcome:
- Local Workspace identity now persists in tracked `.yoi/workspace.toml`
- New ids are UUIDv7 and no longer derived from path/display basename。
- `.yoi/workspace.toml` parsing validates UUIDv7, UTC RFC3339 timestamp, non-empty display name, canonical id form, and denies unknown fields。
- Invalid existing identity files fail closed and are not overwritten。
- Missing-file creation returns the identity actually persisted/read, including `AlreadyExists` race handling。
- Workspace API / SQLite upsert / repository ids / host ids use the persisted Workspace identity。
- Existing legacy `local:*` DB rows are not destructively migrated。
- `.yoi/workspace.toml` remains a minimal safe project identity record, not a runtime/settings/secret store。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-23T07:38:42Z from: inprogress to: done reason: merged_and_validated field: state -->
## State changed
Reviewer approval 後に implementation を orchestration worktree へ merge し、post-merge validation と生成物 cleanup が完了したため done とする。
---
<!-- event: state_changed author: hare at: 2026-06-23T07:39:10Z from: done to: closed reason: closed field: state -->
## State changed
Ticket を closed にしました。
---
<!-- event: close author: hare at: 2026-06-23T07:39:10Z status: closed -->
## 完了
Local Workspace identity を tracked `.yoi/workspace.toml` に永続化する実装を完了した。
完了内容:
- `.yoi/workspace.toml` を local Workspace identity の authority として追加。
- v0 schema は `workspace_id`, `created_at`, `display_name` のみ。
- `workspace_id` は UUIDv7 canonical string として検証し、path / display basename から導出しない。
- `created_at` は UTC RFC3339 timestamp として検証。
- `display_name` は空文字列を拒否。
- unknown fields は v0 では明示的に deny。
- invalid existing file は fail closed し、自動上書きしない。
- missing file 初期化では `OpenOptions::create_new(true)` を使い、既存 file が race で作られた場合は persisted identity を read/parse して返す。
- fixed temp file / `path.exists()` + `rename` finalization は使わない。
- `ServerConfig::local_dev``local:{display}` を生成せず、persisted `WorkspaceIdentity` を受け取る。
- SQLite `workspaces` upsert、Workspace API、repository ids、host ids は persisted Workspace id を使う。
- legacy `/api/repositories/local` alias は維持。
- 既存 `local:*` DB rows の destructive migration はしない。
- `.yoi/workspace.toml` は絶対 path / secret / socket / runtime data を含まない安全な project identity record として維持。
統合:
- Implementation: `31565c9b feat: persist workspace identity`
- Fix: `49c9e190 fix: return persisted workspace identity`
- Merge: `2745f3d5 merge: workspace identity persistence`
検証:
- Reviewer approval: `yoi-reviewer-00001KVSKGDYS-r1`
- `cargo fmt --check`: passed
- `git diff --check HEAD^1..HEAD`: passed
- `cargo test -p yoi-workspace-server`: passed
- `cargo check -p yoi-workspace-server`: passed
- `cd web/workspace && deno task check && deno task build`: passed
- `cargo run -q -p yoi -- ticket doctor`: passed (`doctor: ok`)
- `nix build .#yoi --no-link`: passed
生成物 cleanup:
- orchestration worktree の `target/`, `web/workspace/node_modules/`, `web/workspace/.svelte-kit/`, `web/workspace/build/` を削除済み。
残作業:
- なし。将来必要なら legacy `local:*` row cleanup / migration policy は別 Ticket で扱う。
---
@@ -0,0 +1 @@
{"id":"orch-plan-20260623-064931-1","ticket_id":"00001KVSKJ0EA","kind":"accepted_plan","accepted_plan":{"summary":"Change Workspace Dashboard selection semantics so initial display and reload/background refresh do not auto-select rows; explicit keyboard/mouse selection still works; no-selection + TicketIntake composer submits global/new Intake; add focused tests.","branch":"impl/00001KVSKJ0EA-dashboard-no-auto-selection","worktree":"/home/hare/Projects/yoi/.worktree/00001KVSKJ0EA-dashboard-no-auto-selection","role_plan":"Orchestrator creates a dedicated child worktree and spawns a narrow-scope TUI Coder. Reviewer will be spawned read-only after Coder reports implementation commit(s). After approval, Orchestrator integrates into `orchestration`, validates TUI focused tests, records closure, and cleans only the child worktree/branch."},"author":"yoi-orchestrator","at":"2026-06-23T06:49:31Z"}
+107
View File
@@ -0,0 +1,107 @@
---
title: 'Dashboard reload と初期表示で row を自動選択しない'
state: 'closed'
created_at: '2026-06-23T06:44:20Z'
updated_at: '2026-06-23T07:24:20Z'
assignee: null
readiness: 'implementation_ready'
risk_flags: ['tui-ux', 'panel-selection', 'reload-state']
queued_by: 'workspace-panel'
queued_at: '2026-06-23T06:47:17Z'
---
## User claims / request snapshot
- Dashboard で `Esc` を押して row selection を消しても、reload によって頻繁に選択が戻る。
- そのため、実使用上は「何も選択していない状態」で composer を global / 新規 Intake として使うのが難しい。
- reload で勝手に row selection しないようにしたい。
- 追加合意: 初回 `yoi panel` 起動時から未選択にする。
## Confirmed facts / sources
- `crates/tui/src/dashboard/mod.rs`
- Dashboard は `selected_row: Option<PanelRowKey>` を持ち、`None` の未選択状態が実装上存在する。
- `Esc` 経路で `clear_panel_selection()` が呼ばれ、selection を消せる。
- reload / snapshot 更新後の selection visibility 補正により、visible row があると selection が戻る可能性がある。
- `crates/tui/src/dashboard/tests.rs`
- `Esc` が row selection を clear する既存テストがある。
- 関連 Ticket:
- `00001KTNS1AA8` closed: Panel composer / no-selection / `Esc` semantics の過去文脈。
- `00001KTFMMZP0` closed: background reload と selection/composer preservation の過去文脈。
## Unverified hypotheses
- 現行の reload 完了時の `ensure_selection_visible()` 系処理が、明示的な no-selection と「まだ selection がない初期/補正状態」を区別していない可能性が高い。
- 初期表示時の自動選択も同じ selection visibility 補正または初期化経路に由来している可能性がある。
## Undecided points / open questions
- なし。初回表示から未選択にする方針で合意済み。
## Background
Dashboard composer は row selection と composer target の組み合わせで挙動が変わる。`TicketIntake` target で Ticket row が選択されていると、入力が既存 Ticket の詳細化 / planning return として扱われる経路がある。一方、ユーザーが global / 新規 Intake として入力したい場合は row selection がない状態を維持できる必要がある。
現状は `Esc` で selection を消せても、background reload 等で選択が戻るため、ユーザーが意図した no-selection 状態を維持しづらい。
## Requirements
- 初回 `yoi panel` 表示時は、Ticket row / Pod row を自動選択しない。
- ユーザーが `Esc` で row selection を clear した後、reload / background reload / snapshot 更新が完了しても `selected_row = None` を維持する。
- reload によって既存の明示選択がまだ visible な場合は、その selection を維持してよい。
- reload によって選択中 row が消えた場合は安全に未選択へ落とす。
- Up/Down、mouse click、その他既存の明示的な row selection 操作では従来どおり selection を作れる。
- 未選択状態で `TicketIntake` target に非空 composer 入力して Enter した場合、既存 Ticket refinement ではなく global / 新規 Intake launch として扱われる。
## Acceptance criteria
- 初回 `yoi panel` 起動後、visible row があっても row selection はない。
- `Esc` で selection を clear した後、panel reload completion を挟んでも selection は復活しない。
- 未選択状態で `TicketIntake` target に composer 入力して Enter すると、新規/global Intake handoff になり、既存 Ticket の詳細化 handoff にならない。
- keyboard navigation または mouse click により、ユーザーは明示的に row を再選択できる。
- 既存の selected-row action、queue/close/open 操作、Ticket workflow state semantics は変えない。
- focused tests で少なくとも以下を確認する:
- initial Dashboard state has no selected row when rows exist;
- `Esc` clear -> reload completion -> still no selected row;
- no-selection + `TicketIntake` composer submit routes to global/new Intake rather than selected Ticket refinement.
## Binding decisions / invariants
- no-selection は first-class UX state として扱う。
- reload は、ユーザーが明示的に作った no-selection state を勝手に解除しない。
- 初回表示でも自動選択しない。
- composer draft は reload / selection clear によって失わない。
- Ticket state transition / lifecycle authority は変更しない。
- row click は selection のみを変更し、click だけで action dispatch しない既存方針を維持する。
## Implementation latitude
- 実装は `selected_row: Option<PanelRowKey>` に加えて「明示 no-selection」フラグを持つ、または selection visibility 補正の呼び出し条件を整理するなど、局所的に選んでよい。
- 初期表示・reload 完了・row 消失・navigation 開始の各経路で、selection を作る責務を明示的 user action に寄せられればよい。
- UI 表示文言の小変更は許容するが、この Ticket の主目的は selection semantics の修正であり、広い Dashboard layout redesign は含めない。
## Readiness
- readiness: implementation_ready
- risk_flags: [tui-ux, panel-selection, reload-state]
## Escalation conditions
- 初回未選択にすると keyboard-only navigation や blank Enter action の既存 contract が大きく変わることが判明した場合。
- selection visibility 補正が reload 以外の重要な safety/correctness を担っており、単純に止めると stale selection や action 誤爆が起きる場合。
- focused tests では確認できず、実端末 `yoi panel` / PTY 経路での挙動確認が必要になった場合。
## Validation
- `cargo test -q -p tui workspace_panel`
- 必要に応じて Dashboard 関連の focused test 名指定。
- 変更範囲に応じて `cargo fmt --check``git diff --check``cargo check -q`
- terminal delivery / timing に依存する挙動が疑わしい場合は、既存の Panel PTY/E2E 系テストまたは実 `yoi panel` で確認する。
## Related work
- `00001KTNS1AA8` — Panel composer / no-selection / `Esc` semantics の関連 closed Ticket。
- `00001KTFMMZP0` — Dashboard background reload / selection preservation の関連 closed Ticket。
- `crates/tui/src/dashboard/mod.rs`
- `crates/tui/src/dashboard/tests.rs`
- `crates/tui/src/dashboard/render.rs`
+26
View File
@@ -0,0 +1,26 @@
Dashboard の row selection を明示的 user action の結果として扱うように修正した。
完了内容:
- 初期表示で visible row があっても自動選択しない。
- reload / snapshot reconciliation で `selected_row = None` を勝手に selection へ戻さない。
- `Esc` による no-selection が reload を跨いで維持される。
- 選択中 row が消えた場合は `None` へ安全に落とす。
- keyboard navigation では no-selection から明示的に selection を作成できる。
- `list.selected_name` は実際の Pod row selection と同期し、no-selection / non-Pod selection では clear する。
- no-selection + `TicketIntake` composer submit が既存 Ticket refinement ではなく global/new Intake route へ進むことを focused test で確認した。
統合:
- Implementation: `5c242d96 fix: keep dashboard row selection explicit`
- Merge: `58904c44 merge: dashboard no auto selection`
検証:
- Reviewer approval: `yoi-reviewer-00001KVSKJ0EA-r1`
- `cargo fmt --check`: passed
- `git diff --check HEAD^1..HEAD`: passed
- `cargo test -q -p tui workspace_panel`: passed (`27 passed`)
- `cargo test -q -p tui dashboard`: passed (`111 passed`)
- reviewer 側追加確認 `cargo test -q -p tui`: passed (`372 passed`)
- `cargo run -q -p yoi -- ticket doctor`: passed (`doctor: ok`)
残作業:
- なし。
+313
View File
@@ -0,0 +1,313 @@
<!-- event: create author: LocalTicketBackend at: 2026-06-23T06:44:20Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: ticket-intake at: 2026-06-23T06:46:36Z -->
## Intake summary
ユーザー合意により ready 化。Ticket は `implementation_ready` で、初回 `yoi panel` 表示時から未選択、`Esc` 後の no-selection を reload が復活させない、未選択 + `TicketIntake` composer submit を global/new Intake に流す、という要件・受け入れ条件・validation が明記されている。未決定点なし。
---
<!-- event: state_changed author: ticket-intake at: 2026-06-23T06:46:36Z from: planning to: ready reason: user_requested_ready field: state -->
## State changed
ユーザーから `readyにして` の明示指示があり、対象 Ticket の item/thread を確認した。要件・受け入れ条件・risk flags・validation が揃っており、Orchestrator が routing 可能なため `ready` に遷移する。
---
<!-- event: state_changed author: workspace-panel at: 2026-06-23T06:47:17Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-23T06:49:08Z -->
## Decision
Routing decision: `implementation_ready_parallel`
Reason:
- Ticket body has concrete Dashboard selection semantics and focused test requirements。
- No relations / blockers / orchestration plan records exist。
- Active queued Workspace identity Ticket is backend/workspace-server focused and separate from TUI Dashboard row-selection code, so parallel implementation is acceptable。
- Orchestrator worktree is clean on `orchestration` at `13e76d35`; target worktree / branch is not present。
- Code map from Ticket body points to `crates/tui/src/dashboard/mod.rs`, `tests.rs`, and `render.rs`
IntentPacket:
Intent:
- Treat no-selection as a first-class Dashboard UX state: initial panel display and reload/background refresh must not auto-select rows, while explicit keyboard/mouse selection still works。
Binding decisions / invariants:
- Initial `yoi panel` display should have `selected_row = None` even when rows are visible。
- `Esc` clear must persist across reload/snapshot refresh; reload must not recreate selection after explicit no-selection。
- If a user explicitly selected a row and it remains visible after reload, that selection may be preserved。
- If selected row disappears after reload, fall back safely to no-selection。
- Composer draft must survive selection clear/reload。
- Existing row actions, queue/close/open operations, Ticket workflow state semantics, and row-click-is-selection-only policy must remain unchanged。
- No broad Dashboard layout redesign in this Ticket。
Requirements / acceptance criteria:
- Initial Dashboard state has no selected row when rows exist。
- Esc clear -> reload completion -> still no selected row。
- No-selection + `TicketIntake` composer submit routes to global/new Intake, not selected Ticket refinement。
- Keyboard navigation or mouse click can explicitly create selection again。
- Focused tests cover the above semantics。
Implementation latitude:
- Use an explicit no-selection flag or refine selection visibility correction conditions。
- The key is to make selection creation happen only from explicit user navigation/click/action paths, not from reload/init visibility correction。
Escalate if:
- Initial no-selection substantially breaks keyboard-only navigation or blank Enter action contract。
- Existing selection visibility correction has safety roles that cannot be preserved with a local change。
- Real terminal / PTY behavior is required beyond focused tests。
Validation plan:
- `cargo fmt --check`
- `cargo test -q -p tui workspace_panel`
- Dashboard focused tests as needed。
- `git diff --check`
- `cargo run -p yoi -- ticket doctor`
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-23T06:49:18Z from: queued to: inprogress reason: human_authorized_unblocked_dashboard_no_auto_selection field: state -->
## State changed
Human authorized queue routing from Workspace Dashboard. Ticket has concrete Dashboard selection semantics and no recorded blockers, so Orchestrator accepts implementation.
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T06:51:32Z -->
## Implementation report
Implementation start report:
- Created child implementation worktree:
- `/home/hare/Projects/yoi/.worktree/00001KVSKJ0EA-dashboard-no-auto-selection`
- Created branch:
- `impl/00001KVSKJ0EA-dashboard-no-auto-selection`
- Base commit:
- `4cda83b7 ticket: accept workspace identity and selection work`
- Confirmed tracked Ticket project records are visible in the child worktree。
- Confirmed `.yoi/memory` has no tracked/untracked entries in the child worktree check。
- Spawned sibling Coder Pod:
- `yoi-coder-00001KVSKJ0EA`
- Coder scope:
- read: `/home/hare/Projects/yoi`
- write: `/home/hare/Projects/yoi/.worktree/00001KVSKJ0EA-dashboard-no-auto-selection`
Parallelization note:
- `00001KVSKGDYS` is active separately and targets Workspace backend identity persistence. This Ticket should stay limited to TUI Dashboard selection semantics。
Next action:
- Wait for Coder implementation report, then spawn Reviewer read-only for the implementation diff。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T07:15:44Z -->
## Implementation report
Coder implementation report received from `yoi-coder-00001KVSKJ0EA`.
Implementation commit:
- `5c242d96 fix: keep dashboard row selection explicit`
Changed areas:
- `crates/tui/src/dashboard/mod.rs`
- Reload/snapshot reconciliation preserves only an explicitly selected visible row。
- Initial/reload visibility correction no longer auto-selects a row。
- Stale/disappeared selections fall back to `None`
- `Esc` clear state remains no-selection across reload。
- Keyboard navigation from no-selection explicitly creates selection on first visible row。
- `list.selected_name` stays aligned with actual Pod row selection and clears for non-Pod/no-selection states。
- `crates/tui/src/dashboard/tests.rs`
- Added focused coverage for initial visible rows with no selected row。
- Added `Esc` clear surviving reload while preserving composer draft。
- Added no-selection + `TicketIntake` submit routing to global/new Intake。
- Added keyboard navigation creating selection explicitly。
- Added reload fallback to no-selection when selected row disappears。
- Updated existing Dashboard tests to explicitly select rows where previous behavior depended on auto-selection。
Coder validation reported:
- `cargo fmt --check`: passed
- `cargo test -q -p tui workspace_panel`: passed
- `cargo test -q -p tui dashboard`: passed
- `git diff --check`: passed
- `cargo run -p yoi -- ticket doctor`: passed (`doctor: ok`)
Orchestrator evidence checked:
- Worktree clean on `impl/00001KVSKJ0EA-dashboard-no-auto-selection` at `5c242d96`
- Diff `4cda83b7..HEAD` touches expected Dashboard model/tests only。
- `git diff --check 4cda83b7..HEAD`: passed。
Next action:
- Route to read-only Reviewer Pod for acceptance review。
---
<!-- event: comment author: yoi-orchestrator at: 2026-06-23T07:16:33Z -->
## Comment
Review routing:
Spawned read-only Reviewer Pod:
- `yoi-reviewer-00001KVSKJ0EA-r1`
Review target:
- Worktree: `/home/hare/Projects/yoi/.worktree/00001KVSKJ0EA-dashboard-no-auto-selection`
- Branch: `impl/00001KVSKJ0EA-dashboard-no-auto-selection`
- Baseline: `4cda83b7`
- Implementation commit: `5c242d96 fix: keep dashboard row selection explicit`
- Diff: `4cda83b7..5c242d96`
Reviewer scope:
- read-only `/home/hare/Projects/yoi`
Reviewer focus:
- Initial/reload no-auto-selection semantics。
- Esc clear persistence and composer draft preservation。
- Explicit keyboard/mouse selection still works。
- Stale selection falls back to none。
- No-selection TicketIntake composer routing。
- `list.selected_name` sync and no stale Pod selection。
- No row-action / workflow semantic regressions。
Orchestrator will wait for reviewer verdict before integration。
---
<!-- event: review author: yoi-orchestrator at: 2026-06-23T07:23:02Z status: approve -->
## Review: approve
Reviewer Pod `yoi-reviewer-00001KVSKJ0EA-r1` approved the implementation.
Reviewed target:
- Worktree: `/home/hare/Projects/yoi/.worktree/00001KVSKJ0EA-dashboard-no-auto-selection`
- Branch: `impl/00001KVSKJ0EA-dashboard-no-auto-selection`
- Implementation: `5c242d96 fix: keep dashboard row selection explicit`
- Diff: `4cda83b7..5c242d96`
Reviewer evidence:
- 初期表示 / reload path は row selection を自動生成しない。
- `selected_row = None``Esc` 後も reload/snapshot reconciliation を跨いで維持される。
- 明示的な keyboard navigation は no-selection から selection を作成できる。
- 明示的 selection は row が visible な間は維持される。
- 消えた / stale row selection は `None` へ落ちる。
- `list.selected_name` は no-selection / non-Pod selection で clear され、実際の Pod row selection のみで設定される。
- no-selection + `TicketIntake` submit は global/new Intake launch へ進む。
- mouse click は selection-only のまま。
- 差分は Dashboard selection logic と focused tests に限定され、layout / render / workflow semantic の不要変更はない。
Blocking issues:
- なし。
Non-blocking concerns:
- なし。
Reviewer validation:
- `cargo fmt --check`: passed
- `git diff --check 4cda83b7..HEAD`: passed
- `cargo test -q -p tui workspace_panel`: passed (`27 passed`)
- `cargo test -q -p tui dashboard`: passed (`111 passed`)
- `cargo run -q -p yoi -- ticket doctor`: passed (`doctor: ok`)
- `cargo test -q -p tui`: passed (`372 passed`)
Worktree status:
- clean at `5c242d96`
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T07:24:05Z -->
## Implementation report
Merge and validation completed.
Merged implementation:
- Merge commit: `58904c44 merge: dashboard no auto selection`
- Implementation commit: `5c242d96 fix: keep dashboard row selection explicit`
Post-merge validation in orchestration worktree:
- `cargo fmt --check`: passed
- `git diff --check HEAD^1..HEAD`: passed
- `cargo test -q -p tui workspace_panel`: passed (`27 passed`)
- `cargo test -q -p tui dashboard`: passed (`111 passed`)
- `cargo run -q -p yoi -- ticket doctor`: passed (`doctor: ok`)
Outcome:
- 初期表示と reload/snapshot reconciliation が row selection を自動生成しなくなった。
- `Esc` 後の no-selection は reload を跨いで維持される。
- 明示的 keyboard navigation による selection 作成は維持された。
- `TicketIntake` composer の no-selection submit は global/new Intake route になる。
- stale selection は安全に `None` へ落ちる。
- Ticket lifecycle / row action semantics は変更していない。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-23T07:24:10Z from: inprogress to: done reason: merged_and_validated field: state -->
## State changed
実装は reviewer approval 後に orchestration worktree へ merge され、post-merge validation も通過したため done とする。
---
<!-- event: state_changed author: hare at: 2026-06-23T07:24:20Z from: done to: closed reason: closed field: state -->
## State changed
Ticket を closed にしました。
---
<!-- event: close author: hare at: 2026-06-23T07:24:20Z status: closed -->
## 完了
Dashboard の row selection を明示的 user action の結果として扱うように修正した。
完了内容:
- 初期表示で visible row があっても自動選択しない。
- reload / snapshot reconciliation で `selected_row = None` を勝手に selection へ戻さない。
- `Esc` による no-selection が reload を跨いで維持される。
- 選択中 row が消えた場合は `None` へ安全に落とす。
- keyboard navigation では no-selection から明示的に selection を作成できる。
- `list.selected_name` は実際の Pod row selection と同期し、no-selection / non-Pod selection では clear する。
- no-selection + `TicketIntake` composer submit が既存 Ticket refinement ではなく global/new Intake route へ進むことを focused test で確認した。
統合:
- Implementation: `5c242d96 fix: keep dashboard row selection explicit`
- Merge: `58904c44 merge: dashboard no auto selection`
検証:
- Reviewer approval: `yoi-reviewer-00001KVSKJ0EA-r1`
- `cargo fmt --check`: passed
- `git diff --check HEAD^1..HEAD`: passed
- `cargo test -q -p tui workspace_panel`: passed (`27 passed`)
- `cargo test -q -p tui dashboard`: passed (`111 passed`)
- reviewer 側追加確認 `cargo test -q -p tui`: passed (`372 passed`)
- `cargo run -q -p yoi -- ticket doctor`: passed (`doctor: ok`)
残作業:
- なし。
---
@@ -0,0 +1 @@
{"id":"orch-plan-20260623-132129-1","ticket_id":"00001KVSMJJNV","kind":"accepted_plan","accepted_plan":{"summary":"Ticket 00001KVSMJJNV を implementation worktree `.worktree/00001KVSMJJNV-paused-ctrlx-cancel` / branch `work/00001KVSMJJNV-paused-ctrlx-cancel` で開始する。Paused 中の `Ctrl+X` が shutdown ではなく cancel になり、paused turn を破棄して次入力可能にすることが主目的。","branch":"work/00001KVSMJJNV-paused-ctrlx-cancel","worktree":"/home/hare/Projects/yoi/.worktree/00001KVSMJJNV-paused-ctrlx-cancel","role_plan":"Coder Pod: implementation worktree で TUI key handling / controller paused cancel semantics / focused tests を実装し、ローカル commit まで行う。Orchestrator: output/diff/tests を確認後、Reviewer Pod に recorded IntentPacket に基づく review を依頼する。"},"author":"yoi-orchestrator","at":"2026-06-23T13:21:29Z"}
+105
View File
@@ -0,0 +1,105 @@
---
title: 'TUI Console: Paused 中の Ctrl+X で中断ターンをキャンセルできるようにする'
state: 'closed'
created_at: '2026-06-23T07:02:07Z'
updated_at: '2026-06-23T13:56:16Z'
assignee: null
readiness: 'implementation_ready'
risk_flags: ['tui-keybinding', 'pod-lifecycle']
queued_by: 'workspace-panel'
queued_at: '2026-06-23T08:41:00Z'
---
## User claims / request snapshot
- TUI Console で、`Ctrl+C` により Pause している状態のとき、`Ctrl+X` でキャンセルできるようにしたい。
## Confirmed facts / sources
- 既存 Ticket:
- 未完了 Ticket に直接 duplicate は確認できなかった。
- `00001KSVP63K8 Support immediate in-flight TUI composer injection` は TUI 実行中入力に関連するが、「Do not interrupt/cancel the current run」が non-goal なので今回の要求とは別件。
- `crates/tui/src/console/mod.rs`
- 現在の `Ctrl+X``PodStatus::Running` のときだけ `Method::Cancel` を返す。
- `PodStatus::Paused | PodStatus::Idle` のときは `Method::Shutdown` を返している。
- `Ctrl+C` は Running では `Method::Pause`、Idle/Paused では TUI 終了の 2-tap guard として扱われている。
- 既存 test `pause_and_cancel_clear_queued_input` は名前上 pause/cancel を扱うが、`Ctrl+C` 後に app status を Paused に更新していないため、Paused 状態での `Ctrl+X` cancel は実質カバーしていない。
- `crates/protocol/src/lib.rs`
- `Method::Pause` は in-flight turn を止めて Paused に遷移するもの。
- コメント上、`Cancel` は破棄して Idle に戻る意味を持ち、Paused は `Resume` または新規 `Run` が可能。
- `crates/pod/src/controller.rs`
- 非 running 側の `Method::Cancel` は現状 `NotRunning` error を返す経路があるため、TUI keymap 変更だけでなく Paused 状態の cancel semantics を確認・必要なら実装する必要がある。
## Unverified hypotheses
- 実装は比較的小さい可能性が高いが、`Paused` 状態で `Method::Cancel` を受け取った時に、Worker/Pod の interrupted state や orphan tool-use cleanup をどう clear するべきかは実装時に確認が必要。
- UI 表示は paused hint に `Ctrl+X to cancel` 相当を追加するだけで足りる可能性がある。
## Undecided points / open questions
- blocking な未決定点はなし。
- Orchestrator/実装者判断で、`Ctrl+X` の Idle 挙動を現状通り `Shutdown` に残すか、Paused のみ `Cancel` に分岐するのがよさそう。
## Background
`Ctrl+C` で Pause した turn は resume 可能だが、ユーザーが「やっぱりこの中断 turn は破棄したい」と判断した時に、Console 上の自然な操作として `Ctrl+X` cancel が効くべき。現在は Paused/Idle の `Ctrl+X` が shutdown に割り当てられており、Paused turn のキャンセル導線として機能していない。
## Requirements
- TUI Console で Pod が `Paused` のとき、`Ctrl+X` は shutdown ではなく paused/interrupted turn の cancel を要求する。
- `Running` 中の `Ctrl+X -> Method::Cancel` は維持する。
- `Idle` 中の `Ctrl+X` の挙動は、意図的に変更する理由がなければ現状の shutdown を維持する。
- Paused 状態で cancel した後、Pod は resume 可能な paused turn を残さず、Idle 相当として次の入力を受けられる。
- queued input がある場合は、既存 Running cancel と同様に誤送信されないよう clear される。
- UI の paused hint / actionbar / tests など、ユーザーが操作を理解できる範囲を必要に応じて更新する。
## Acceptance criteria
- `Ctrl+C` で Running turn を Pause した後、Console 上で `Ctrl+X` を押すと、TUI 終了や Pod shutdown ではなく turn cancel が実行される。
- Paused cancel 後、`Enter` で前の turn を resume しようとしても resume されない。
- Paused cancel 後、新規入力は通常の新規 turn として扱われる。
- Running 中の `Ctrl+X` cancel と Idle 中の `Ctrl+X` 挙動に意図しない regressions がない。
- Focused tests が Paused 状態の `Ctrl+X` key handling と Pod/controller 側の cancel semantics をカバーする。
## Binding decisions / invariants
- `Ctrl+C` の Pause semantics は変更しない。
- `Ctrl+C` の Idle/Paused 2-tap TUI quit guard は、この Ticket では置き換えない。
- この Ticket は in-flight input injection / history append policy を扱わない。
- provider stream を直接 mutate しない。
- cancel は hidden context/history mutation ではなく、既存の typed `Method::Cancel` semantics またはそれに準じる明示的 lifecycle 操作で表現する。
## Implementation latitude
- `Paused` 中の `Ctrl+X``Method::Cancel` に変えるだけで足りるか、controller/worker 側に Paused cancel handling を追加するかは実装調査で判断してよい。
- UI hint の文言や配置は実装者に任せてよい。
- 必要なら既存 test を修正・分割して、「Running cancel」と「Paused cancel」を明確に分ける。
## Readiness
- readiness: implementation_ready
- risk_flags: [tui-keybinding, pod-lifecycle]
## Escalation conditions
- `Method::Cancel` を Paused 状態に拡張すると session/history cleanup や orphan tool-result closure の設計判断が必要になる場合は、実装者は Orchestrator/maintainer に戻す。
- `Ctrl+X` の Idle 挙動変更が必要になる場合は、別途合意を取る。
## Validation
- Focused tests:
- `cargo test -p tui pause` または該当 TUI key handling tests
- `cargo test -p pod cancel` または controller paused cancel の focused tests
- 通常確認:
- `cargo fmt --check`
- 必要に応じて `cargo check -q`
- terminal UX 変更なので、可能なら実機 TUI/manual check も推奨。
## Related work
- Related, not duplicate: `.yoi/tickets/00001KSVP63K8`
- Files:
- `crates/tui/src/console/mod.rs`
- `crates/tui/src/ui.rs`
- `crates/protocol/src/lib.rs`
- `crates/pod/src/controller.rs`
+28
View File
@@ -0,0 +1,28 @@
Ticket 00001KVSMJJNV は完了。
実装内容:
- TUI Console の `Paused` 状態で `Ctrl+X` を TUI/Pod shutdown ではなく `Method::Cancel` として扱うようにした。
- `Idle``Ctrl+X` shutdown behavior と、`Running` の cancel behavior は維持した。
- paused cancel 後に resume 可能な interrupted state が残らず、次の入力が fresh run になるよう controller / Pod lifecycle を更新した。
- paused cancel を normal `RunCompleted { Finished }` として永続化せず、typed session log entry `LogEntry::PausedTurnAbandoned { ts }` として記録するようにした。
- replay / restore で `PausedTurnAbandoned``last_run_interrupted` を clear するようにした。
- focused tests を追加・更新し、TUI key handling、controller behavior、session log JSON/replay semantics、fake normal-finished run が残らないことを確認した。
主要 commits:
- `90b1a1fc tui: cancel paused turns with ctrl-x`
- `8c8fb014 fix: log paused cancel lifecycle explicitly`
- `76c80054 merge: 00001KVSMJJNV paused ctrl-x cancel`
Review:
- 初回 review は lifecycle log semantics の不一致で request_changes。
- `PausedTurnAbandoned` による typed lifecycle representation を追加後、Reviewer が approve。
Validation:
- `cargo test -p session-store paused_turn_abandoned -- --nocapture`
- `cargo test -p pod paused_cancel_abandons_resume_and_next_input_is_fresh_run -- --nocapture`
- `cargo test -p tui ctrl_x -- --nocapture`
- `cargo fmt --check`
- `git diff --check HEAD~1..HEAD`
Merge note:
- orchestration branch で実装・Ticket 記録・close resolution をまとめ、merge target へ fast-forward 予定。
+318
View File
@@ -0,0 +1,318 @@
<!-- event: create author: ticket-intake at: 2026-06-23T07:02:07Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: state_changed author: workspace-panel at: 2026-06-23T08:41:00Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-23T12:42:28Z -->
## Decision
Routing decision: blocked_by_dependency_or_missing_authority
Reason:
- Ticket 自体は `implementation_ready` と判断できる。目的、binding decisions / invariants、acceptance criteria、validation、escalation conditions は揃っており、`tui-keybinding` / `pod-lifecycle` risk は reviewer focus として扱える。
- ただし routing 時点の merge target `/home/hare/Projects/yoi` に、この Ticket の対象ではない `crates/tui/src/dashboard/mod.rs``crates/tui/src/dashboard/tests.rs` の未コミット変更がある。workflow の dirty-state gate に従い、この状態では implementation worktree 作成 / Coder Pod 起動 / merge へ進めない。
- 既存 worktree entry `.worktree/00001KVSMJJNV-paused-ctrlx-cancel``git worktree list` 上で prunable と表示され、gitdir が存在しないため、再利用には cleanup/recreate 判断が必要。
Evidence checked:
- Ticket body / thread / artifacts: TicketShow で body、queued event、artifacts なしを確認。
- Ticket relations: TicketRelationQuery で blocker relation なし。
- Orchestration plan: TicketOrchestrationPlanQuery で accepted-plan / conflict / waiting record なし。
- Queue state: queued Ticket はこの 1 件のみ。ready / inprogress は 0 件。
- Workspace/worktree/Pod state: `/home/hare/Projects/yoi``develop...origin/develop [ahead 48]` かつ dashboard files に dirty change あり。`.worktree/orchestration` は clean。visible Pods は peer `yoi` idle と self `yoi-orchestrator` running、spawned child なし。
Next action:
- merge target の未コミット dashboard 変更を所有者が commit / revert / 明示的に退避許可した後、この Ticket を再 routing する。
- その時点で blocker がなければ `queued -> inprogress` を記録してから、worktree を clean に recreate し、Coder/Reviewer loop に渡す。
Escalate if:
- dashboard 変更がこの Ticket と同時に保持すべき作業なら、別 Ticket/branch として扱うか、今回の implementation worktree/merge 計画に含めるかを人間が決める。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-23T13:21:02Z -->
## Decision
Routing correction:
直前の `blocked_by_dependency_or_missing_authority` 判断は取り下げる。merge target `/home/hare/Projects/yoi` の dashboard files dirty は最終 merge 時の確認事項だが、この Ticket の implementation acceptance / implementation worktree 作成を止める gate として扱うのは過剰だった。
Corrected routing decision: implementation_ready
Evidence checked:
- Ticket body / thread: 目的、requirements、acceptance criteria、binding decisions / invariants、implementation latitude、escalation conditions、validation が揃っている。
- TicketRelationQuery: blocker relation なし。
- TicketOrchestrationPlanQuery: accepted-plan / conflict / blocker / waiting-capacity record なし。
- Queue/capacity: queued Ticket はこの 1 件。visible spawned child はなし。
- Worktree state: orchestration worktree は Ticket 記録変更のみ。既存 `.worktree/00001KVSMJJNV-paused-ctrlx-cancel` は prunable なので cleanup/recreate 対象。
Next action:
- `queued -> inprogress` を記録する。
- stale/prunable implementation worktree entry を掃除して、`.worktree/00001KVSMJJNV-paused-ctrlx-cancel` を作り直す。
- Coder Pod に IntentPacket を渡して実装を開始する。
Merge note:
- merge target 側の unrelated dashboard dirty は final merge 前に再確認し、必要なら人間へ確認する。implementation worktree 内の作業開始 blocker にはしない。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-23T13:21:07Z from: queued to: inprogress reason: orchestrator_acceptance field: state -->
## State changed
Orchestrator acceptance: Ticket evidence、relation blockers、orchestration plan、worktree/capacity 状態を再確認し、implementation_ready と判断した。implementation side effect 前に queued -> inprogress を記録する。
Acceptance basis:
- blocker relation なし。
- orchestration plan blocker/conflict なし。
- queued Ticket はこの 1 件で child capacity あり。
- risk flags は `tui-keybinding` / `pod-lifecycle` として IntentPacket の reviewer focus に反映する。
Next:
- implementation worktree を clean に作成し、Coder Pod に routing する。
---
<!-- event: plan author: yoi-orchestrator at: 2026-06-23T13:21:47Z -->
## Plan
IntentPacket for implementation
Intent:
- TUI Console で Pod が `Paused` のとき、`Ctrl+X` を TUI shutdown ではなく paused/interrupted turn の cancel として扱えるようにする。
- Paused cancel 後は resume 可能な paused turn を残さず、次の新規入力を通常の新規 turn として受けられる状態にする。
Binding decisions / invariants:
- `Ctrl+C` の Running -> Pause semantics は変更しない。
- `Ctrl+C` の Idle/Paused 2-tap TUI quit guard はこの Ticket では置き換えない。
- `Running` 中の `Ctrl+X -> Method::Cancel` は維持する。
- `Idle` 中の `Ctrl+X` は、必要な明示理由がなければ現状の shutdown を維持する。
- in-flight input injection / history append policy は扱わない。
- provider stream を直接 mutate しない。
- cancel は hidden context/history mutation ではなく、既存の typed `Method::Cancel` semantics またはそれに準じる明示的 lifecycle 操作で表現する。
Requirements / acceptance criteria:
- `Ctrl+C` で Running turn を Pause した後、Console 上で `Ctrl+X` を押すと TUI exit / Pod shutdown ではなく turn cancel が実行される。
- Paused cancel 後、`Enter` で前の turn が resume されない。
- Paused cancel 後、新規入力は通常の新規 turn として扱われる。
- Running cancel と Idle `Ctrl+X` に意図しない regression がない。
- focused tests が Paused 状態の `Ctrl+X` key handling と Pod/controller 側の cancel semantics をカバーする。
Implementation latitude:
- `Paused` 中の `Ctrl+X``Method::Cancel` に変えるだけで足りるか、controller/worker 側に Paused cancel handling を追加するかは実装調査で判断してよい。
- UI hint の文言や配置は実装者に任せてよい。
- 既存 test の修正・分割は許容する。
Escalate if:
- `Method::Cancel` を Paused 状態に拡張すると session/history cleanup や orphan tool-result closure の新しい設計判断が必要になる。
- `Ctrl+X` の Idle 挙動変更が必要になる。
- provider stream / history mutation / lifecycle authority の境界を変える必要が出る。
Validation:
- focused: `cargo test -p tui pause` または該当 TUI key handling tests。
- focused: `cargo test -p pod cancel` または controller paused cancel tests。
- `cargo fmt --check`
- 必要に応じて `cargo check -q`
Current code map:
- 主対象: `crates/tui/src/console/mod.rs`
- 関連: `crates/tui/src/ui.rs`, `crates/protocol/src/lib.rs`, `crates/pod/src/controller.rs`
- 既存事実: `Ctrl+X` は Running のみ `Method::Cancel`、Paused/Idle は shutdown 側。controller の非-running Cancel は `NotRunning` 経路がある可能性。
Critical risks / reviewer focus:
- Paused と Idle の key handling を混同して shutdown behavior を壊していないか。
- Running cancel の既存 behavior / queued input clear に regression がないか。
- Paused cancel 後に resume 可能な interrupted state が残らないか。
- Cancel semantics が hidden history/context mutation になっていないか。
- tests が実際に app status を Paused に更新した状態をカバーしているか。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T13:37:51Z -->
## Implementation report
Implementation report
Worktree / branch:
- `/home/hare/Projects/yoi/.worktree/00001KVSMJJNV-paused-ctrlx-cancel`
- `work/00001KVSMJJNV-paused-ctrlx-cancel`
Commit:
- `90b1a1fc tui: cancel paused turns with ctrl-x`
Implemented changes:
- `crates/tui/src/console/mod.rs`: `Ctrl+X` key handling を Paused 状態でも cancel path に乗せ、Idle の shutdown behavior と分離。
- `crates/tui/src/ui.rs`: Paused UI hint を cancel 操作に合わせて更新。
- `crates/pod/src/controller.rs` / `crates/pod/src/pod.rs`: Paused 状態の `Method::Cancel``NotRunning` ではなく paused turn cancel として処理し、resume 可能な interrupted state を破棄して次入力を fresh run として扱う経路を追加。
- `crates/pod/tests/controller_test.rs`: Paused cancel 後に resume されず、次入力が fresh run になることを確認する focused test を追加。
Validation run by Orchestrator:
- `cargo test -p tui ctrl_x -- --nocapture`
- `cargo test -p pod paused_cancel_abandons_resume_and_next_input_is_fresh_run -- --nocapture`
- `git diff --check`
- `cargo fmt --check`
Next:
- Reviewer Pod に recorded IntentPacket と commit diff を基準に review を依頼する。
---
<!-- event: review author: reviewer-00001KVSMJJNV-ctrlx-cancel at: 2026-06-23T13:42:48Z status: request_changes -->
## Review: request changes
Review result: request_changes
Findings:
- `crates/pod/src/pod.rs:1871-1884` は paused cancel cleanup を `LogEntry::RunCompleted { result: WorkerResult::Finished, interrupted: false }` として記録している。しかしここでは `run()` / `resume()` が完了したわけではなく、paused turn を明示的に abandon している。`crates/session-store/src/segment_log.rs:31` および `:100-106` の session log semantics では、`RunCompleted``run()` / `resume()` call の終了を表し、`Finished` は通常の Worker completion を意味する。現在の immediate replay は `interrupted` を使うため focused behavior は通るが、durable session/audit stream が cancel 後に normal `Finished` run を記録してしまう。
- Ticket の invariant は cancel を explicit typed lifecycle semantics で表現することなので、typed paused-cancel / abandoned-turn lifecycle record を追加するか、log schema / consumers / tests を更新して durable history が normal `Finished` run を主張しないようにする必要がある。
Validation performed:
- `cargo test -p tui ctrl_x -- --nocapture` passed
- `cargo test -p pod paused_cancel_abandons_resume_and_next_input_is_fresh_run -- --nocapture` passed
- `git diff --check 5954021c..90b1a1fc` passed
- `cargo fmt --check` passed
- `git status --short` clean
Rationale:
- TUI key handling separation は正しい。`Paused``Ctrl+X``Method::Cancel``Idle``Shutdown` のまま、`Ctrl+C` quit/pause behavior も変更されていない。
- controller test は Pod を実際に `Paused` にし、cancel 後の `Resume` rejection と次入力の fresh `Run` を確認している。
- blocker は persisted lifecycle representation。functional state reset は実装されているが、session log が abandoned paused turn を normal finished run として記録している点が durable semantics と不一致。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T13:49:36Z -->
## Implementation report
Implementation report update after review request
Reviewer-requested change addressed in implementation worktree:
- `/home/hare/Projects/yoi/.worktree/00001KVSMJJNV-paused-ctrlx-cancel`
- branch `work/00001KVSMJJNV-paused-ctrlx-cancel`
New commit:
- `8c8fb014 fix: log paused cancel lifecycle explicitly`
Change summary:
- `crates/session-store/src/segment_log.rs`: typed `LogEntry::PausedTurnAbandoned { ts }` を追加し、replay / `collect_state``last_run_interrupted` を clear するようにした。JSON round-trip と replay semantics の tests を追加。
- `crates/pod/src/pod.rs`: paused cancel cleanup で fake `RunCompleted { result: Finished }` を記録せず、`PausedTurnAbandoned` を記録するように変更。
- `crates/pod/src/segment_log_sink.rs`: lifecycle コメントを更新。
- `crates/pod/tests/controller_test.rs`: paused cancel が `PausedTurnAbandoned` を記録し、normal finished run としては記録されないことを確認する assertion を追加。
Validation run by Orchestrator:
- `cargo test -p session-store paused_turn_abandoned -- --nocapture`
- `cargo test -p pod paused_cancel_abandons_resume_and_next_input_is_fresh_run -- --nocapture`
- `cargo test -p tui ctrl_x -- --nocapture`
- `git diff --check 5954021c..HEAD`
Next:
- Reviewer に再 review を依頼する。
---
<!-- event: review author: reviewer-00001KVSMJJNV-ctrlx-cancel at: 2026-06-23T13:53:16Z status: approve -->
## Review: approve
Review result: approve
Findings:
- なし。
Validation performed by Reviewer:
- `cargo test -p session-store paused_turn_abandoned -- --nocapture` — passed
- `cargo test -p pod paused_cancel_abandons_resume_and_next_input_is_fresh_run -- --nocapture` — passed
- `cargo test -p tui ctrl_x -- --nocapture` — passed
- `git diff --check 5954021c..HEAD` — passed
- `cargo fmt --check` — passed
- `git status --short` — clean
Rationale:
- prior blocker は解消済み。`Pod::cancel_paused_turn()` は fake `RunCompleted { Finished, interrupted: false }` ではなく `LogEntry::PausedTurnAbandoned` を記録する。
- typed lifecycle entry は session log schema / replay に含まれ、`last_run_interrupted` を clear するため restore 後も resume 不可になる。
- tests は JSON kind `paused_turn_abandoned`、replay semantics、explicit lifecycle log、fake normal-finished run が残らないことを確認している。
- TUI acceptance criteria も満たしている。Paused `Ctrl+X``Method::Cancel`、Idle `Ctrl+X` は shutdown のまま、Running cancel は queued input clear を含む既存 behavior を維持している。
- controller test は Paused 到達、cancel 後 Idle、resume rejection、cancel/resume で追加 LLM request が発生しないこと、次入力が fresh run になることを確認している。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-23T13:56:05Z from: inprogress to: done reason: implementation_review_validation_passed field: state -->
## State changed
Implementation, review, and post-merge validation completed in orchestration worktree.
Evidence:
- Implementation commits merged into orchestration: `90b1a1fc`, `8c8fb014`, merge commit `76c80054`.
- Reviewer approved after requested lifecycle-log fix.
- Validation on merged orchestration branch passed:
- `cargo test -p session-store paused_turn_abandoned -- --nocapture`
- `cargo test -p pod paused_cancel_abandons_resume_and_next_input_is_fresh_run -- --nocapture`
- `cargo test -p tui ctrl_x -- --nocapture`
- `cargo fmt --check`
- `git diff --check HEAD~1..HEAD`
Next:
- Close Ticket with resolution, then fast-forward merge target if safe.
---
<!-- event: state_changed author: hare at: 2026-06-23T13:56:16Z from: done to: closed reason: closed field: state -->
## State changed
Ticket を closed にしました。
---
<!-- event: close author: hare at: 2026-06-23T13:56:16Z status: closed -->
## 完了
Ticket 00001KVSMJJNV は完了。
実装内容:
- TUI Console の `Paused` 状態で `Ctrl+X` を TUI/Pod shutdown ではなく `Method::Cancel` として扱うようにした。
- `Idle``Ctrl+X` shutdown behavior と、`Running` の cancel behavior は維持した。
- paused cancel 後に resume 可能な interrupted state が残らず、次の入力が fresh run になるよう controller / Pod lifecycle を更新した。
- paused cancel を normal `RunCompleted { Finished }` として永続化せず、typed session log entry `LogEntry::PausedTurnAbandoned { ts }` として記録するようにした。
- replay / restore で `PausedTurnAbandoned``last_run_interrupted` を clear するようにした。
- focused tests を追加・更新し、TUI key handling、controller behavior、session log JSON/replay semantics、fake normal-finished run が残らないことを確認した。
主要 commits:
- `90b1a1fc tui: cancel paused turns with ctrl-x`
- `8c8fb014 fix: log paused cancel lifecycle explicitly`
- `76c80054 merge: 00001KVSMJJNV paused ctrl-x cancel`
Review:
- 初回 review は lifecycle log semantics の不一致で request_changes。
- `PausedTurnAbandoned` による typed lifecycle representation を追加後、Reviewer が approve。
Validation:
- `cargo test -p session-store paused_turn_abandoned -- --nocapture`
- `cargo test -p pod paused_cancel_abandons_resume_and_next_input_is_fresh_run -- --nocapture`
- `cargo test -p tui ctrl_x -- --nocapture`
- `cargo fmt --check`
- `git diff --check HEAD~1..HEAD`
Merge note:
- orchestration branch で実装・Ticket 記録・close resolution をまとめ、merge target へ fast-forward 予定。
---
@@ -0,0 +1,37 @@
# 実装報告: 00001KVTNAY20
## 変更概要
- `client::spawn``PodProcessLaunchConfig``PodProcessLaunchOptions` を導入し、低レベルの Pod プロセス起動設定から Ticket role marker を分離した。
- Ticket role 起動は `TicketRoleLaunchPlan::spawn_options()` 経由で hidden CLI marker を渡す形にし、`TicketRoleLaunchResult` に Run 受理証跡 (`TicketRoleLaunchAcceptanceEvidence`) を追加した。
- Workspace server の `LocalRuntimeBridge``WorkspaceWorkerRuntime` trait の実装として整理し、hosts/workers 一覧、worker lookup、spawn/stop typed request/result、将来の proxy/stream 接続点を型として追加した。
- Workspace 側の spawn request shape は policy intent ベースにし、browser/API caller から raw `workspace_root` / `cwd` / executable path / raw profile selector を受け取らない形にした。
- Dashboard/TUI 側の直接 spawn 呼び出しを新しい low-level config/options 分離に追従した。
## 変更ファイル
- `crates/client/src/lib.rs`
- `crates/client/src/spawn.rs`
- `crates/client/src/ticket_role.rs`
- `crates/tui/src/dashboard/mod.rs`
- `crates/tui/src/spawn.rs`
- `crates/workspace-server/src/hosts.rs`
- `crates/workspace-server/src/server.rs`
## 検証結果
- `cargo test -p yoi-workspace-server`: 成功
- `cargo check -p yoi`: 成功
- `cd web/workspace && deno task check && deno task build`: 成功
- `cargo test -p client`: 成功(追加確認)
- `git diff --check`: 成功
## コミット
- 実装コミット: `217a4828d73ab553b5406cc7e22e43b1ec7be48e`
## 残リスク / 非ゴールとして残したもの
- `WorkspaceWorkerRuntime::spawn_worker` / `stop_worker` は typed boundary と request/result を用意した段階で、実際の Worker operation UI 完成、stream proxy、remote Host protocol、認可/権限、registry locking までは実装していない。
- low-level launcher は trusted in-process resolver からの追加 CLI args を受け取れるが、Ticket role などのドメイン概念は `PodProcessLaunchConfig` からは除外している。
- TS 型生成やフロントエンド API surface の追加は本 Ticket の非ゴールとして変更していない。
@@ -0,0 +1,2 @@
{"id":"orch-plan-20260623-192711-1","ticket_id":"00001KVTNAY20","kind":"accepted_plan","accepted_plan":{"summary":"Ticket 00001KVTNAY20 は implementation_ready。専用 worktree `/home/hare/Projects/yoi/.worktree/00001KVTNAY20-worker-runtime-spawn` と branch `work/00001KVTNAY20-worker-runtime-spawn` で、Workspace Worker runtime spawn abstraction を実装し、routing record の IntentPacket を基準に coder/reviewer sibling loop へ進める。root/original workspace では git/read/write/validate/merge/cleanup を行わない。","branch":"work/00001KVTNAY20-worker-runtime-spawn","worktree":"/home/hare/Projects/yoi/.worktree/00001KVTNAY20-worker-runtime-spawn","role_plan":"Orchestrator: acceptance/routing, worktree creation, final integration/validation/cleanup. Coder: implementation in dedicated child worktree with narrow write scope. Reviewer: read-only review by default against recorded IntentPacket, validation evidence, and diff. Orchestrator handles merge into orchestration branch after reviewer approval."},"author":"yoi-orchestrator","at":"2026-06-23T19:27:11Z"}
{"id":"orch-plan-20260623-193340-2","ticket_id":"00001KVTNAY20","kind":"waiting_capacity_note","note":"Coder sibling execution is temporarily blocked by runtime/provider startup failures, not by Ticket requirements. `builtin:coder` started but both initial run and retry failed with provider API error `Unsupported content type`; alternate `inherit` / `builtin:default` / `builtin:companion` recovery attempts did not produce a socket within 10s. Worktree `/home/hare/Projects/yoi/.worktree/00001KVTNAY20-worker-runtime-spawn` remains clean at orchestration commit `b8388655`; no implementation diff exists yet. Human/tooling follow-up is needed before continuing multi-agent implementation, unless Orchestrator is explicitly authorized to implement directly.","author":"yoi-orchestrator","at":"2026-06-23T19:33:40Z"}
+158
View File
@@ -0,0 +1,158 @@
---
title: 'Abstract Workspace Worker runtime spawn operations'
state: 'done'
created_at: '2026-06-23T16:34:39Z'
updated_at: '2026-06-24T10:35:01Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-23T19:25:09Z'
---
## 背景
Workspace web / Workspace backend から Worker を起動・停止・操作する UI/API を作るには、現在 read-only live view として実装されている runtime bridge を、Worker 操作も扱える backend abstraction に拡張する必要がある。
現状:
- `crates/workspace-server/src/hosts.rs``LocalRuntimeBridge` は local runtime metadata を読んで `HostSummary` / `WorkerSummary` を返す read-only bridge である。
- `/api/hosts``/api/workers``/api/hosts/{host_id}/workers` はこの bridge を直接使っている。
- Worker 起動の最終境界は `yoi pod [POD_OPTIONS]` process launch である。
- 既存の `client::spawn::SpawnConfig``yoi pod` 起動 options の typed 表現に近いが、`ticket_role` のような Ticket/Orchestration domain の情報も混ざっている。
- Workspace backend には Worker spawn / stop / protocol proxy の抽象境界がまだない。
- Worker 起動を web backend から実装する際、workspace root / cwd / pod name / profile / role-session claim の経路が TUI とズレると、Panel 表示や workspace 所属判定が壊れる。
この Ticket では、Workspace backend から Worker spawn 操作を実装する前段として、**Pod process 起動境界**と**Ticket / Orchestration intent 境界**を分離して整理する。低レベル起動は Ticket system を知らず、Ticket/Orchestration 情報は上位 resolver / launch policy が低レベル config へ落とす。
## 要件
### Layering / responsibility split
起動系は少なくとも以下の層に分ける。
1. **Pod process launch layer**
- `yoi pod [POD_OPTIONS]` を起動する唯一の低レベル境界。
- Ticket / Orchestration / Role / TicketId を直接知らない。
- `workspace_root``pod_name``profile``manifest``project``store``cwd``initial_input``workflow``require_pod_state` のような process 起動 options だけを扱う。
2. **Worker runtime layer**
- Workspace backend / API から見える Worker 操作境界。
- hosts/workers live view、worker lookup、spawn/stop/protocol proxy の entrypoint を持つ。
- API intent を低レベル Pod process launch config へ解決する。
3. **Ticket / Orchestration resolver layer**
- Ticket ID、Ticket role、Orchestrator/Coder/Reviewer といった workflow domain を扱う。
- Role/Profile/Workflow/Ticket context を解決し、低レベル Pod process launch config と必要な launch policy / prompt input を作る。
- Ticket/Orchestration 情報を低レベル launcher に直接漏らさない。
### Pod process launch abstraction
- `SpawnConfig` をそのまま Workspace backend API の入力として扱わない。
- 低レベルの起動 config は Ticket/Orchestration domain を持たない形に整理する。
- 既存 `SpawnConfig` を改名/分割するか、別の `PodProcessLaunchConfig` / `PodLaunchOptions` を導入する。
- `ticket_role` は低レベル process config から分離する。
- 必要なら `PodLaunchPolicy` / `ProfileLaunchPolicy` のような別引数/別 layer に移す。
- `--ticket-role` は現状 hidden CLI marker として存在してよいが、汎用 `yoi pod` 起動 config の概念には含めない。
- 低レベル launcher は `Command::new("yoi")` 相当の process 起動と acceptance evidence 取得に責務を限定する。
- TUI/Panel、SpawnPod tool、Workspace backend はこの低レベル launcher を共有できるようにする。
### Worker runtime abstraction
- `LocalRuntimeBridge` 相当の read-only live view を、Worker 操作用の抽象境界へ整理する。
- trait 名は実装時に決めてよいが、概念としては `WorkerRuntime` / `WorkspaceWorkerRuntime` / `HostRuntime` のような境界を作る。
- 最低限、以下の責務を同じ abstraction から扱えるようにする。
- hosts list
- workers list
- worker detail / lookup
- worker spawn request
- worker stop request
- 将来の protocol method proxy / event stream の接続点
- v0 では local implementation のみでよい。
- 例: `LocalPodRuntime` / `LocalWorkspaceRuntime`
- 将来 Host protocol / remote worker backend を差し替えられるよう、Workspace API handler が concrete local implementation に密結合しすぎない形にする。
### Spawn operation abstraction
- Backend API から Worker を起動するための typed request / result を設計する。
- Browser から `workspace_root` / `cwd` / executable path / raw profile selector を自由入力させない。
- Frontend/API request は intent を中心にする。
- 例: generic worker profile launch、orchestrator start、ticket coder/reviewer start。
- ticket_id が必要な operation は path または typed field から受ける。
- Backend / resolver が以下を解決する。
- canonical workspace root
- process cwd
- pod name
- profile / manifest / project
- initial input / workflow
- optional launch policy
- role-session claim
- Orchestrator の dedicated worktree 起動では、runtime workspace identity と cwd を混同しない。
- `workspace_root` は original/main workspace
- `cwd` は orchestration worktree
- Coder / Reviewer / Orchestrator の Ticket-specific 起動経路は、低レベル Pod process launch config を生成する上位 resolver として扱う。
- Workspace backend が独自に `Command::new("yoi")` を組み立てて挙動を分岐させない。
### Ticket / Orchestration information boundary
- Ticket ID、Ticket role、Orchestration role は低レベル `PodProcessLaunchConfig` に入れない。
- Ticket/Orchestration resolver は以下を出力する。
- low-level Pod process launch config
- optional profile/launch policy
- role-session claim metadata
- model-visible initial input / workflow selection
- Role は Profile と同一視しない。
- Role は workflow 上の責務。
- Profile は Pod recipe。
- Role/Profile/Ticket の対応は上位 resolver の責務であり、低レベル launcher の責務ではない。
- Role を使わない generic Worker 起動では Ticket/Orchestration 情報を一切要求しない。
### API surface planning
- 実装する API はこの Ticket で確定しなくてもよいが、初期案として以下を検討する。
- generic: `POST /api/workers/spawn`
- operation: `POST /api/workers/{worker_id}/stop`
- orchestration: `POST /api/orchestrator/start`
- ticket-specific: `POST /api/tickets/{ticket_id}/workers`
- 起動 operation は HTTP command として扱う。
- 状態観測 / stream / transcript は後続 Ticket で SSE/WebSocket を検討する。
- spawn success は process start だけでなく、socket/connect/snapshot 等の acceptance evidence を持つこと。
### Role-session / Panel integration
- Backend spawn でも Panel が Worker を見つけられるようにする。
- 起動成功後に必要な local role-session claim を作る、または既存 helper を共有する。
- Runtime registry、Pod metadata、role-session registry のどれを authority / hint として扱うかを明確にする。
- stale/missing/already-running の扱いを typed error として返す。
### Safety / authority boundary
- Browser は local Unix socket path や runtime registry path を直接知らない。
- Backend が worker identity を解決し、対象 Worker が current Workspace から見えることを確認する。
- user permission model はこの Ticket の scope 外だが、将来挟める場所を backend abstraction に残す。
- Raw Pod protocol を browser から受ける場合でも、backend が最低限の method allow/block を挟める構造にする。
## Non-goals
- Worker operation UI の完成。
- WebSocket/SSE event stream の完成。
- Full permission / user auth model の設計。
- Remote Host protocol implementation。
- Pod protocol TypeScript 型生成の実装。
- Workspace identity `.yoi/workspace.toml` 実装。
- StopPod / registry locking の追加修正。
- Ticket/Orchestration domain を廃止すること。
- `--ticket-role` hidden CLI marker の即時削除。
## 受け入れ条件
この Ticket は planning から開始する。ready に進める前に以下を満たす。
- Workspace backend runtime abstraction の責務境界が明文化されている。
- `yoi pod [POD_OPTIONS]` process launch 境界と Ticket/Orchestration resolver 境界が分離されている。
- 低レベル Pod process launch config が Ticket ID / Ticket role / Orchestration role を直接持たない方針が明記されている。
- 既存 `SpawnConfig``ticket_role` をどう分離するかが決まっている。
- 現在の `LocalRuntimeBridge` をどう trait / service に置き換えるかが決まっている。
- Worker spawn request / result の typed shape が設計されている。
- Orchestrator / Coder / Reviewer の起動時に `workspace_root``cwd` をどう決めるかが明記されている。
- TUI/Panel、SpawnPod tool、Workspace backend が低レベル Pod process launcher を共有する方針が明記されている。
- Ticket/Orchestration resolver が低レベル launch config・launch policy・role-session claim を生成する方針が明記されている。
- Backend API endpoint の初期案が整理されている。
- role-session claim / Pod metadata / runtime registry との連携方針が明記されている。
- 実装まで含める場合は `cargo test -p yoi-workspace-server``cargo check -p yoi``cd web/workspace && deno task check && deno task build``git diff --check``nix build .#yoi --no-link` が通る。
+291
View File
@@ -0,0 +1,291 @@
<!-- event: create author: "yoi ticket" at: 2026-06-23T16:34:39Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: hare at: 2026-06-23T18:26:48Z -->
## Intake summary
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-23T18:26:48Z from: planning to: ready reason: cli_state field: state -->
## State changed
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: workspace-panel at: 2026-06-23T19:25:09Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-23T19:26:52Z -->
## Decision
Routing decision: implementation_ready
Reason:
- Dashboard Queue により人間が Orchestrator routing を許可した queued Ticket として確認した。
- Ticket body は Workspace backend の Worker runtime / Pod process launch / Ticket-Orchestration resolver の境界分離、typed spawn request/result、role-session 連携、安全境界、non-goals、validation を具体的に列挙している。
- `TicketRelationQuery` では blocking relation は 0 件、`TicketOrchestrationPlanQuery` では既存の ordering / blocker / conflict / accepted-plan 記録は 0 件だった。
- orchestration worktree は `## orchestration` で dirty changes なし、既存 implementation worktree は別 Ticket `00001KVSMJJNV-paused-ctrlx-cancel` と orchestration worktree のみで、この Ticket 用 worktree/branch はまだ無い。
- `crates/client/src/spawn.rs``crates/client/src/ticket_role.rs``crates/workspace-server/src/hosts.rs``crates/workspace-server/src/server.rs` を bounded に確認し、Ticket に書かれた現状認識と実装対象の code map が一致している。残る不確実性は local tactic / bounded investigation に収まる。
Evidence checked:
- Ticket body / thread: `item.md``thread.md`。thread は create、planning->ready、ready->queued のみで、未解決 blocker は記録されていない。
- related Ticket / orchestration plan records: relation 0 件、orchestration plan 0 件。
- code paths: `crates/client/src/spawn.rs``crates/client/src/ticket_role.rs``crates/workspace-server/src/hosts.rs``crates/workspace-server/src/server.rs`
- workspace state: `/home/hare/Projects/yoi/.worktree/orchestration``git status --short --branch` は clean な `## orchestration`。visible Pods は `yoi` peer と current `yoi-orchestrator` のみ。
IntentPacket:
Intent:
- Workspace backend から Worker spawn/stop/proxy を実装できる前段として、低レベル Pod process launch 境界と Worker runtime 境界、Ticket/Orchestration resolver 境界を分離し、既存 read-only `LocalRuntimeBridge` を操作境界へ拡張できる形に整理する。
Binding decisions / invariants:
- 低レベル Pod process launch layer は `yoi pod [POD_OPTIONS]` 起動だけを扱い、Ticket ID / Ticket role / Orchestration role を直接知らない。
- `SpawnConfig` をそのまま Workspace backend API 入力にしない。`ticket_role` は低レベル process config から分離する。
- Browser/API から `workspace_root` / `cwd` / executable path / raw profile selector を自由入力させない。backend/resolver が canonical workspace root、cwd、pod name、profile、initial input、workflow、launch policy、role-session claim を解決する。
- Orchestrator dedicated worktree 起動では runtime `workspace_root` と process `cwd` を混同しない。`workspace_root` は original/main workspace、`cwd` は orchestration worktree。
- Workspace backend が独自に `Command::new("yoi")` を組み立てて分岐する設計にしない。共有可能な低レベル launcher / config / acceptance evidence 境界を使う。
- この Ticket では Worker operation UI、SSE/WebSocket stream、full auth/permission、remote Host protocol、TypeScript protocol generation、Workspace identity `.yoi/workspace.toml`、StopPod/registry locking の追加修正は non-goal。
Requirements / acceptance criteria:
- Workspace backend runtime abstraction の責務境界を code/docs/tests で追える形にする。
- Pod process launch config と Ticket/Orchestration resolver 境界を分離し、低レベル config が Ticket/role/orchestration domain を持たないことを型・モジュール境界で示す。
- `LocalRuntimeBridge` 相当を Worker runtime trait/service として整理し、hosts/workers list、worker lookup、spawn/stop request、将来 proxy/stream 接続点を表現できるようにする。
- Worker spawn request/result の typed shape と acceptance evidence の扱いを導入する。
- role-session claim / Pod metadata / runtime registry の連携方針を実装境界または明文化された設計として残す。
- validation として少なくとも `cargo test -p yoi-workspace-server``cargo check -p yoi``cd web/workspace && deno task check && deno task build``git diff --check` を実施する。`nix build .#yoi --no-link` は変更量・依存/packaging 影響に応じて Orchestrator が最終判断する。
Implementation latitude:
- trait / struct 名は Ticket の例示に縛られず、既存 module organization に沿ってよい。
- `SpawnConfig` を rename/split するか、新規 `PodProcessLaunchConfig` / `PodLaunchOptions` を導入するかは coder が code map を見て選んでよい。
- API endpoint の完全実装ではなく、backend abstraction と typed request/result の初期 shape / handler 接続点を優先してよい。
- tests は既存 workspace-server/client test style に合わせて最小限でよい。
Escalate if:
- Browser から raw local path / raw executable / raw profile selector を受ける必要が出る。
- Ticket/Orchestration 情報を低レベル launcher に持たせないと実装できないと判断した場合。
- workspace_root / cwd / runtime identity / role-session authority の境界を変える必要が出る。
- public API endpoint shape をこの Ticket の初期案から大きく固定する必要が出る。
- existing Panel/role-session semantics を破壊する変更が必要になる。
Validation:
- `cargo test -p yoi-workspace-server`
- `cargo check -p yoi`
- `cd web/workspace && deno task check && deno task build`
- `git diff --check`
- 必要に応じて `nix build .#yoi --no-link`
Current code map:
- Primary: `crates/workspace-server/src/hosts.rs`, `crates/workspace-server/src/server.rs`, `crates/client/src/spawn.rs`, `crates/client/src/ticket_role.rs`
- Secondary if needed: workspace API/client integration, role-session helpers, Pod runtime command utilities, docs/resources prompts only when implementation requires。
- Avoid: unrelated UI completion, broad permission model, remote Host protocol, root/original workspace operations。
Critical risks / reviewer focus:
- Ticket/role/orchestration fields leaking into the low-level launcher.
- Workspace runtime abstraction becoming a UI-specific concrete local bridge instead of a backend boundary.
- Browser/API accepting authority-bearing raw paths or executable/profile selectors.
- `workspace_root` and `cwd` confusion for dedicated Orchestrator/Coder/Reviewer launches.
- acceptance evidence treated as mere process start.
- role-session claim / metadata / runtime registry authority and hint semantics left ambiguous.
Next action:
- `queued -> inprogress` を記録してから、worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVTNAY20-worker-runtime-spawn` を作成し、multi-agent-workflow で sibling coder/reviewer loop に進める。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-23T19:27:22Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked field: state -->
## State changed
Orchestrator acceptance: queued -> inprogress
- 直前確認で `TicketShow` は state `queued`、blocking relation は 0 件、accepted plan は `orch-plan-20260623-192711-1` のみだった。
- routing decision と IntentPacket は Ticket thread に記録済み。
- orchestration worktree は clean な `## orchestration` で、この Ticket 用 implementation worktree/branch はまだ作成していない。
- これ以降に worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVTNAY20-worker-runtime-spawn` を作成し、multi-agent-workflow に接続する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T19:30:10Z -->
## Implementation report
Implementation orchestration update:
- worktree-workflow に従い、implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVTNAY20-worker-runtime-spawn` と branch `work/00001KVTNAY20-worker-runtime-spawn` を作成した。
- child worktree では sparse-checkout を設定し、tracked `.yoi` project records は見える一方で `.yoi/memory`、local/runtime/log/lock/secret-like `.yoi` paths を除外する方針にした。
- Coder Pod `yoi-coder-00001KVTNAY20-worker-runtime` を sibling として起動した。write scope は child worktree に限定し、runtime workspace root `/home/hare/Projects/yoi` は startup 要件を満たすため non-recursive read のみにした。Coder には root/original workspace と orchestration worktree で read/write/validation/git operation を行わないよう明示した。
- 初回 spawn は workspace root read scope が不足して失敗したため、implementation side effect は残さず scope を修正して再試行した。現在 reachable child Pod として起動済み。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T19:31:58Z -->
## Implementation report
Implementation orchestration recovery:
- `yoi-coder-00001KVTNAY20-worker-runtime` は provider API error `Unsupported content type` で初回実行・再送とも assistant output を生成できなかった。
- child worktree `/home/hare/Projects/yoi/.worktree/00001KVTNAY20-worker-runtime-spawn` は実装差分なしで、last commit は routing acceptance commit `5a8bcebd ticket: accept worker runtime spawn task` のままだった。
- 失敗 Pod は停止して scope を回収した。
- 実装 side effect の回復として、同じ child worktree/branch を使い、別 profile で Coder role を再起動する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-23T19:33:48Z -->
## Implementation report
Implementation orchestration blocked:
- Coder sibling execution is blocked by runtime/provider failures, not by Ticket requirements or repository state.
- `builtin:coder` Pod `yoi-coder-00001KVTNAY20-worker-runtime` started, but both initial run and retry failed before assistant output with provider API error: `Unsupported content type`.
- That Pod was stopped and scope reclaimed. child worktree status remained clean with no implementation diff.
- Recovery attempts using `inherit` (`yoi-coder-00001KVTNAY20-worker-runtime-2`), `builtin:default` (`...-3`), and `builtin:companion` (`...-4`) did not produce sockets within 10s.
- Worktree `/home/hare/Projects/yoi/.worktree/00001KVTNAY20-worker-runtime-spawn` and branch `work/00001KVTNAY20-worker-runtime-spawn` are prepared and clean at orchestration commit `b8388655`.
- `orch-plan-20260623-193340-2` に waiting-capacity note を残した。
Next action:
- Coder Pod/profile/provider startup issue の解消後に同じ worktree/branch で multi-agent workflow を再開する。
- あるいは人間が明示的に Orchestrator direct implementation を許可する場合のみ、Orchestrator がこの child worktree で実装へ進む。
---
<!-- event: implementation_report author: hare at: 2026-06-24T10:26:04Z -->
## Implementation report
# 実装報告: 00001KVTNAY20
## 変更概要
- `client::spawn``PodProcessLaunchConfig``PodProcessLaunchOptions` を導入し、低レベルの Pod プロセス起動設定から Ticket role marker を分離した。
- Ticket role 起動は `TicketRoleLaunchPlan::spawn_options()` 経由で hidden CLI marker を渡す形にし、`TicketRoleLaunchResult` に Run 受理証跡 (`TicketRoleLaunchAcceptanceEvidence`) を追加した。
- Workspace server の `LocalRuntimeBridge``WorkspaceWorkerRuntime` trait の実装として整理し、hosts/workers 一覧、worker lookup、spawn/stop typed request/result、将来の proxy/stream 接続点を型として追加した。
- Workspace 側の spawn request shape は policy intent ベースにし、browser/API caller から raw `workspace_root` / `cwd` / executable path / raw profile selector を受け取らない形にした。
- Dashboard/TUI 側の直接 spawn 呼び出しを新しい low-level config/options 分離に追従した。
## 変更ファイル
- `crates/client/src/lib.rs`
- `crates/client/src/spawn.rs`
- `crates/client/src/ticket_role.rs`
- `crates/tui/src/dashboard/mod.rs`
- `crates/tui/src/spawn.rs`
- `crates/workspace-server/src/hosts.rs`
- `crates/workspace-server/src/server.rs`
## 検証結果
- `cargo test -p yoi-workspace-server`: 成功
- `cargo check -p yoi`: 成功
- `cd web/workspace && deno task check && deno task build`: 成功
- `cargo test -p client`: 成功(追加確認)
- `git diff --check`: 成功
## コミット
- 実装コミット: `217a4828d73ab553b5406cc7e22e43b1ec7be48e`
## 残リスク / 非ゴールとして残したもの
- `WorkspaceWorkerRuntime::spawn_worker` / `stop_worker` は typed boundary と request/result を用意した段階で、実際の Worker operation UI 完成、stream proxy、remote Host protocol、認可/権限、registry locking までは実装していない。
- low-level launcher は trusted in-process resolver からの追加 CLI args を受け取れるが、Ticket role などのドメイン概念は `PodProcessLaunchConfig` からは除外している。
- TS 型生成やフロントエンド API surface の追加は本 Ticket の非ゴールとして変更していない。
---
<!-- event: review author: yoi-reviewer-00001KVTNAY20-worker-runtime at: 2026-06-24T10:33:54Z status: approve -->
## Review: approve
Review result: approve
Read-only で Ticket `00001KVTNAY20`、implementation report、`a729d686..HEAD` の差分を確認した。ファイル変更・Ticket review 記録・merge/cleanup は reviewer では行っていない。
確認した主な点:
- 低レベル Pod launcher は `crates/client/src/spawn.rs``PodProcessLaunchConfig` / options に分離され、Ticket/role/orchestration 固有の型や field は入っていない。`--workspace` と process `cwd` も別 field のまま扱われている。
- Ticket role 起動は `crates/client/src/ticket_role.rs` 側に寄せられ、role/profile/workflow/prompt/初回 `Run` の組み立ては launcher ではなく role 層で処理されている。
- 起動成功は単なる process start ではなく、既存 socket readiness に加えて Ticket role launch では `Method::Run` 送信後の `UserMessage` / `InvokeStart(UserSend)` / `TurnStart` を待つ形になっている。
- Workspace server 側は `WorkspaceWorkerRuntime` trait と `WorkerSpawnRequest` / `WorkerStopRequest` / `WorkerProtocolProxyRequest` を導入し、現在の local 実装は Pod metadata read boundary + unsupported lifecycle operation として明示されている。Browser/API request shape に `workspace_root` / `cwd` / executable path / raw profile selector は受けていない。
- Dedicated Orchestrator launch では runtime `workspace_root` と process `cwd` を混同しないテストが追加されており、original workspace と orchestration checkout が分離されている。
- 非ゴールである実 scheduler、E2E、merge/cleanup automation 等の scope creep は入っていない。
Validation evidence:
- reviewer は read-only 制約のため build/test は再実行していない。
- 実装報告の検証結果として `cargo test -p yoi-workspace-server``cargo check -p yoi``cd web/workspace && deno task check && deno task build``cargo test -p client``git diff --check` の成功を確認した。
- reviewer 側では read-only な `git diff --check a729d686..HEAD` を実行し、問題なし。
Non-blocking notes:
- `WorkerSpawnRequest` はまだ実 endpoint 未接続かつ local runtime では `Unsupported` なのでこの Ticket では問題ない。今後実装時は `requested_worker_name` を backend 側で厳格に validate し、Browser 由来の値を Pod authority として扱わないことを明示すると安全。
- `launch_ticket_role_pod` の acceptance 待ちは fresh spawn 前提では妥当。将来既存 Pod restore/競合を扱う場合は、connect-time `Snapshot`/`Alert` の drain と「送信した Run に対応する evidence」の対応付けをより強くするとよい。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T10:34:55Z -->
## Implementation report
Integration outcome:
- Coder retry `yoi-coder-00001KVTNAY20-worker-runtime-retry1` が完走し、implementation branch `work/00001KVTNAY20-worker-runtime-spawn` に実装を commit した。
- Reviewer `yoi-reviewer-00001KVTNAY20-worker-runtime` は read-only review で `approve`。低レベル launcher と Ticket/role/orchestration resolver の分離、Workspace runtime abstraction、raw authority input 非受理、`workspace_root`/`cwd` 分離、acceptance evidence、scope creep なしを確認した。
- Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration` で branch `work/00001KVTNAY20-worker-runtime-spawn` を merge 済み。
- merge commit: `97555bb5 merge: 00001KVTNAY20 worker runtime spawn`
- implementation commit: `217a4828 feat: abstract worker runtime spawn boundary`
- implementation report commit: `d62ab6e1 docs: record worker runtime implementation report`
Validation in Orchestrator worktree:
- `cargo test -p yoi-workspace-server`: success
- `cargo check -p yoi`: success
- `cd web/workspace && deno task check`: success
- `cd web/workspace && deno task build`: success
- `git diff --check`: success
Notes:
- Reviewer non-blocking note: future actual spawn endpoint should validate any requested worker name server-side and avoid treating Browser-provided values as Pod authority.
- Reviewer non-blocking note: future restore/competition handling may need stronger association between a sent `Run` and acceptance evidence after connect-time `Snapshot`/`Alert` drain.
Next action:
- Mark Ticket done after this integration/validation evidence.
- Then stop related child Pods and remove only the child implementation worktree/branch.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T10:35:01Z from: inprogress to: done reason: merged_validated field: state -->
## State changed
Implementation is merged into the orchestration branch and validated.
Evidence:
- merge commit: `97555bb5 merge: 00001KVTNAY20 worker runtime spawn`
- reviewer result: approve
- validation in `/home/hare/Projects/yoi/.worktree/orchestration` succeeded:
- `cargo test -p yoi-workspace-server`
- `cargo check -p yoi`
- `cd web/workspace && deno task check`
- `cd web/workspace && deno task build`
- `git diff --check`
Closure is not performed here; this state records implementation completion after merge/validation.
---
+90
View File
@@ -0,0 +1,90 @@
---
title: 'Planning Ticket API and UI without queue operations'
state: 'planning'
created_at: '2026-06-23T19:41:51Z'
updated_at: '2026-06-23T19:41:51Z'
assignee: null
---
## 背景
Ticket 管理 UI を作るにあたり、最初から DB migration や Queue 操作まで含めると authority 境界が大きくなりすぎる。現行の Ticket authority は `.yoi/tickets/<ticket-id>/` の flat file backend であり、Workspace server 側にも Ticket 用 SQLite schema の器はあるが、現時点では authority ではない。
この Ticket では migration を行わず、まず Workspace backend に Ticket 操作用 API を整備し、その API を操作する最小 UI を実装する。対象は Planning Ticket の作成・確認に限定し、`ready -> queued` の Queue 操作や Orchestrator 起動には踏み込まない。
## 目的
- Workspace web から Ticket を確認し、Planning 状態の Ticket を作成できるようにする。
- UI は backend API を通じて Ticket を操作し、frontend が `.yoi/tickets` の内部ファイル構造や authority path に依存しないようにする。
- 将来 DB authority / migration に進む余地を残しつつ、この Ticket では現行 file backend を正として扱う。
## 要件
### Backend API
- Workspace server に Ticket 管理用 API を追加または整理する。
- v0 では現行 `LocalTicketBackend` / project record reader を authority として使い、DB migration はしない。
- API は少なくとも以下を扱う。
- Ticket 一覧取得
- Ticket 詳細取得
- Planning 状態の Ticket 作成
- 作成 API は title と本文/背景/受け入れ条件などの必要最小限の入力を受け、canonical Ticket ID を返す。
- frontend から raw `.yoi/tickets` path や内部 artifact path を直接指定させない。
- Ticket ID は canonical ID のみを扱い、title slug や legacy alias を API contract にしない。
- API の error は typed response として扱う。
- invalid input
- duplicate / conflict
- backend unavailable
- workspace not found / outside current workspace
### UI
- Workspace web に Ticket 管理画面または Ticket 作成導線を追加する。
- v0 UI は Planning Ticket の作成までに限定する。
- 作成フォームは最低限以下を扱う。
- title
- 背景 / 要件の本文
- 受け入れ条件
- 作成後は作成された canonical Ticket ID と状態を表示し、詳細画面または一覧へ反映する。
- UI は API response を authority とし、frontend 側で Ticket file layout を再実装しない。
### Scope boundary
- Queue 操作は実装しない。
- `ready -> queued`
- Panel / Workspace UI からの Queue button
- Orchestrator / Coder / Reviewer 起動
- role-session claim 作成
- Ticket DB migration は実装しない。
- 既存 `.yoi/tickets` authority を変更しない。
- 既存 Ticket CLI / Pod tool の挙動を壊さない。
### Safety / authority
- Browser は local path / runtime path / socket path を直接 authority として渡さない。
- Workspace backend が current workspace の Ticket backend root を解決する。
- Ticket 作成は current workspace の `.yoi/tickets` に限定する。
- 将来 permission model を挟めるよう、API handler と backend operation の境界を分ける。
## Non-goals
- Ticket storage の DB migration。
- SQLite `tickets` schema を authority に昇格すること。
- Queue / ready / inprogress / close などの lifecycle mutation UI。
- Orchestrator 起動、Worker spawn、role-session claim 連携。
- Ticket relation / artifact / orchestration-plan 編集 UI。
- SSE/WebSocket による live Ticket update。
## 受け入れ条件
- Workspace server に Ticket list/detail/create planning 用 API がある。
- Planning Ticket 作成 API が canonical Ticket ID を返す。
- 作成された Ticket は既存 `yoi ticket list/show` から確認できる。
- Workspace web から Planning Ticket を作成できる。
- UI から Queue 操作はできない。
- DB migration や SQLite Ticket authority 化を行っていない。
- `cargo test -p yoi-workspace-server` が通る。
- `cargo check -p yoi` が通る。
- `cd web/workspace && deno task check && deno task build` が通る。
- `git diff --check` が通る。
- `nix build .#yoi --no-link` が通る。
+7
View File
@@ -0,0 +1,7 @@
<!-- event: create author: "yoi ticket" at: 2026-06-23T19:41:51Z -->
## 作成
LocalTicketBackend によって作成されました。
---
@@ -0,0 +1,3 @@
{"id":"orch-plan-20260624-092429-1","ticket_id":"00001KVWECEQG","kind":"do_not_parallelize","related_ticket":"00001KVTNAY20","note":"`00001KVWECEQG` と inprogress `00001KVTNAY20` はどちらも `LocalRuntimeBridge` / Workspace Worker runtime abstraction / runtime identity・cwd 境界を扱い、primary code surface が `crates/workspace-server/src/hosts.rs` と API handler 層で重なる。`00001KVTNAY20` は Coder runtime/provider blocker で実装未開始のまま child worktree を保持しているため、並列実装すると registry abstraction と spawn abstraction の境界を二重に固定する conflict risk が高い。先に `00001KVTNAY20` の blocker 解消または明示的な ordering/design decision が必要。","author":"yoi-orchestrator","at":"2026-06-24T09:24:29Z"}
{"id":"orch-plan-20260624-092435-2","ticket_id":"00001KVWECEQG","kind":"waiting_capacity_note","note":"Queued routing was authorized, and the Ticket appears implementation-ready after bounded evidence checks, but current Coder sibling capacity is unavailable: prior inprogress Ticket `00001KVTNAY20` already attempted multi-agent startup and hit provider/startup failures (`Unsupported content type`, alternate profiles no socket). Because this Ticket overlaps the same Worker runtime abstraction surface and no Coder/Reviewer loop can currently run safely, leave `00001KVWECEQG` queued rather than accepting `queued -> inprogress`. Resume when Coder runtime is healthy or when a human explicitly authorizes Orchestrator direct implementation/order.","author":"yoi-orchestrator","at":"2026-06-24T09:24:35Z"}
{"id":"orch-plan-20260624-103655-3","ticket_id":"00001KVWECEQG","kind":"accepted_plan","accepted_plan":{"summary":"`00001KVTNAY20` は done/merged/validated になったため、以前の do_not_parallelize/waiting-capacity blocker は解消済みとして扱う。`00001KVWECEQG` は dedicated worktree `/home/hare/Projects/yoi/.worktree/00001KVWECEQG-worker-runtime-registry` と branch `work/00001KVWECEQG-worker-runtime-registry` で Worker runtime registry / capability / overview abstraction を実装する。root/original workspace では git/read/write/validate/merge/cleanup を行わない。","branch":"work/00001KVWECEQG-worker-runtime-registry","worktree":"/home/hare/Projects/yoi/.worktree/00001KVWECEQG-worker-runtime-registry","role_plan":"Orchestrator: routing acceptance, worktree creation, final integration/validation/cleanup. Coder: implementation in dedicated child worktree with narrow write scope, using current `00001KVTNAY20` result as base. Reviewer: read-only review against recorded IntentPacket, diff, and validation evidence."},"author":"yoi-orchestrator","at":"2026-06-24T10:36:55Z"}
+162
View File
@@ -0,0 +1,162 @@
---
title: 'Abstract Worker runtime registry and overview reporting'
state: 'done'
created_at: '2026-06-24T09:11:38Z'
updated_at: '2026-06-24T11:15:13Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-24T09:22:55Z'
---
## 背景
Workspace backend には現状 `LocalRuntimeBridge` があり、local Pod metadata から `HostSummary` / `WorkerSummary` を返す read-only live view として動いている。しかしこれは concrete local 実装に近く、将来の Backend internal runtime、local Pod runtime、remote / multi-machine runtime を同じ control plane から扱う抽象境界にはまだなっていない。
今後の Ticket / Worker 管理 UI では、Backend が Worker を直接 `yoi pod` や local metadata path として扱うのではなく、複数の Worker runtime を束ねる control plane になる必要がある。Companion / Intake / routing-only Orchestrator のような filesystem を必須としない Worker は Backend internal runtime で動かせるべきであり、Coder / Reviewer / build-heavy Worker は local / remote の filesystem-capable runtime に配置できるべきである。
また、Worker session を Backend が raw log として全量・恒久的に収集する設計にはしない。raw session / provider trace / detailed event stream は execution runtime 側の prune 可能な debug/source log とし、Backend は Worker run overview、usage aggregate、lifecycle event、Ticket / role / runtime link、artifact reference、failure summary のような後から使える durable projection を持つ。
この Ticket では、既存 `LocalRuntimeBridge` をレベル上げして、Workspace backend の Worker runtime registry / runtime abstraction として整理する。
## 目的
- Workspace backend が複数の Worker runtime を扱える抽象境界を持つ。
- 既存 local Pod metadata read-only bridge を `LocalPodRuntime` 相当の runtime 実装として位置付ける。
- Backend が runtime registry を持ち、API handler が concrete local bridge に直接依存しないようにする。
- raw session を Backend authority にせず、overview / metrics / durable outcome を中心に扱う方針を型と API 境界に反映する。
- 後続の spawn / stop / protocol proxy / remote runtime / internal runtime 実装に進める基盤を作る。
## 要件
### Worker runtime abstraction
- `LocalRuntimeBridge` をそのまま API handler から直接使う構造をやめ、Worker runtime trait / service 境界を導入する。
- 概念名は実装時に決めてよいが、`WorkerRuntime` / `WorkspaceWorkerRuntime` / `RuntimeRegistry` / `WorkerRuntimeRegistry` のような責務が明確な名前にする。
- v0 では少なくとも以下の read operations を抽象化する。
- runtime / host list
- runtime / host detail
- worker list
- worker detail / lookup
- 後続 operation の接続点を型として残す。
- spawn worker
- stop worker
- send input / interrupt / compact などの worker command routing
- bounded transcript / debug session read
- event stream / run overview stream
- v0 で未実装の operation は unsupported として typed error を返せるようにする。
### Runtime registry
- Workspace backend は単一の concrete `LocalRuntimeBridge` ではなく、runtime registry 経由で Worker / Host を取得する。
- registry は複数 runtime を保持できる構造にする。
- backend internal runtime
- local Pod runtime
- future remote runtime
- v0 では local Pod runtime のみ登録してよい。
- runtime_id / host_id / worker_id は API contract 上 opaque id として扱う。
- `pod_name` は local Pod runtime の implementation detail / display hint とし、外部操作の主キーにはしない。
- API handler は runtime_id / worker_id を registry に解決させ、raw local path / socket path / metadata path を browser に出さない。
### Runtime capability model
- Runtime / Host detail は capability summary を返す。
- 少なくとも以下のような区別ができること。
- can_list_workers
- can_get_worker
- can_spawn_worker
- can_stop_worker
- can_accept_input
- can_stream_events
- can_read_bounded_transcript / debug session
- has_workspace_fs
- has_shell
- has_git
- supports_worktrees
- supports_backend_internal_tools / ticket tools
- Companion / Intake / routing-only Orchestrator は filesystem なし runtime でも動かせる、Coder / Reviewer は filesystem-capable runtime が必要、という placement 判断を後続で表現できる shape にする。
### Worker identity / visibility
- Worker identity は runtime scoped に扱う。
- runtime_id
- worker_id
- optional active run id
- optional active session id / segment id は implementation detail または debug field
- Workspace UI の通常表示では current workspace から見える Worker だけを返す。
- local Pod runtime では `PodMetadata.workspace_root` を current workspace root と比較して visibility を決める。
- `cwd` と runtime workspace identity を混同しない。
- dedicated Orchestrator worktree があっても、runtime workspace identity は original/main workspace root である。
- workspace root が無い / 不一致 / metadata invalid な Worker は diagnostics または debug scope として扱い、通常 UI の authority を歪めない。
### Session / overview boundary
- Backend は raw session log を恒久 authority として持たない。
- raw session / provider trace / verbose event stream は execution runtime 側に残し、runtime retention / prune 対象とする。
- Backend が durable に持つものは以下を中心にする。
- Worker run overview
- lifecycle event
- usage aggregate
- Ticket / role / runtime / worker link
- durable outcome summary
- failure summary
- artifact / report reference
- Runtime interface は必要に応じて bounded transcript / debug session tail を読める接続点を持つが、これは debug / live UI 用であり Backend DB の durable authority ではない。
- usage dashboard は raw session ingest ではなく、runtime からの usage event / aggregate を元に構築できる形にする。
### Backend internal runtime planning
- Backend internal runtime はこの Ticket で完全実装しなくてよいが、registry / capability / identity model がそれを表現できること。
- Backend internal runtime は filesystem tool を前提にしない Worker 用とする。
- Companion
- Intake
- Ticket planning / routing assistant
- Dashboard assistant
- Backend internal Worker は Ticket / Workspace API を tool として使う形を想定し、raw local filesystem authority を要求しない。
### Local Pod runtime migration
- 既存 `LocalRuntimeBridge` の read-only local metadata 実装を `LocalPodRuntime` 相当へ移す、または薄い adapter として registry に登録する。
- 既存 API の挙動を壊さない。
- `GET /api/hosts`
- `GET /api/workers`
- `GET /api/hosts/{host_id}/workers`
- 追加可能であれば worker detail endpoint を追加する。
- `GET /api/workers/{worker_id}`
- `GET /api/hosts/{host_id}/workers/{worker_id}`
- local Pod metadata の raw content / snapshot content / socket path は API response に出さない。
### API / error boundary
- API response は runtime registry 経由の typed response にする。
- unknown runtime / host / worker は typed error として返す。
- runtime unavailable、operation unsupported、worker not visible、invalid query は区別できるようにする。
- Browser は local Unix socket path、runtime registry file path、Pod metadata path、raw session path を authority として渡さない。
## Non-goals
- Worker spawn / stop の本実装。
- Remote runtime protocol の本実装。
- Backend internal runtime で実際に LLM Worker を起動すること。
- raw session 全量の Backend DB ingest。
- Session log schema migration。
- Ticket storage migration。
- Worker operation UI の完成。
- SSE/WebSocket event stream の完成。
- Permission / auth model の完成。
## 受け入れ条件
この Ticket は planning から開始する。ready に進める前に以下を満たす。
- Worker runtime / runtime registry / local Pod runtime の責務境界が明文化されている。
- 既存 `LocalRuntimeBridge` をどう level-up / rename / adapter 化するかが決まっている。
- Backend が複数 runtime を保持できる registry 構造の方針が決まっている。
- runtime_id / host_id / worker_id の identity 方針が決まっている。
- local Pod `pod_name` を external operation key にしない方針が明記されている。
- Runtime capability model の最小 field が整理されている。
- current workspace visibility と workspace_root / cwd の扱いが明記されている。
- raw session を Backend durable authority にしない方針が明記されている。
- Backend に残す Worker run overview / usage aggregate / lifecycle projection の方針が明記されている。
- bounded transcript / debug session read は runtime-local optional operation とする方針が明記されている。
- 既存 `/api/hosts` / `/api/workers` を registry 経由へ移す方針が明記されている。
- 実装まで含める場合は `cargo test -p yoi-workspace-server``cargo check -p yoi``git diff --check``nix build .#yoi --no-link` が通る。
+377
View File
@@ -0,0 +1,377 @@
<!-- event: create author: "yoi ticket" at: 2026-06-24T09:11:38Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: hare at: 2026-06-24T09:21:22Z -->
## Intake summary
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-24T09:21:22Z from: planning to: ready reason: cli_state field: state -->
## State changed
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: workspace-panel at: 2026-06-24T09:22:55Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-24T09:24:52Z -->
## Decision
Routing decision: blocked_by_dependency_or_missing_authority
Reason:
- Dashboard Queue により人間が Orchestrator routing を許可した queued Ticket として確認した。
- Ticket body は Worker runtime registry / overview reporting / runtime capability / identity / visibility / raw-session boundary / local Pod runtime migration を具体的に列挙しており、実装意図と受け入れ条件は implementation-ready に近い。
- `TicketRelationQuery` では blocking relation は 0 件、初回 `TicketOrchestrationPlanQuery` では既存 plan record は 0 件だった。
- ただし現在 `00001KVTNAY20` (`Abstract Workspace Worker runtime spawn operations`) が `inprogress` で、同じ `LocalRuntimeBridge` / Workspace Worker runtime abstraction / runtime identity・cwd 境界と `crates/workspace-server/src/hosts.rs` 周辺を扱っている。
- `00001KVTNAY20` は multi-agent startup が provider/runtime error で停止しており、implementation worktree は clean だが Coder sibling capacity が実質利用できない状態である。
- この Ticket を今 `queued -> inprogress` にすると、同じ runtime abstraction の registry 側と spawn 側を別 branch で同時に固定して conflict risk が高い。さらに Coder/Reviewer loop を起動できないため、acceptance 後に進められない。
Evidence checked:
- Ticket body / thread: `00001KVWECEQG` の item/thread。thread は create、planning->ready、ready->queued のみで未解決 planning question は記録されていない。
- Relations / orchestration plan: `00001KVWECEQG` は relation 0 件、既存 plan 0 件。routing 中に `orch-plan-20260624-092429-1` (`do_not_parallelize` with `00001KVTNAY20`) と `orch-plan-20260624-092435-2` (waiting capacity note) を記録した。
- Related Ticket: `00001KVTNAY20``inprogress`、accepted plan と waiting-capacity note があり、Coder Pod startup/provider failure により実装未開始。
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean。既存 implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVTNAY20-worker-runtime-spawn` は clean at `a729d686`
- Visible Pods: `yoi`, current `yoi-orchestrator`, and stopped/restorable failed `yoi-coder-00001KVTNAY20-worker-runtime` metadata only; active usable Coder/Reviewer sibling は無い。
Decision:
- `00001KVWECEQG` は planning へ戻さない。Ticket の missing decision / missing information は現時点で具体化していない。
- ただし `queued -> inprogress` acceptance は行わず、queued のまま待機させる。
- Blocker は Ticket 要件ではなく、(1) `00001KVTNAY20` との runtime abstraction/code-surface conflict、(2) Coder sibling runtime/provider capacity failure。
Next action:
- 先に `00001KVTNAY20` の Coder runtime/provider blocker を解消して multi-agent workflow を再開する、または人間が Orchestrator direct implementation / ordering を明示的に許可する。
- その後、この Ticket を再 routing し、必要なら `00001KVTNAY20` の実装結果に合わせて registry abstraction の IntentPacket を作る。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-24T10:37:25Z -->
## Decision
Routing decision: implementation_ready
Reason:
- ユーザーから「進めて」と明示 follow-up があり、以前 `queued` のまま待機させた blocker を再確認した。
- Ticket body は Worker runtime registry / overview reporting / runtime capability / identity / visibility / raw-session boundary / local Pod runtime migration を具体的に列挙しており、observable な acceptance criteria と reviewer focus がある。
- `TicketRelationQuery` は blocking relation 0 件。
- 以前の `do_not_parallelize` / waiting-capacity note は `00001KVTNAY20``done` になり、merge/validation 済みになったことで解消済みとして扱える。
- 現在の `crates/workspace-server/src/hosts.rs` には `00001KVTNAY20` の結果として `WorkspaceWorkerRuntime`、typed spawn/stop/proxy request/result、local Pod metadata overview が入り、今回の registry/capability/overview abstraction をその上に重ねられる。残る不確実性は local tactic / bounded investigation に収まる。
- orchestration worktree は clean で、同一 Ticket 用 worktree/branch はまだ無い。
Evidence checked:
- Ticket body / thread: `00001KVWECEQG` の item/thread。前回 routing decision 以降の missing planning question はなし。
- Relations / orchestration plan: relation 0 件。既存 plan は前回の `do_not_parallelize` / waiting-capacity と、今回記録した accepted plan `orch-plan-20260624-103655-3`
- Related Ticket: `00001KVTNAY20``done`。review approve、orchestration merge、validation success、child cleanup 済み。
- Code map: `crates/workspace-server/src/hosts.rs`, `crates/workspace-server/src/server.rs` を current orchestration branch で確認。
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean。
IntentPacket:
Intent:
- Workspace backend の Worker runtime surface を、single local bridge から registry / host / capability / overview reporting の境界へ抽象化し、Dashboard/API が runtime 一覧・worker 一覧・capability/diagnostic を安全に表示できる基盤を作る。
Binding decisions / invariants:
- registry/overview は local Pod metadata と role-session/runtime metadata の安全な summary を扱う。raw session contents や unchecked private/runtime paths を API response に出さない。
- Browser/API 由来の値を runtime authority として扱わない。worker/host id は backend が解決・検証した bounded identifier として扱う。
- runtime identity / workspace_root / process cwd / role-session claim は混同しない。
- `00001KVTNAY20` で入った low-level launch boundary と Ticket/role/orchestration resolver separation を崩さない。
- remote Host protocol、full scheduler、operation UI、stream/proxy 実装、permission/auth の完成、raw session inspection は non-goal。
Requirements / acceptance criteria:
- Worker runtime registry/service abstraction が、複数 runtime/host summary と capability/diagnostic reporting を表現できる。
- `LocalRuntimeBridge` 由来の local runtime が registry の一実装/entry として扱われ、API handler が concrete bridge に直接密結合しすぎない。
- overview response は bounded/safe fields のみで、local Pod metadata failure や unavailable capability を diagnostic として返せる。
- existing `/api/hosts` / `/api/workers` semantics を維持または明確に migration し、UI が使う shape を壊す場合は対応も含める。
- validation として少なくとも `cargo test -p yoi-workspace-server`, `cargo check -p yoi`, `cd web/workspace && deno task check && deno task build`, `git diff --check` を実施する。
Implementation latitude:
- registry trait/struct 名、module split、capability enum/string shape、diagnostic aggregation の詳細は coder が既存 code style に合わせて決めてよい。
- API の大規模追加ではなく、current endpoints の内部境界整理と typed overview/capability shape の初期導入を優先してよい。
- UI 変更は必要最小限でよい。既存 web build/typecheck を通すための追随は行ってよい。
Escalate if:
- Browser/API から raw path/session/executable/profile 等を受けないと実装できない。
- runtime registry を scheduler/lease/remote Host protocol として固定する必要が出る。
- `00001KVTNAY20` の launch boundary を戻す/破る必要が出る。
- raw session content や private metadata を overview に出す必要がある。
Validation:
- `cargo test -p yoi-workspace-server`
- `cargo check -p yoi`
- `cd web/workspace && deno task check && deno task build`
- `git diff --check`
- 影響範囲に応じて追加 Rust tests。
Current code map:
- Primary: `crates/workspace-server/src/hosts.rs`, `crates/workspace-server/src/server.rs`
- Secondary: Workspace web API consumers under `web/workspace`, role-session/local metadata helpers if needed, `crates/client/src/spawn.rs` / `crates/client/src/ticket_role.rs` only to preserve boundaries.
- Avoid: root/original workspace operations, unrelated Panel UI completion, remote Host protocol, scheduler/queue implementation, raw session reading.
Critical risks / reviewer focus:
- registry abstraction becoming a scheduler or authority layer by accident.
- leaking raw session/private paths/unchecked metadata into overview responses.
- host/worker ids derived from user-controlled strings without bounding/validation.
- breaking the launch boundary from `00001KVTNAY20`.
- conflating runtime workspace root, process cwd, role-session claim, and local metadata hints.
Next action:
- `queued -> inprogress` を記録してから、worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVWECEQG-worker-runtime-registry` を作成し、multi-agent-workflow で Coder/Reviewer sibling loop に進める。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T10:37:43Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked field: state -->
## State changed
Orchestrator acceptance: queued -> inprogress
- 直前確認で `TicketShow` は state `queued`、blocking relation は 0 件。
- 以前の blocker だった `00001KVTNAY20``done` になり、orchestration branch に merge/validated 済み。
- `TicketOrchestrationPlanQuery` では過去の do_not_parallelize / waiting-capacity note と、今回の accepted plan `orch-plan-20260624-103655-3` を確認した。過去 blocker は解消済みとして扱う。
- routing decision と IntentPacket は Ticket thread に記録済み。
- これ以降に worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVWECEQG-worker-runtime-registry` を作成し、multi-agent-workflow に接続する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T10:38:50Z -->
## Implementation report
Implementation orchestration update:
- worktree-workflow に従い、implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVWECEQG-worker-runtime-registry` と branch `work/00001KVWECEQG-worker-runtime-registry` を作成した。
- child worktree では tracked `.yoi` project records を見える状態にし、`.yoi/memory`、local/runtime/log/lock/secret-like `.yoi` paths を sparse-checkout で除外する方針にした。
- Coder Pod `yoi-coder-00001KVWECEQG-worker-registry` を sibling として起動した。write scope は child worktree に限定し、runtime workspace root `/home/hare/Projects/yoi` は startup 要件を満たすため non-recursive read のみにした。
- Coder には root/original workspace と orchestration worktree で read/write/validation/git operation を行わないよう明示した。
---
<!-- event: implementation_report author: yoi-coder-00001KVWECEQG-worker-registry at: 2026-06-24T10:57:04Z -->
## Implementation report
Implementation report:
実装概要:
- `crates/workspace-server/src/hosts.rs`
- `WorkerRuntimeRegistry` を追加。
- `WorkspaceWorkerRuntime` trait を runtime entry の抽象境界として再整理。
- 既存 local Pod metadata 読み取りを `LocalPodRuntime` adapter として registry に登録。
- `RuntimeSummary`, `RuntimeCapabilitySummary`, `WorkerCapabilitySummary`, `WorkerWorkspaceSummary`, typed `RuntimeDiagnostic` を追加。
- API caller 由来の `host_id` / `worker_id` を backend 側で bounded identifier として検証。
- raw session contents / socket path / metadata file path / raw workspace path を summary に出さないよう整理。
- metadata の `workspace_root` は runtime workspace identity との照合にだけ使い、API には `current_workspace` summary として出す。
- 他 workspace / workspace_root missing の Pod metadata は通常 worker overview から除外し、typed diagnostic のみ返す。
- 先行 Ticket `00001KVTNAY20` の low-level launch 境界用 typed request/intent/result shape は維持。
- `crates/workspace-server/src/server.rs`
- server state を `Arc<WorkerRuntimeRegistry>` に変更。
- `/api/hosts`, `/api/workers`, `/api/hosts/{host_id}/workers` を registry 経由に変更。
- `/api/runtimes` を追加し、runtime overview を返せるようにした。
- invalid runtime identifier / unknown worker / unsupported capability の API error mapping を追加。
- server tests を registry source / runtime overview / safe summary 仕様に更新。
- `crates/workspace-server/src/lib.rs`
- registry/worker/capability 用 error variant を追加。
- `crates/workspace-server/src/repositories.rs`
- shared typed diagnostic severity に合わせて更新。
- `web/workspace/src/lib/workspace-sidebar/types.ts`
- runtime / capability / worker workspace summary 型を更新。
- raw `workspace_root` / local `pod_name` 前提を削除。
- `web/workspace/src/lib/workspace-pages/WorkspacePage.svelte`
- Host/Worker overview 表示を safe summary fields に合わせて更新。
Validation:
- `cargo test -p yoi-workspace-server`: success
- `cargo check -p yoi`: success
- `cd web/workspace && deno task check`: success
- `cd web/workspace && deno task build`: success
- `git diff --check`: success
Commit:
- `9bd15507 feat: add worker runtime registry overview`
Residual risks / notes:
- remote Host protocol / scheduler / stream proxy / worker control operation は non-goal のまま、typed capability/diagnostic で unsupported/pending を返す境界に留めた。
- API response から raw workspace/session/private path は出さない方針に寄せたため、UI は workspace path ではなく `current_workspace` scope/identity summary を表示する。
- `nix build .#yoi --no-link` は Cargo/Deno validation が通過済みだが、最終統合 confidence を上げる場合は Orchestrator 側判断で実行可能。
---
<!-- event: review author: yoi-reviewer-00001KVWECEQG-worker-registry at: 2026-06-24T11:01:43Z status: request_changes -->
## Review: request changes
Review result: request_changes
確認範囲:
- child worktree `/home/hare/Projects/yoi/.worktree/00001KVWECEQG-worker-runtime-registry` のみで read-only inspection。
- Ticket `item.md` / `thread.md` / orchestration plan、`371fd7c6..HEAD` の差分、主に `crates/workspace-server/src/hosts.rs` / `server.rs` / web 型・表示を確認。
- read-only のため、テスト・ビルド・`git diff --check` は実行していない。
良い点:
- `WorkspaceWorkerRuntime` trait、`WorkerRuntimeRegistry``LocalPodRuntime` が導入され、`WorkspaceApi``Arc<WorkerRuntimeRegistry>` を保持し、`/api/hosts` / `/api/workers` / `/api/hosts/{host_id}/workers` / 新規 `/api/runtimes` が registry 経由になっている。
- `HostSummary` / `WorkerSummary` は raw `workspace_root` / metadata path / socket path / raw session を直接返さない形に整理されており、UI 側も新しい safe summary shape に追随している。
- spawn/stop/proxy/stream は unsupported/reserved の型・diagnostic に留めており、scheduler / remote host protocol / raw session ingest には踏み込んでいない。
Blockers:
1. worker detail/lookup の id 解決が opaque id 境界を満たしていない。
- `worker_id_for_pod()``pod_name` を sanitize/lowercase/truncate して `local-pod-*` を作る一方、`LocalPodRuntime::worker()``local-pod-` を strip した値をそのまま `pod_root/<pod_name>/metadata.json` のディレクトリ名として使っている。
- 実 Pod 名に大文字、`.` などの置換対象文字、truncate 対象が含まれると、`list_workers()` が返した `worker_id` から同じ Worker を lookup できない。
- Yoi の実 Pod 名には Ticket ID 由来の大文字を含むものがあり得るため現実的に壊れる。これは Ticket の「worker detail / lookup」「`pod_name` を external operation key にしない」「worker_id は backend が解決する opaque id」という要件に反する。
2. id の bounded/unique 性が full id に対して保証されていない。
- `sanitize_identifier()` は fragment を最大 120 にするが、`local-pod-` / `local-` prefix 付与後の id は `validate_backend_identifier()``MAX_IDENTIFIER_LEN = 120` を超え得る。
- sanitize による衝突(例: `foo.bar``foo-bar`)も避けられていない。将来 detail endpoint や operation routing を足した時に、一覧で返した id が lookup/validation で拒否される・別 Worker に衝突するリスクがある。
Required fixes:
- `worker_id` は返却後に確実に backend で再解決できる opaque/stable id にする。raw pod name を authority として外へ出さず、scan した metadata/dir entry から `generated_worker_id -> actual pod dir` を backend 内で照合する、または衝突しない hash/digest を含める。
- `MAX_IDENTIFIER_LEN` は prefix を含む full id に対して満たすようにし、生成側と検証側の不一致をなくす。
- 大文字 Ticket ID を含む Pod 名、`.` など sanitize される Pod 名、長い Pod 名、衝突ケースについて、`list_workers()` が返した `worker_id``registry.worker()` / `LocalPodRuntime::worker()` で引けるテストを追加する。
Non-blocking note:
- Ticket acceptance には `nix build .#yoi --no-link` も含まれている一方、coder 報告にはない。必要なら修正後に Orchestrator 判断で実施・明示すること。
---
<!-- event: implementation_report author: yoi-coder-00001KVWECEQG-worker-registry at: 2026-06-24T11:09:03Z -->
## Implementation report
Follow-up implementation report after review request_changes:
対応内容:
- `worker_id` を raw Pod dir 名から逆算する実装を廃止した。
- `LocalPodRuntime::worker()` は Pod registry を backend 側で scan し、生成済み opaque `worker_id` と実 Pod dir を照合して解決する。
- API caller が渡す `worker_id` は raw path / raw Pod name authority ではなく、backend-resolved identifier として扱う。
- `worker_id` / `host_id` の生成を bounded + collision-resistant に変更した。
- prefix を含む full id が `MAX_IDENTIFIER_LEN = 120` 以下になるよう制御。
- `sha2` digest の短縮 hex を suffix として含め、`foo.bar` / `foo-bar` や長大名 truncation による collision を避ける。
- 生成 id は既存 validation を通る形式に限定。
- focused tests を追加・更新した。
- uppercase Ticket-ID-like Pod name。
- `.` / `@` / `#` 等の sanitized char。
- `foo.bar` vs `foo-bar` collision case。
- long/truncated names。
- `list_workers()` が返す id を `registry.worker()` / `LocalPodRuntime::worker()` に渡して解決できること。
- id が bounded / validation-compatible / duplicate-free であること。
変更ファイル:
- `Cargo.lock`
- `crates/workspace-server/Cargo.toml`
- `crates/workspace-server/src/hosts.rs`
- `crates/workspace-server/src/server.rs`
Validation:
- `cargo test -p yoi-workspace-server`: success
- `cargo check -p yoi`: success
- `cd web/workspace && deno task check`: success
- `cd web/workspace && deno task build`: success
- `git diff --check`: success
Commit:
- `38d25582 fix: make worker runtime ids resolvable`
Residual risks / notes:
- `sha2.workspace = true``yoi-workspace-server` に追加したため、`Cargo.lock` の workspace-server dependency entry が更新された。
- raw session/private path leak、scheduler/remote protocol scope creep、`00001KVTNAY20` の low-level launch boundary は維持している。
---
<!-- event: review author: yoi-reviewer-00001KVWECEQG-worker-registry at: 2026-06-24T11:10:55Z status: approve -->
## Review: approve
Review result: approve
確認内容:
- child worktree のみで `9bd15507..HEAD` fix diff と必要箇所の現行実装を read-only 確認。
- ビルド/テストは read-only 指示のため再実行せず、coder 報告を参照。
判断:
- 以前の blocker 2件は解消されている。
根拠:
- `LocalPodRuntime::worker()``local-pod-` strip で raw Pod dir 名へ戻す実装をやめ、`pod_names()` で registry を scan し、各 actual Pod dir name から生成した `worker_id_for_pod()` と照合して backend 内部で解決する形になっている。これにより、大文字・記号・sanitize 対象文字を含む Pod 名でも list→lookup が成立する。
- `worker_id_for_pod()` / `host_id_for_workspace()``bounded_backend_identifier()` 経由になり、prefix を含む full id が `MAX_IDENTIFIER_LEN` 内に収まるよう body を切り詰め、SHA-256 digest suffix を付けて sanitize/truncation collision を避ける設計になっている。
- 追加テストで Ticket-like uppercase Pod 名、`.`/`-` collision、`#`/`@`、長い Pod 名、duplicate-free、validation-compatible、registry/runtime lookup roundtrip がカバーされている。
- raw metadata path / socket path / workspace root / raw session を overview API に出さない方針、registry 経由の read-only API、spawn/stop/proxy 非実装の unsupported 境界は維持されている。
Non-blocking notes:
- `worker_id` は sanitize 済み body を含むため完全な無意味 IDではないが、操作キーとしては backend 解決の opaque id になっており、今回の Ticket 意図には十分合っている。
- reported validation は通過とのことだが、reviewer では再実行していない。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T11:15:04Z -->
## Implementation report
Integration outcome:
- Coder `yoi-coder-00001KVWECEQG-worker-registry` が implementation branch `work/00001KVWECEQG-worker-runtime-registry` に実装を commit した。
- Reviewer `yoi-reviewer-00001KVWECEQG-worker-registry` は初回 review で `worker_id` opaque lookup / bounded unique id の blocker を指摘し、Coder が `38d25582 fix: make worker runtime ids resolvable` で修正した。
- Follow-up review は `approve``LocalPodRuntime::worker()` の backend 内部 scan/照合、bounded digest 付き id 生成、list→lookup roundtrip tests、raw path/session 非公開、unsupported capability boundary が確認された。
- Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration` で branch `work/00001KVWECEQG-worker-runtime-registry` を merge 済み。
- merge commit: `1251c0ca merge: 00001KVWECEQG worker runtime registry`
- implementation commit: `9bd15507 feat: add worker runtime registry overview`
- fix commit: `38d25582 fix: make worker runtime ids resolvable`
- `sha2.workspace = true` 追加により `Cargo.lock` が変わったため、Nix package cargoHash を更新した。
- package hash commit: `b975812c nix: update yoi cargo hash`
Validation in Orchestrator worktree:
- `cargo test -p yoi-workspace-server`: success
- `cargo check -p yoi`: success
- `cd web/workspace && deno task check`: success
- `cd web/workspace && deno task build`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success after updating `package.nix` cargoHash to `sha256-rR8mrMDg1CCXb3Y5MnhUqSd0vAsI5OStFQCRIrvx8fg=`
Notes:
- Initial `nix build .#yoi --no-link` failed only because cargoHash was stale after the new dependency; this was fixed and revalidated.
- Reviewer non-blocking note: generated `worker_id` still includes a sanitized body fragment, but as an operation key it is now backend-resolved opaque id and acceptable for this Ticket.
Next action:
- Mark Ticket done after this integration/validation evidence.
- Then stop related child Pods and remove only the child implementation worktree/branch.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T11:15:13Z from: inprogress to: done reason: merged_validated field: state -->
## State changed
Implementation is merged into the orchestration branch and validated.
Evidence:
- merge commit: `1251c0ca merge: 00001KVWECEQG worker runtime registry`
- package hash commit: `b975812c nix: update yoi cargo hash`
- reviewer result: approve after requested changes were fixed
- validation in `/home/hare/Projects/yoi/.worktree/orchestration` succeeded:
- `cargo test -p yoi-workspace-server`
- `cargo check -p yoi`
- `cd web/workspace && deno task check`
- `cd web/workspace && deno task build`
- `git diff --check`
- `nix build .#yoi --no-link`
Closure is not performed here; this state records implementation completion after merge/validation.
---
@@ -0,0 +1 @@
{"id":"orch-plan-20260624-120242-1","ticket_id":"00001KVWPVHFJ","kind":"accepted_plan","accepted_plan":{"summary":"Ticket `00001KVWPVHFJ` は implementation_ready。専用 worktree `/home/hare/Projects/yoi/.worktree/00001KVWPVHFJ-storage-cleanup-cli` と branch `work/00001KVWPVHFJ-storage-cleanup-cli` で、safe Pod/session storage cleanup CLI を実装する。destructive operations は dry-run/force/live refusal/path-safety を厳守する。","branch":"work/00001KVWPVHFJ-storage-cleanup-cli","worktree":"/home/hare/Projects/yoi/.worktree/00001KVWPVHFJ-storage-cleanup-cli","role_plan":"Orchestrator: routing acceptance, worktree creation, final integration/validation/cleanup. Coder: CLI/storage implementation in dedicated child worktree. Reviewer: read-only review focusing on path safety, live Pod refusal, dry-run/force semantics, and session history preservation."},"author":"yoi-orchestrator","at":"2026-06-24T12:02:42Z"}
+150
View File
@@ -0,0 +1,150 @@
---
title: 'Pod/session storage cleanup CLI を追加する'
state: 'done'
created_at: '2026-06-24T11:39:41Z'
updated_at: '2026-06-24T12:36:12Z'
assignee: null
readiness: 'implementation_ready'
risk_flags: ['pod-lifecycle', 'persistence', 'destructive-operation', 'cli-ux', 'session-history', 'authority-boundary']
queued_by: 'workspace-panel'
queued_at: '2026-06-24T12:01:42Z'
---
## User claims / request snapshot
- Pod 名を指定して Pod を消去する CLI コマンドを実装する。
- Pod から参照されていないセッションを削除するコマンドを用意する。
- 古い Pod / セッションを削除するコマンドを用意する。
- コマンド名は以下で合意済み:
- `yoi pod delete <NAME> [--force] [--dry-run]`
- `yoi pod prune [--older-than <DURATION>] [--force] [--dry-run]`
- `yoi session prune --unreferenced [--older-than <DURATION>] [--force] [--dry-run]`
## Confirmed facts / sources
- 関連 closed Ticket `00001KTJ7MACG` は fresh-start 専用 UX を実装しない方針で closed 済み。ただし resolution で、Pod 名を明示的に空ける archive/delete と storage cleanup/prune commands は必要になれば別 Ticket として扱う、と記録されている。
- `crates/pod-store/src/lib.rs` では Pod metadata authority は `{data_dir}/pods/{pod_name}/metadata.json` であり、`PodMetadataStore::delete_by_name` / `FsPodStore::delete_by_name` が既に存在する。
- `crates/session-store/src/fs_store.rs` / `crates/session-store/src/lib.rs` では session log は `{sessions_root}/{session_id}/{segment_id}.jsonl` と trace jsonl で、`FsStore``list_sessions`, `list_segments`, `lookup_session_of` を持つ。確認範囲では削除用 public API は見当たらない。
- `crates/yoi/src/main.rs` では `yoi pod ...` は現在 `pod::entrypoint::run_cli_from("yoi pod", args)` に委譲されており、Pod runtime spawn/restore 用 entrypoint として使われている。
- `crates/yoi/src/session_cli.rs` では `yoi session ...` は現在 `analyze <SESSION_JSONL_PATH> --json` のみ。
- `crates/pod/src/entrypoint.rs` では `--pod <NAME>` は name-keyed Pod state があれば restore、なければ fresh create する。sessions dir は `--store` または `manifest::paths::sessions_dir()`、Pod store は `manifest::paths::data_dir()/pods` から解決している。
- duplicate / related search では、直接同じ目的の open Ticket は見つからなかった。関連 closed Ticket は `00001KTJ7MACG`, `00001KSTRGFX0`, `00001KSXXRRC8`
## Unverified hypotheses
- live Pod protection は runtime socket / Pod registry / discovery layer と組み合わせて判定できる可能性が高いが、実装時に正確な helper/API を確認する必要がある。
- orphan session 判定は「active Pod metadata から参照されない SessionId」を基本にできそうだが、fork/segment lineage、trace file、古い closed metadata、manual session restore use case をどこまで保存対象にするかは実装時に注意する必要がある。
- `yoi pod delete` を runtime entrypoint に足すより、product CLI 側で `yoi pod delete` を捕捉するほうが安全かもしれないが、既存 `yoi pod` の意味と衝突しない設計確認が必要。
## Undecided points / open questions
- Blocking open question は現時点ではない。
- 実装中に CLI boundary が既存 `yoi pod` runtime entrypoint と衝突する場合は、Orchestrator / maintainer に escalate する。
- orphan session 判定で session lineage semantics や manual restore use case への影響が大きい場合は、削除範囲を安全側に狭めるか follow-up に分ける。
## Background
以前の fresh-start Ticket では、restore bypass 専用 path を作らず、Pod 名の衝突解消や古い履歴蓄積は Pod/session storage cleanup commands として扱う方針になった。現在も Pod metadata と session logs は durable authority が分かれており、手で `~/.yoi/pods``~/.yoi/sessions` を消すのは危険で discoverable ではない。公式 CLI と safety rails が必要。
## Requirements
- Pod 名を指定して、name-keyed Pod metadata を削除できる CLI を追加する。
- コマンド名は `yoi pod delete <NAME> [--force] [--dry-run]` とする。
- 削除後、同じ Pod 名の通常起動は既存の `missing -> spawn fresh` path に乗る。
- session logs/history は `pod delete` では削除しない。
- live/reachable Pod の metadata 削除は拒否する。
- 実装が live 判定できない場合は安全側に倒し、削除せず診断する。
- live Pod を止める操作や stop semantics の変更はこの Ticket の主目的にしない。
- orphan session cleanup CLI を追加する。
- コマンド名は `yoi session prune --unreferenced [--older-than <DURATION>] [--force] [--dry-run]` とする。
- Pod metadata の active pointer から参照されていない session/segment を候補として列挙できる。
- 実削除は `--force` が必要。
- 初期実装は dry-run / report を重視し、削除対象と根拠を表示する。
- old Pod / old session cleanup CLI を追加する。
- Pod 側は `yoi pod prune [--older-than <DURATION>] [--force] [--dry-run]` とする。
- 「古い」の判定は暗黙 default を持たせず、`--older-than <DURATION>` のような明示 threshold を要求する。
- threshold 未指定で「古い」と判断して削除しない。
- destructive operation は共通で:
- default は dry-run または削除拒否にする。
- 実削除には `--force` を要求する。
- 削除対象、保持対象、拒否理由を bounded に表示する。
- product CLI help/docs を更新し、manual data-dir surgery ではなく公式 command を案内する。
- `yoi pod` が既存 runtime entrypoint として使われている境界を壊さない。
- management subcommands を product CLI 側で捕捉するか、runtime entrypoint 側に安全に追加するかは実装判断でよいが、既存 spawn/restore options と互換的に動くこと。
## Acceptance criteria
- `yoi pod delete <NAME>` 相当の公式 CLI で stopped/restorable Pod metadata を削除できる。
- 削除後、同名 Pod 起動は existing metadata restore ではなく fresh create path になる。
- live/reachable Pod に対する delete/prune は拒否され、理由が表示される。
- `pod delete` は session logs を削除しない。
- Pod metadata から参照されていない session を dry-run で列挙でき、`--force` 指定時だけ削除できる。
- 古い Pod / session cleanup は `--older-than` 等の明示条件なしでは削除しない。
- prune/delete の出力は、削除予定/削除済み/保持/拒否をユーザーが確認できる。
- focused tests が以下を cover する:
- stopped/restorable Pod delete success。
- live Pod delete refusal。
- Pod delete 後の same-name missing/fresh behavior。
- referenced session is preserved。
- unreferenced session prune dry-run and force behavior。
- old Pod/session prune requires explicit threshold。
- CLI parsing/help。
## Binding decisions / invariants
- No silent restore bypass. Fresh same-name start は、ユーザーが明示的に metadata を削除した結果としてのみ発生する。
- Pod metadata delete の副作用として session history を自動削除しない。
- live/reachable Pod metadata を削除しない。
- 暗黙の “old” threshold を持たせない。old Pod/session cleanup では age criteria をユーザーが明示する。
- destructive deletion には明示的な `--force` を要求し、dry-run/report behavior を提供する。
- Pod metadata authority は `pod-store`、session log authority は `session-store` のままにする。
- legacy top-level resume flags や bare Pod-name inference を再導入しない。
- この Ticket では Panel/TUI を broad Pod manager にしない。CLI 実装で十分とし、Panel integration は必要なら follow-up に分ける。
## Implementation latitude
- 合意済み command spelling は維持する。ただし clap/parser 上の曖昧さを避けるための minor option shape 調整は、既存 UX を壊さない範囲で許容する。
- product CLI 側で management subcommands を捕捉するか、runtime entrypoint 側へ安全に追加するかは実装判断でよい。
- 必要なら product CLI から使う shared cleanup module を作ってよい。
- 必要なら `session-store` に削除 API を追加してよい。その場合は path safety と trace/log cleanup の tests を追加する。
- Orphan session detection は初期実装では active `PodMetadata.active.session_id` references を authority としてよい。より高度な lineage-aware retention は、referenced sessions を削除しない限り follow-up に分けてよい。
- Output は初期実装では human-readable でよい。JSON output は周辺 CLI と自然に揃えられる場合を除き follow-up でよい。
## Readiness
- readiness: implementation_ready
- risk_flags: [pod-lifecycle, persistence, destructive-operation, cli-ux, session-history, authority-boundary]
## Escalation conditions
以下の場合は実装を進める前に Orchestrator / maintainer に escalate する。
- live Pod detection を安全に拒否できるほど reliable にできない。
- orphan detection が session lineage semantics の変更を必要とする。
- Pod delete の副作用として sessions を削除する必要が出る。
- storage migration や compatibility fallback が必要になる。
- command design が `yoi pod` runtime entrypoint usage と衝突する。
- cleanup が Panel role-session/Ticket claims、worktrees、branches、Ticket state を mutate しようとする。
## Validation
- `cargo fmt --check`
- focused `cargo test` for `yoi`, `pod-store`, `session-store`, and affected Pod/discovery code
- `cargo check -p yoi -p pod -p pod-store -p session-store`
- `target/debug/yoi ticket doctor` または `yoi ticket doctor`
- `git diff --check`
Reviewer は path safety、live/refusal behavior、dry-run/force semantics、session history を accidental に削除しないことを重点確認する。
## Related work
- Related closed Ticket: `00001KTJ7MACG` — Add Pod archive and fresh-start path.
- Related closed Ticket: `00001KSTRGFX0` — Split Pod metadata into a dedicated pod-store crate.
- Related closed Ticket: `00001KSXXRRC8` — Pod tools: unify pod listing and rename restore operation.
- Files inspected:
- `crates/yoi/src/main.rs`
- `crates/yoi/src/session_cli.rs`
- `crates/pod/src/entrypoint.rs`
- `crates/pod-store/src/lib.rs`
- `crates/session-store/src/fs_store.rs`
- `crates/session-store/src/lib.rs`
+216
View File
@@ -0,0 +1,216 @@
<!-- event: create author: ticket-intake at: 2026-06-24T11:39:41Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: state_changed author: workspace-panel at: 2026-06-24T12:01:42Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-24T12:03:42Z -->
## Decision
Routing decision: implementation_ready
Reason:
- Dashboard Queue により人間が Orchestrator routing を許可した queued Ticket として確認した。
- Ticket body は `yoi pod delete`, `yoi pod prune`, `yoi session prune --unreferenced` の command spelling、dry-run/force semantics、live Pod refusal、session history preservation、explicit age threshold、validation を具体的に列挙している。
- `TicketRelationQuery` は blocking relation 0 件、`TicketOrchestrationPlanQuery` は既存 plan 0 件だった。
- risk flags は pod-lifecycle / persistence / destructive-operation / cli-ux / session-history / authority-boundary だが、destructive operations の safety rails と escalation conditions が明記されている。risk は reviewer focus として扱えばよく、planning return 理由にはならない。
- 同時 queued Ticket `00001KVWPW3KX` は TUI Console rendering で code surface が別。conflict risk は低く、別 worktree/branch で並列開始可能。
Evidence checked:
- Ticket body / thread: `item.md`, `thread.md`。thread は create と ready->queued のみで未解決 blocker は記録されていない。
- Relations / orchestration plan: relation 0 件、routing 前 plan 0 件。accepted plan `orch-plan-20260624-120242-1` を記録済み。
- Code map: Grep で `crates/yoi/src/main.rs`, `crates/yoi/src/session_cli.rs`, `crates/pod-store/src/lib.rs`, `crates/session-store`, `crates/pod/src/entrypoint.rs`, `crates/pod/src/discovery.rs` 周辺を確認。
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean。active inprogress Ticket は 0 件。
IntentPacket:
Intent:
- Pod/session storage を手動削除せずに安全に整理できる公式 CLI を追加し、Pod metadata delete / Pod prune / unreferenced session prune を dry-run-first、force-required、live-safe に実装する。
Binding decisions / invariants:
- No silent restore bypass。same-name fresh start は、ユーザーが明示的に stopped/restorable Pod metadata を削除した結果としてのみ発生する。
- `pod delete` は session logs/history を削除しない。
- live/reachable Pod metadata は削除しない。live 判定が不確実なら安全側に拒否する。
- old cleanup に暗黙 threshold を持たせない。`--older-than` など明示 criteria が必要。
- destructive deletion は `--force` 必須。`--dry-run` / default report を重視する。
- Pod metadata authority は `pod-store`、session log authority は `session-store` のまま。
- legacy top-level resume flags / bare Pod-name inference は再導入しない。
- Panel/TUI の broad Pod manager 化は non-goal。
Requirements / acceptance criteria:
- `yoi pod delete <NAME> [--force] [--dry-run]` で stopped/restorable Pod metadata を削除できる。
- live/reachable Pod delete/prune は拒否され理由を出す。
- `yoi pod prune --older-than <DURATION> [--force] [--dry-run]` は explicit threshold なしに old 判定削除しない。
- `yoi session prune --unreferenced [--older-than <DURATION>] [--force] [--dry-run]` は Pod metadata active pointer から参照されない session/segment を report/prune できる。
- delete/prune output は deleted/would delete/kept/refused reason を bounded に示す。
- focused tests が stopped Pod delete, live refusal, session preservation, unreferenced prune dry-run/force, threshold requirement, CLI parsing/help を cover する。
Implementation latitude:
- product CLI 側で management subcommands を捕捉するか、runtime entrypoint 側に安全に追加するかは coder が code map を見て判断してよい。
- 必要なら shared cleanup module や `session-store` delete API を追加してよい。path safety tests を伴うこと。
- Orphan detection は初期実装では active `PodMetadata.active.session_id` references を authority としてよい。lineage-aware retention は referenced sessions を削除しない限り follow-up に分けてよい。
- Output は human-readable でよい。JSON は自然なら追加してよいが必須ではない。
Escalate if:
- live Pod detection を安全に拒否できるほど reliable にできない。
- orphan detection が session lineage semantics の変更を必要とする。
- Pod delete の副作用として sessions を削除する必要が出る。
- storage migration / compatibility fallback が必要になる。
- command design が existing `yoi pod` runtime entrypoint usage と衝突する。
- cleanup が Panel role-session/Ticket claims, worktrees, branches, Ticket state を mutate しようとする。
Validation:
- `cargo fmt --check`
- focused `cargo test` for `yoi`, `pod-store`, `session-store`, affected Pod/discovery code
- `cargo check -p yoi -p pod -p pod-store -p session-store`
- `target/debug/yoi ticket doctor` または `yoi ticket doctor`
- `git diff --check`
Current code map:
- Primary: `crates/yoi/src/main.rs`, `crates/yoi/src/session_cli.rs`, `crates/pod-store/src/lib.rs`, `crates/session-store/src/fs_store.rs`, `crates/session-store/src/lib.rs`, `crates/pod/src/entrypoint.rs`, `crates/pod/src/discovery.rs`
- Avoid: Panel/TUI manager UI, scheduler/stop semantics changes, Ticket/worktree/branch cleanup operations。
Critical risks / reviewer focus:
- accidental deletion of session history from `pod delete`
- live/reachable Pod metadata deletion。
- unsafe path deletion or broad directory removal。
- force/dry-run semantics bypass。
- ambiguous age parsing/default threshold。
- breaking `yoi pod` runtime entrypoint spawn/restore behavior。
Next action:
- `queued -> inprogress` を記録してから worktree-workflow で dedicated worktree を作成し、Coder/Reviewer sibling loop に進める。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T12:04:07Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked field: state -->
## State changed
Orchestrator acceptance: queued -> inprogress
- 直前確認で `TicketShow` は state `queued`、blocking relation は 0 件、accepted plan `orch-plan-20260624-120242-1` を確認した。
- 同時 queued Ticket `00001KVWPW3KX` は disjoint code surface のため別 worktree/branch で並列開始可能と判断した。
- routing decision と IntentPacket は Ticket thread に記録済み。
- これ以降に worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVWPVHFJ-storage-cleanup-cli` を作成し、multi-agent-workflow に接続する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T12:05:13Z -->
## Implementation report
Implementation orchestration update:
- worktree-workflow に従い、implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVWPVHFJ-storage-cleanup-cli` と branch `work/00001KVWPVHFJ-storage-cleanup-cli` を作成した。
- child worktree では tracked `.yoi` project records を見える状態にし、`.yoi/memory`、local/runtime/log/lock/secret-like `.yoi` paths を sparse-checkout で除外する方針にした。
- Coder Pod `yoi-coder-00001KVWPVHFJ-cleanup-cli` を sibling として起動した。write scope は child worktree に限定し、runtime workspace root `/home/hare/Projects/yoi` は startup 要件を満たすため non-recursive read のみにした。
- Coder には root/original workspace と orchestration worktree で read/write/validation/git operation を行わないよう明示した。
---
<!-- event: review author: yoi-reviewer-00001KVWPVHFJ-cleanup-cli at: 2026-06-24T12:30:40Z status: approve -->
## Review: approve
Review result: approve
確認内容:
- child worktree `/home/hare/Projects/yoi/.worktree/00001KVWPVHFJ-storage-cleanup-cli` のみを read-only 確認。
- Ticket 要件・acceptance criteria・IntentPacket / implementation report、`5c9331e8..HEAD` の差分、主な実装箇所 `crates/yoi/src/main.rs`, `crates/yoi/src/pod_cleanup_cli.rs`, `crates/yoi/src/session_cli.rs`, `crates/pod-store/src/lib.rs`, `crates/session-store/src/fs_store.rs` を確認。
- read-only 指示のため validation は再実行していない。
承認根拠:
- `yoi pod delete <NAME> [--force] [--dry-run]`, `yoi pod prune --older-than <DURATION> ...`, `yoi session prune --unreferenced ...` の command spelling と product CLI routing が追加されている。
- `pod delete``FsPodStore::delete_by_name` 経由で Pod metadata のみを削除し、session logs/history を削除しない。出力にも preserve が明示される。
- live/reachable Pod は Unix socket connect 成功時に拒否され、権限エラー等の uncertain liveness も fail-safe で拒否される。`--force` でも live/uncertain は override しない。
- destructive deletion は `--force && !--dry-run` の場合のみ実行され、通常時/dry-run は report-only になる。
- `pod prune``--older-than` 必須で、暗黙の old threshold を持たない。
- `session prune --unreferenced` は Pod metadata の active `session_id` を保護集合にしており、referenced session を削除対象から外す。削除は `SessionId` ベースの session-store API 経由で、path traversal 的な広範削除には見えない。
- Panel/TUI/scheduler や legacy resume/bare Pod-name inference の scope creep は見当たらない。
- Coder reported validation は Ticket 指定範囲を概ね満たしており、広い Pod lib test の失敗は既存 prompt assertion と説明され、対象 discovery test は再実行成功と記録されている。
Non-blocking note:
- dry-run/失敗 path でも既存 store constructor が root directory を初期作成し得る設計に見える。削除は発生しないため今回の blocker とは見なさないが、将来「dry-run は完全 no-write」と定義するなら follow-up で明確化するとよい。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T12:36:01Z -->
## Implementation report
Integration outcome:
- Coder `yoi-coder-00001KVWPVHFJ-cleanup-cli` が implementation branch `work/00001KVWPVHFJ-storage-cleanup-cli` に実装を commit した。
- implementation commit: `80d6861a feat: add pod and session cleanup CLI`
- child ticket report commit: `439f967c chore: record cleanup cli implementation report`
- Reviewer `yoi-reviewer-00001KVWPVHFJ-cleanup-cli` は read-only review で `approve`。Pod metadata only delete、session preservation、live/uncertain refusal、force/dry-run semantics、explicit threshold、session active reference protection、path authority、scope creep なしを確認した。
- Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration` で branch `work/00001KVWPVHFJ-storage-cleanup-cli` を merge 済み。
- merge commit: `4fb75ec3 merge: 00001KVWPVHFJ storage cleanup cli`
- merge 時に Ticket item/thread の append conflict が発生したため、orchestration 側の Ticket record を保持して merge し、この integration outcome に実装・review・validation evidence を集約して記録した。
- cleanup CLI 実装で Cargo dependencies / `Cargo.lock` が変わったため、Nix package cargoHash を更新した。
- package hash commit: `28d53aad nix: update yoi cleanup cargo hash`
Implemented behavior:
- `yoi pod delete <NAME> [--force] [--dry-run]`: stopped/restorable Pod metadata のみ削除。sessions/history は削除しない。live/uncertain liveness は拒否。
- `yoi pod prune --older-than <DURATION> [--force] [--dry-run]`: explicit threshold required。Pod metadata のみ prune。
- `yoi session prune --unreferenced [--older-than <DURATION>] [--force] [--dry-run]`: Pod metadata active session references を保護し、unreferenced session のみ対象。
- `session-store` に session deletion / mtime support を追加。
Validation in Orchestrator worktree:
- `cargo fmt --check`: success
- `cargo test -p yoi`: success
- `cargo test -p session-store --lib`: success
- `cargo test -p pod-store --lib`: success
- `cargo test -p pod discovery:: --lib`: success
- `cargo check -p yoi -p pod -p pod-store -p session-store`: success
- `cargo run -p yoi -- ticket doctor`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success after updating `package.nix` cargoHash to `sha256-8mo2/IZMq3tfnv8fKRxJOdfb+T3NOheUmqT8TiR+Wag=`
Notes:
- 初回 `nix build .#yoi --no-link` は cargoHash stale のため失敗し、hash 更新後に成功した。
- Reviewer non-blocking note: dry-run/失敗 path でも既存 store constructor が root directory を初期作成し得る設計に見える。削除は起きないため blocker ではないが、将来「dry-run は完全 no-write」と定義するなら follow-up で明確化可能。
Next action:
- Mark Ticket done after this integration/validation evidence.
- Then stop related child Pods and remove only the child implementation worktree/branch.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T12:36:12Z from: inprogress to: done reason: merged_validated field: state -->
## State changed
Implementation is merged into the orchestration branch and validated.
Evidence:
- merge commit: `4fb75ec3 merge: 00001KVWPVHFJ storage cleanup cli`
- package hash commit: `28d53aad nix: update yoi cleanup cargo hash`
- reviewer result: approve
- validation in `/home/hare/Projects/yoi/.worktree/orchestration` succeeded:
- `cargo fmt --check`
- `cargo test -p yoi`
- `cargo test -p session-store --lib`
- `cargo test -p pod-store --lib`
- `cargo test -p pod discovery:: --lib`
- `cargo check -p yoi -p pod -p pod-store -p session-store`
- `cargo run -p yoi -- ticket doctor`
- `git diff --check`
- `nix build .#yoi --no-link`
Closure is not performed here; this state records implementation completion after merge/validation.
---
@@ -0,0 +1 @@
{"id":"orch-plan-20260624-120242-1","ticket_id":"00001KVWPW3KX","kind":"accepted_plan","accepted_plan":{"summary":"Ticket `00001KVWPW3KX` は implementation_ready。専用 worktree `/home/hare/Projects/yoi/.worktree/00001KVWPW3KX-thinking-group` と branch `work/00001KVWPW3KX-thinking-group` で、TUI Console の render-time Thinking block aggregation を実装する。protocol/history/persistence は変更せず、TUI rendering tests と focused validation を行う。","branch":"work/00001KVWPW3KX-thinking-group","worktree":"/home/hare/Projects/yoi/.worktree/00001KVWPW3KX-thinking-group","role_plan":"Orchestrator: routing acceptance, worktree creation, final integration/validation/cleanup. Coder: TUI implementation in dedicated child worktree. Reviewer: read-only review focusing on render-only aggregation, selection behavior, no protocol/history changes."},"author":"yoi-orchestrator","at":"2026-06-24T12:02:42Z"}
+124
View File
@@ -0,0 +1,124 @@
---
title: 'TUI Console: 連続した Thinking block を一つの表示グループにまとめる'
state: 'done'
created_at: '2026-06-24T11:39:59Z'
updated_at: '2026-06-24T12:20:08Z'
assignee: null
readiness: 'implementation_ready'
risk_flags: ['tui-rendering', 'reasoning-display', 'block-aggregation', 'text-selection']
queued_by: 'workspace-panel'
queued_at: '2026-06-24T12:01:41Z'
---
## User claims / request snapshot
- ユーザーは、TUI Console で連続した `Thinking``Read` tool 表示のように一つにまとめて表示したいと依頼した。
- 対象 workspace は `yoi`
- Panel handoff の orchestrator Pod は `yoi-orchestrator`
## Confirmed facts / sources
- 既存 Ticket 確認では、同じ目的の active duplicate は見当たらなかった。
- 関連 closed Ticket:
- `00001KT5D44Z0` — reasoning / thinking persistence を block lifecycle に統一済み。
- `00001KVMT2J25` — in-flight snapshot が unfinished thinking block を含め、TUI が snapshot から unfinished thinking を seed して live delta 継続できるようにした。
- `00001KVHX0WBE` — Dashboard / Console / TUI の呼称と module boundary を整理済み。今回の対象は Console / single-Pod chat surface。
- `00001KTKCK8W8` — chat view の Markdown rendering 改善。今回と同じく表示層の変更であり、history / context 変更は避ける方針が近い。
- `00001KSVP63K8` は planning の in-flight composer injection で、今回の Thinking 表示集約とは別件。
- `crates/tui/src/block.rs` では `Block::Thinking(ThinkingBlock)` が独立した history block として存在し、`ThinkingState::{Streaming, Finished, Incomplete}` を持つ。
- `crates/tui/src/app.rs` では `Event::ThinkingStart` ごとに新しい `Block::Thinking` を push し、`ThinkingDelta` は最後の streaming thinking に追記し、`ThinkingDone` で finished にする。
- `crates/tui/src/ui.rs``compute_history` は block 列を走査し、`Block::ToolCall` だけ `crate::tool::render_tool(...)` に委譲して複数 block 消費を許している。
- `crates/tui/src/tool.rs``Read` renderer は、連続する `Read` tool call block を `render_read_aggregate` でまとめ、1つの header と path list として表示している。
- `crates/tui/src/ui.rs``render_thinking` は現在、各 `ThinkingBlock` を個別に header / body preview / detail として描画している。連続 Thinking をまとめる専用 path は見当たらない。
## Unverified hypotheses
- 実装は `Read` と同様に `compute_history` 側で連続する `Block::Thinking` をまとめる renderer を導入する形が自然そう。
- 「連続」は、同じ turn 内で他の block を挟まない `Block::Thinking` の連なりとして扱うのが妥当そう。
- Normal mode では 1 header + 最新または先頭 preview、Detail mode では各 Thinking body を連結または小見出し付きで読める形にすればよさそう。
## Undecided points / open questions
- blocking な未決定点はない。
- 表示文言の細部(例: `Thoughts — 3 blocks` / `Thinking... (3 blocks)` / elapsed 表示の集約方法)は実装者判断でよい。
- ただし、連続していない Thinking や turn を跨ぐ Thinking までまとめるべきではない、という invariant は維持する。
## Background
Provider / model によっては 1 turn 内で Thinking / reasoning block が複数連続して発生する。現状の Console 表示では、それぞれが個別の Thinking 表示になり、会話ログが冗長に見える可能性がある。`Read` tool は連続する `Read` call を 1つの表示グループにまとめる既存 UX があるため、Thinking でも同様の表示集約を行う。
## Requirements
- TUI Console の history rendering で、連続する `Block::Thinking` を一つの logical display group として表示する。
- 対象は Console / single-Pod chat view の表示層に限定する。
- `Read` tool の連続 aggregation と同じく、rendering 時に複数 block を消費して一つの表示にまとめられること。
- 連続していない Thinking、別 turn の Thinking、間に `AssistantText` / `ToolCall` / `User` / `System` などを挟む Thinking はまとめない。
- `Streaming` / `Finished` / `Incomplete` が混在しても、状態が誤解されない header / body 表示にする。
- Normal / Detail / Overview mode の既存意味を保つ。
- Pod history、session log、worker history、prompt context、protocol event model、reasoning persistence は変更しない。
- In-flight snapshot から復元された unfinished Thinking と live delta continuation の表示が退行しない。
## Acceptance criteria
- 1 turn 内で `Thinking` block が複数連続する場合、Console history 上では 1つの Thinking group として表示される。
- 連続 Thinking group は、少なくとも group header と読み取り可能な preview / detail body を持つ。
- Normal mode で冗長な `Thought` / `Thinking...` header が連続表示されない。
- Detail mode では、集約された各 Thinking の本文が欠落せず読める。
- Overview mode でも 1 group として扱われ、行数が不必要に増えない。
- 間に non-Thinking block がある場合や turn を跨ぐ場合は、別 group として表示される。
- Text selection / copy の既存方針が壊れない。Thinking は現在 text-like selectable block ではないため、集約後も不用意に selectable transcript text にしない。
- Read tool aggregation、ToolCall rendering、AssistantText rendering に退行がない。
- Focused TUI rendering test が追加または更新される。
## Binding decisions / invariants
- これは TUI Console の表示改善であり、reasoning / thinking の protocol、persistence、provider event semantics の変更ではない。
- Thinking content を history / context に別形式で保存し直さない。
- 未完了 Thinking を finalized assistant history と混同しない。
- `Read` aggregation と同じく render-time aggregation として扱い、source block sequence 自体は保持する。
- Turn boundary を跨いで group 化しない。
- Non-Thinking block を跨いで group 化しない。
- Dashboard / Panel の Ticket rows や web UI は対象外。
## Implementation latitude
- `compute_history``Block::Thinking` の consecutive aggregation path を追加し、`tool::render_tool` と同様に consumed count を返す helper を作ってよい。
- `render_thinking` を単体 renderer として残しつつ、複数 block 用の `render_thinking_aggregate` を追加してよい。
- Header 文言、elapsed 表示、複数 state 混在時の summary は実装者判断でよい。ただし streaming / incomplete を隠さない。
- Detail mode で各 block の境界を薄い separator / index / blank line で示すか、単純連結するかは実装者判断でよい。
- Existing tests に加えて、synthetic `App { blocks: [...] }` から `compute_history` output を見る focused unit test を追加するのが自然。
## Readiness
- readiness: implementation_ready
- risk_flags: [tui-rendering, reasoning-display, block-aggregation, text-selection]
## Escalation conditions
- protocol / pod / persistence 層の変更が必要だと分かった場合。
- Thinking を selectable / copyable transcript text として扱う UX 変更が必要になる場合。
- Provider-specific reasoning metadata の扱いに踏み込む必要が出た場合。
- Console 以外の Dashboard / web / protocol UI surface まで範囲が広がる場合。
- 表示集約のために broad TUI rendering rewrite が必要になる場合。
## Validation
- `cargo test -p tui` または focused `cargo test -p tui thinking` / rendering tests。
- `cargo fmt --check`
- `git diff --check`
- 必要に応じて `cargo check -p tui`
- 変更範囲が TUI / package integration に広がる場合のみ `nix build .#yoi` を検討。
## Related work
- Tickets:
- `00001KT5D44Z0` — Unify reasoning persistence with block lifecycle
- `00001KVMT2J25` — Pod protocol: in-flight LLM response reconnect snapshot should include unfinished blocks
- `00001KVHX0WBE` — Dashboard / Console 呼称導入と TUI モジュール境界整理
- `00001KTKCK8W8` — TUI chat view should render Markdown tables
- related but not duplicate: `00001KSVP63K8`
- Files:
- `crates/tui/src/block.rs`
- `crates/tui/src/app.rs`
- `crates/tui/src/ui.rs`
- `crates/tui/src/tool.rs`
+211
View File
@@ -0,0 +1,211 @@
<!-- event: create author: LocalTicketBackend at: 2026-06-24T11:39:59Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: ticket-intake at: 2026-06-24T11:56:52Z -->
## Intake summary
ユーザーが draft 内容を確認し、Ticket `00001KVWPW3KX` を ready にするよう明示した。Ticket 本文には implementation_ready、blocking open questions なし、対象範囲・invariants・validation が記録済みで、Orchestrator が implementation routing を判断できる。
---
<!-- event: state_changed author: ticket-intake at: 2026-06-24T11:56:52Z from: planning to: ready reason: user_confirmed_ready field: state -->
## State changed
ユーザー確認済みのため planning から ready へ移行する。blocking open questions はなく、Orchestrator は通常の routing 判断に進める。
---
<!-- event: state_changed author: workspace-panel at: 2026-06-24T12:01:41Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-24T12:03:09Z -->
## Decision
Routing decision: implementation_ready
Reason:
- Dashboard Queue により人間が Orchestrator routing を許可した queued Ticket として確認した。
- Ticket body は TUI Console の連続 Thinking block を render-time で一つの表示 group にまとめる目的、non-goals、binding invariants、validation を具体的に列挙している。
- `TicketRelationQuery` は blocking relation 0 件、`TicketOrchestrationPlanQuery` は既存 plan 0 件だった。
- risk flags は TUI rendering / reasoning-display / block-aggregation / text-selection だが、protocol / pod / persistence 変更をしない、source block sequence を保持する、turn boundary / non-Thinking block を跨がない、selection behavior を壊さない、という invariant が明示されている。risk は reviewer focus として扱えばよく、planning return 理由にはならない。
- 同時 queued Ticket `00001KVWPVHFJ` は Pod/session storage cleanup CLI で code surface が別。conflict risk は低く、別 worktree/branch で並列開始可能。
Evidence checked:
- Ticket body / thread: `item.md`, `thread.md`。thread は create、planning->ready、ready->queued のみで未解決 blocker は記録されていない。
- Relations / orchestration plan: relation 0 件、routing 前 plan 0 件。accepted plan `orch-plan-20260624-120242-1` を記録済み。
- Code map: Grep で `ThinkingStart` / `ThinkingDelta` / `ThinkingStop` / `thinking` 周辺を確認し、primary files `crates/tui/src/block.rs`, `crates/tui/src/app.rs`, `crates/tui/src/ui.rs`, `crates/tui/src/tool.rs` が妥当。
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean。active inprogress Ticket は 0 件。
IntentPacket:
Intent:
- TUI Console の transcript rendering で、assistant turn 内に連続する `Thinking` blocks を一つの表示 group として描画し、reasoning が細切れに見える UX を改善する。
Binding decisions / invariants:
- これは render-time aggregation。history / protocol / persistence / block sequence は変更しない。
- turn boundary を跨いで group 化しない。
- non-Thinking block を跨いで group 化しない。
- streaming / incomplete thinking を隠さない。
- Thinking を selectable/copyable transcript text に変える UX 変更はしない。
- Dashboard / Panel / web UI は対象外。
Requirements / acceptance criteria:
- 連続 Thinking blocks が Console 表示で一つのまとまりとして見える。
- 既存の `Read` aggregation / tool rendering と同様に、rendering helper が consumed count 等で `compute_history` の走査を壊さない。
- 単独 Thinking block の表示は regress しない。
- Detail mode / collapsed mode / streaming state が破綻しない。
- focused unit tests で consecutive Thinking, non-Thinking separator, turn boundary, incomplete/streaming case を確認する。
Implementation latitude:
- `render_thinking` を単体 renderer として残し、必要なら aggregate renderer/helper を追加してよい。
- Header 文言、elapsed 表示、detail mode の separator/index/blank line は既存 UX に沿って実装判断してよい。
- `compute_history` の構造に合わせ、最小変更で実装してよい。
Escalate if:
- protocol / pod / persistence 層の変更が必要になる。
- Thinking を text selection/copy target にする必要が出る。
- Provider-specific reasoning metadata の意味づけ変更が必要になる。
- Console 以外の UI surface に範囲が広がる。
- broad TUI rendering rewrite が必要になる。
Validation:
- `cargo test -p tui` または focused `cargo test -p tui thinking` / rendering tests。
- `cargo fmt --check`
- `git diff --check`
- 必要に応じて `cargo check -p tui`
Current code map:
- Primary: `crates/tui/src/block.rs`, `crates/tui/src/app.rs`, `crates/tui/src/ui.rs`, `crates/tui/src/tool.rs`
- Avoid: protocol/history/persistence, Dashboard/Panel/web UI, broad TUI rewrite。
Critical risks / reviewer focus:
- source blocks/history を変更してしまうこと。
- non-Thinking block や turn boundary を跨いだ誤 grouping。
- streaming/incomplete thinking state の欠落。
- text selection/copy semantics の accidental change。
Next action:
- `queued -> inprogress` を記録してから worktree-workflow で dedicated worktree を作成し、Coder/Reviewer sibling loop に進める。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T12:04:07Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked field: state -->
## State changed
Orchestrator acceptance: queued -> inprogress
- 直前確認で `TicketShow` は state `queued`、blocking relation は 0 件、accepted plan `orch-plan-20260624-120242-1` を確認した。
- 同時 queued Ticket `00001KVWPVHFJ` は disjoint code surface のため別 worktree/branch で並列開始可能と判断した。
- routing decision と IntentPacket は Ticket thread に記録済み。
- これ以降に worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVWPW3KX-thinking-group` を作成し、multi-agent-workflow に接続する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T12:05:13Z -->
## Implementation report
Implementation orchestration update:
- worktree-workflow に従い、implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVWPW3KX-thinking-group` と branch `work/00001KVWPW3KX-thinking-group` を作成した。
- child worktree では tracked `.yoi` project records を見える状態にし、`.yoi/memory`、local/runtime/log/lock/secret-like `.yoi` paths を sparse-checkout で除外する方針にした。
- Coder Pod `yoi-coder-00001KVWPW3KX-thinking-group` を sibling として起動した。write scope は child worktree に限定し、runtime workspace root `/home/hare/Projects/yoi` は startup 要件を満たすため non-recursive read のみにした。
- Coder には root/original workspace と orchestration worktree で read/write/validation/git operation を行わないよう明示した。
---
<!-- event: review author: yoi-reviewer-00001KVWPW3KX-thinking-group at: 2026-06-24T12:18:51Z status: approve -->
## Review: approve
Review result: approve
確認内容:
- child worktree の `.yoi/tickets/00001KVWPW3KX/item.md` / `thread.md` / implementation report / `5c9331e8..HEAD` diff を read-only 確認。
- 変更範囲は `crates/tui/src/ui.rs` の Console history rendering と、既存 dashboard test fixture の必須 field 追加に限定されている。
- `compute_history``Block::Thinking` のみを render-time に連続消費し、source block sequence / history / protocol / persistence は変更していない。
- 単独 Thinking は既存 `render_thinking` に委譲され、複数 Thinking のみ `render_thinking_group` で集約される。
- `TurnHeader``AssistantText` など non-Thinking を跨ぐ grouping は起きない構造で、focused tests も追加されている。
- streaming / incomplete は group header と preview/detail に反映され、未完了 Thinking を finalized text と混同する変更はない。
- group 行は `selectable=false` で追加されており、Thinking を copyable transcript text にする変更はない。
- Read tool aggregation / single ToolCall rendering の回帰確認テストも追加されている。
Validation:
- reviewer: `git diff --check 5c9331e8..HEAD`: PASS
- cargo 系は read-only/no-write 指示に合わせて再実行せず、implementation report の結果を確認した。
Non-blocking note:
- 実端末での目視確認は未実施だが、`compute_history` の行生成・selectable 属性を中心にテストされており、Ticket acceptance には十分。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T12:20:02Z -->
## Implementation report
Integration outcome:
- Coder `yoi-coder-00001KVWPW3KX-thinking-group` が implementation branch `work/00001KVWPW3KX-thinking-group` に実装を commit した。
- implementation commit: `0b2ce6ca tui: group consecutive thinking blocks`
- child ticket report commit: `7ee2b78b ticket: report thinking grouping implementation`
- Reviewer `yoi-reviewer-00001KVWPW3KX-thinking-group` は read-only review で `approve`。render-time aggregation のみで history/protocol/persistence を変更していないこと、turn/non-Thinking boundary、streaming/incomplete visibility、selectable=false、tool rendering regression tests を確認した。
- Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration` で branch `work/00001KVWPW3KX-thinking-group` を merge 済み。
- merge commit: `b52b7c96 merge: 00001KVWPW3KX thinking grouping`
- merge 時に Ticket item/thread の append conflict が発生したため、orchestration 側の Ticket record を保持して merge し、この integration outcome に実装・review・validation evidence を集約して記録した。
Implemented behavior:
- `crates/tui/src/ui.rs``compute_history` の render-time 処理に連続 `Block::Thinking` aggregation を追加。
- 単一 Thinking は既存 `render_thinking` に委譲。
- 複数 Thinking group は Normal/Overview で一つの header、Detail で各 Thinking body を読める形にした。
- streaming / incomplete state は header / preview / detail に残し、group rows は selectable=false を維持。
- `crates/tui/src/dashboard/tests.rs` は current `TicketRoleLaunchResult.acceptance_evidence` 必須 field に追随。
Validation in Orchestrator worktree:
- `cargo test -p tui`: success
- `cargo fmt --check`: success
- `cargo check -p tui`: success
- `git diff --check`: success
Notes:
- 実端末での目視確認は未実施。focused tests と full `cargo test -p tui``compute_history` rows/selectable/tool regression を確認済み。
- dependency/package change は無いため `nix build .#yoi` は実施していない。
Next action:
- Mark Ticket done after this integration/validation evidence.
- Then stop related child Pods and remove only the child implementation worktree/branch.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T12:20:08Z from: inprogress to: done reason: merged_validated field: state -->
## State changed
Implementation is merged into the orchestration branch and validated.
Evidence:
- merge commit: `b52b7c96 merge: 00001KVWPW3KX thinking grouping`
- reviewer result: approve
- validation in `/home/hare/Projects/yoi/.worktree/orchestration` succeeded:
- `cargo test -p tui`
- `cargo fmt --check`
- `cargo check -p tui`
- `git diff --check`
Closure is not performed here; this state records implementation completion after merge/validation.
---
@@ -0,0 +1 @@
{"id":"orch-plan-20260624-190611-1","ticket_id":"00001KVWSQM22","kind":"accepted_plan","accepted_plan":{"summary":"Ticket `00001KVWSQM22` は implementation_ready as a design/planning artifact。専用 worktree `/home/hare/Projects/yoi/.worktree/00001KVWSQM22-backend-orch-runtime` と branch `work/00001KVWSQM22-backend-orch-runtime` で、Backend internal Orchestrator runtime / Kanban durable orchestration event / WorkerRuntime registry identity and spawn-intent boundary を明文化する。Backend internal Orchestrator の full implementation や direct filesystem authority は non-goal。","branch":"work/00001KVWSQM22-backend-orch-runtime","worktree":"/home/hare/Projects/yoi/.worktree/00001KVWSQM22-backend-orch-runtime","role_plan":"Orchestrator: routing acceptance, worktree creation, final integration/validation/cleanup. Coder: design/planning implementation in dedicated child worktree, producing maintained design documentation and any minimal code/test updates needed to align existing Workspace runtime/Panel queue boundaries. Reviewer: read-only review against recorded IntentPacket, focusing on authority boundaries, durable event semantics, runtime identity, and non-goals."},"author":"yoi-orchestrator","at":"2026-06-24T19:06:11Z"}
+153
View File
@@ -0,0 +1,153 @@
---
title: 'Backend internal Orchestrator runtime for Kanban operations'
state: 'done'
created_at: '2026-06-24T12:29:58Z'
updated_at: '2026-06-24T19:15:42Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-24T19:04:55Z'
---
## 背景
Workspace / Kanban UI から Ticket を `ready -> queued` する操作は、単なる表示変更ではなく「Orchestrator が routing / start-if-unblocked を開始してよい」という human gate として扱いたい。一方で、現状の Orchestrator は local Pod として起動・接続・通知される形に寄っており、Kanban 操作を transient Pod notification に直接結びつけると、Pod lifetime / socket / session / workspace cwd / local filesystem authority に依存しやすい。
今後の Workspace backend は Worker runtime registry を持つ control plane になり、Backend internal runtime、local Pod runtime、remote / multi-machine runtime を束ねる方向に進めたい。Kanban 操作を Orchestration に接続するなら、routing / decision / spawn intent を担当する Orchestrator は、filesystem-capable Pod ではなく Backend internal runtime 上の Worker として扱う方が自然である。
ただし、Backend internal Orchestrator は実装者ではなく control-plane の判断者 / ルーターであり、Bash / raw filesystem / raw socket path / raw session 全量読みのような authority を持つべきではない。実装・review・build・worktree 操作は、local / remote の filesystem-capable runtime 上の Coder / Reviewer / helper Worker に委譲する。
この Ticket では、Kanban operation と Orchestration を durable event / Backend internal Worker / WorkerRuntime registry 経由で接続する設計を planning する。
## 目的
- Kanban の Ticket state operation を Orchestration の durable trigger として扱う設計を固める。
- Orchestrator を Backend internal runtime 上の Worker として表現する設計を固める。
- Backend internal Orchestrator が使う tool / operation surface を、Pod 用 filesystem tools ではなく domain-specific backend tools として整理する。
- routing decision、spawn intent、overview / audit projection を raw session に依存せず残せるようにする。
- Coder / Reviewer / worktree / shell 操作は filesystem-capable runtime に委譲する境界を明確にする。
## 要件
### Kanban operation / orchestration event
- Workspace / Kanban UI の Ticket operation は backend API を通じて実行する。
- `ready -> queued` は「Orchestrator routing を開始してよい」という human gate として扱う。
- Kanban operation は transient Pod notify ではなく、durable orchestration event を生成する。
- 例: `ticket_queued`, `ticket_state_changed`, `ticket_returned_to_planning`, `ticket_done`
- orchestration event は actor / source / ticket_id / before state / after state / timestamp / request id を持つ。
- event は再処理・重複・backend restart に耐える形にする。
- UI click から直接 local Pod socket や raw runtime path へ通知しない。
### Backend internal Orchestrator Worker
- Backend internal runtime 上に Orchestrator Worker を置ける設計にする。
- Backend internal Orchestrator は filesystem / shell / git authority を前提にしない。
- Backend internal Orchestrator は Kanban / Ticket / runtime registry event を読み、routing decision を行う。
- Orchestrator の durable output は raw session ではなく以下を中心にする。
- routing decision
- orchestration plan / blocker / waiting reason
- spawn intent
- worker run overview
- usage aggregate / lifecycle projection
- failure summary / escalation request
- Backend internal Orchestrator が停止・再起動しても、未処理 event / in-progress decision の扱いが壊れないようにする。
### Tool / operation surface redesign
Backend internal Orchestrator に渡す tool / operation surface は、Pod 用の汎用 filesystem tools ではなく、Backend domain operation として設計する。
最低限検討する tool / operation:
- Ticket list / show / comment / state transition。
- Ticket relation / orchestration plan read/write。
- Kanban event read / ack / defer / fail。
- Runtime registry list / capability query / worker lookup。
- Worker spawn intent create / dispatch。
- Worker run overview read / append。
- Usage aggregate read。
- Audit / decision record append。
原則として渡さないもの:
- `Bash`
- raw `Read` / `Write` / `Edit` over workspace filesystem。
- raw Unix socket path 操作。
- raw session 全量 ingest / unrestricted read。
- local Pod metadata path / session path を直接 authority とする操作。
### WorkerRuntime registry connection
- Backend internal Orchestrator は、実装作業を直接行わず、WorkerRuntime registry に typed spawn intent を渡す。
- runtime selection は capability を見る。
- routing-only / intake / dashboard assistant は Backend internal runtime でもよい。
- coder / reviewer / worktree / git / shell / build が必要な作業は filesystem-capable local / remote runtime を必要とする。
- spawn intent は raw process launch config ではなく role / ticket_id / required capabilities / workspace identity / cwd semantics / profile/workflow selection intent を表す。
- local Pod runtime はその intent を低レベル Pod process launch config へ解決する adapter として扱う。
### Worker identity / API / DB shape
- UI 表示では human-readable な `worker-name@runtime-name` 形式を使う。
- 例: `super-duper-pod@runtimeA`
- これは表示用 label / `display_ref` であり、操作対象の authority にはしない。
- API の canonical identity は `runtime_id` + `worker_id` とする。
- `runtime_id` / `worker_id` は opaque id として扱う。
- `pod_name` は local Pod runtime の implementation detail / display hint とする。
- Browser から `display_ref``pod_name`、runtime display name を authority として渡さない。
- Worker list/detail response は少なくとも以下を返せる形にする。
- `runtime_id`
- `worker_id`
- `display_name`
- `runtime_display_name`
- `display_ref`
- `implementation.kind`
- local Pod runtime の場合のみ diagnostic/display 用 `implementation.pod_name`
- API path は runtime scoped identity を表現する。
- 例: `GET /api/runtimes/{runtime_id}/workers/{worker_id}`
- 横断 list は `GET /api/workers` とし、各 item に `runtime_id` / `worker_id` / `display_ref` を含める。
- DB では `UNIQUE(runtime_id, worker_id)` を必須にする。
- DB の primary key は composite PK でもよいが、run overview / lifecycle event / usage aggregate からの参照を考えると、surrogate `id` + `UNIQUE(runtime_id, worker_id)` を優先候補とする。
- Worker run / lifecycle / usage / overview record は surrogate worker record id を参照できる形にしつつ、external API では runtime scoped identity を正とする。
### Session / overview boundary
- Backend は raw session log を durable authority として全量保持しない。
- Backend internal Orchestrator の decision / overview / audit を durable projection とする。
- raw session / verbose event stream / provider trace は runtime-local debug/source log とし、必要時に bounded read できる程度に留める。
- Kanban / Orchestration UI は raw transcript ではなく overview / decision / worker state / usage aggregate を表示する。
### Safety / authority
- Kanban UI click から Backend internal Orchestrator が直接 shell/git/filesystem を実行する設計にしない。
- implementation side effect 前には、既存 Ticket lifecycle authority に従って `queued -> inprogress` acceptance と decision record を残す。
- dependency / conflict / dirty workspace / missing requirement / runtime unavailable の場合は、spawn せず decision / blocker / waiting reason を記録する。
- Backend internal Orchestrator の tool set は permission/auth model を後から挟める境界にする。
- Browser は local path / socket / runtime registry path / raw session path を authority として渡さない。
## Non-goals
- Backend internal Orchestrator の full implementation。
- Kanban UI の完成。
- Coder / Reviewer spawn の本実装。
- Remote runtime protocol の本実装。
- raw session 全量の Backend DB ingest。
- Ticket storage の DB migration。
- Permission / auth model の完成。
- Worktree / git 操作を Backend process に直接持たせること。
## 受け入れ条件
この Ticket は planning から開始する。ready に進める前に以下を満たす。
- Kanban operation から durable orchestration event を生成する方針が明文化されている。
- `ready -> queued` を Orchestrator routing human gate として扱う方針が明記されている。
- Backend internal Orchestrator Worker の責務と non-responsibility が明確になっている。
- Backend internal Orchestrator に渡す domain-specific tool / operation surface が整理されている。
- Bash / raw filesystem / raw socket / raw session 全量 ingest を internal Orchestrator authority にしない方針が明記されている。
- WorkerRuntime registry と spawn intent の接続方針が明記されている。
- UI 表示は `worker-name@runtime-name` 形式を使い、API authority にはしない方針が明記されている。
- API canonical identity は `runtime_id` + `worker_id` とし、runtime scoped opaque id として扱う方針が明記されている。
- DB は surrogate worker record id + `UNIQUE(runtime_id, worker_id)` を優先候補とし、run overview / lifecycle / usage 参照に使える方針が明記されている。
- filesystem-capable work は local / remote runtime 上の Coder / Reviewer / helper Worker に委譲する方針が明記されている。
- raw session ではなく overview / decision / lifecycle / usage aggregate を Backend durable projection とする方針が明記されている。
- failure / blocker / retry / event ack semantics の初期方針が整理されている。
- 実装まで含める場合は `cargo test -p yoi-workspace-server``cargo check -p yoi``git diff --check`、必要に応じて `nix build .#yoi --no-link` が通る。
+257
View File
@@ -0,0 +1,257 @@
<!-- event: create author: "yoi ticket" at: 2026-06-24T12:29:58Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: hare at: 2026-06-24T13:20:54Z -->
## Intake summary
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-24T13:20:54Z from: planning to: ready reason: cli_state field: state -->
## State changed
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: workspace-panel at: 2026-06-24T19:04:55Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-24T19:06:45Z -->
## Decision
Routing decision: implementation_ready
Reason:
- Dashboard Queue により人間が Orchestrator routing を許可した queued Ticket として確認した。
- Ticket body は Backend internal Orchestrator runtime / Kanban operation / durable orchestration event / WorkerRuntime registry identity / spawn intent / session overview boundary / safety authority を具体的に列挙している。
- `TicketRelationQuery` は blocking relation 0 件、routing 前 `TicketOrchestrationPlanQuery` は既存 plan 0 件だった。
- Ticket の受け入れ条件は「planning artifact を明文化する」性質であり、Backend internal Orchestrator の full implementation は non-goal と明記されている。したがって Coder に実装詳細を固定させるのではなく、maintained design/planning document と必要最小限の boundary alignment を作る task として bounded に進められる。
- risk flags 相当の domain は orchestration authority / runtime identity / durable event / raw session boundary だが、Bash/raw filesystem/raw socket/raw session ingest を authority にしない、`queued -> inprogress` acceptance を守る、Browser が raw local path を渡さない、という invariant が明示されている。risk は reviewer focus と escalation condition に反映すればよく、planning return 理由にはならない。
- 現在 inprogress Ticket は 0 件、orchestration worktree は clean。既存 worktree は unrelated paused Ctrl-X cancel worktree のみで、この Ticket の branch/worktree はまだ無い。
Evidence checked:
- Ticket body / thread: `item.md`, `thread.md`。thread は create、planning->ready、ready->queued のみで未解決 blocker は記録されていない。
- Relations / orchestration plan: relation 0 件、routing 前 plan 0 件。accepted plan `orch-plan-20260624-190611-1` を記録済み。
- Workspace/code context: recent Worker runtime registry / spawn boundary work in `crates/workspace-server/src/hosts.rs`, `crates/workspace-server/src/server.rs`; Dashboard/Kanban queue surface in `crates/tui/src/dashboard/mod.rs`; docs/design structure under `docs/design/`
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean。visible Pods は current Orchestrator と stopped/restorable historical role Pods only, active child work is none。
IntentPacket:
Intent:
- Kanban UI operation を Backend internal Orchestrator Worker へ安全に接続するための design/planning artifact を作る。Kanban operation は durable orchestration event として記録され、internal Orchestrator は domain-specific tools で event を処理し、実 filesystem-capable work は local/remote runtime 上の Coder/Reviewer/helper Worker へ委譲する境界を明確にする。
Binding decisions / invariants:
- Kanban click / API request から Backend process が直接 shell/git/filesystem を実行しない。
- implementation side effect 前には既存 Ticket lifecycle authority、特に `queued -> inprogress` acceptance と decision record を保持する。
- `ready -> queued` は Orchestrator routing human gate として扱い、unattended scheduler にしない。
- Browser は raw local path / socket path / runtime registry path / raw session path / executable path を authority として渡さない。
- UI display は `worker-name@runtime-name` 形式を使ってよいが、API authority にはしない。
- canonical API identity は `runtime_id` + `worker_id` の runtime-scoped opaque id とする。
- DB design は surrogate worker record id + `UNIQUE(runtime_id, worker_id)` を優先候補とし、run overview / lifecycle event / usage aggregate 参照に使える形にする。
- Backend durable projection は raw transcript 全量ではなく overview / decision / lifecycle / usage aggregate を中心にする。raw session/provider trace は runtime-local source/debug log として bounded read に留める。
- Backend internal Orchestrator の full implementation、Kanban UI completion、remote protocol、raw session full DB ingest、Ticket DB migration、permission/auth completion は non-goal。
Requirements / acceptance criteria:
- Kanban operation から durable orchestration event を生成する方針が明文化される。
- Backend internal Orchestrator Worker の責務 / non-responsibility、domain-specific tool/operation surface、failure/blocker/retry/event ack semantics が整理される。
- WorkerRuntime registry と spawn intent の接続方針が明記される。
- runtime/worker identity, display label, DB identity, event and overview projection boundaries が明確化される。
- filesystem-capable work は runtime 上の Coder/Reviewer/helper Worker に委譲する方針が明記される。
- existing docs/code organization に沿って maintained design artifact が置かれる。
Implementation latitude:
- Primary output は docs/design などの maintained design document でよい。必要に応じて README/index や small comments/tests を追加してよい。
- 既存 Workspace runtime registry / spawn boundary の code names に合わせて wording を調整してよい。
- Full backend implementation や new DB schema migration はしない。
Escalate if:
- Backend process が direct shell/git/filesystem authority を持つ設計にしないと要件を満たせない。
- raw session full ingest / raw socket path / raw workspace path を API authority にする必要が出る。
- `ready -> queued` の human gate を scheduler/lease に置き換える必要が出る。
- API canonical identity を display label や raw Pod name に寄せる必要が出る。
- Ticket DB migration / permission-auth model completion / remote runtime protocol をこの Ticket で固定する必要が出る。
Validation:
- `git diff --check`
- docs-only なら `cargo fmt --check` は不要だが、Rust/comments/tests を触るなら `cargo fmt --check` と relevant `cargo check` / `cargo test` を実施する。
- If `crates/workspace-server` is touched: `cargo test -p yoi-workspace-server` and `cargo check -p yoi`
Current code/docs map:
- Primary docs: `docs/design/` and docs index/README if appropriate。
- Context code: `crates/workspace-server/src/hosts.rs`, `crates/workspace-server/src/server.rs`, `crates/tui/src/dashboard/mod.rs`
- Avoid: root/original workspace operations, broad scheduler implementation, Kanban UI completion, remote Host protocol, raw session DB ingest, Ticket storage migration。
Critical risks / reviewer focus:
- accidental scheduler/lease semantics。
- Backend internal Orchestrator gaining raw shell/filesystem/socket authority。
- display labels becoming API authority。
- runtime workspace root / process cwd / worker id / DB surrogate id confusion。
- raw session/provider trace becoming Backend durable projection instead of bounded debug source。
- design doc that repeats Ticket text without resolving event/ack/failure/spawn-intent boundaries.
Next action:
- `queued -> inprogress` を記録してから worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVWSQM22-backend-orch-runtime` を作成し、multi-agent-workflow で Coder/Reviewer sibling loop に進める。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T19:07:09Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked field: state -->
## State changed
Orchestrator acceptance: queued -> inprogress
- 直前確認で `TicketShow` は state `queued`、blocking relation は 0 件、accepted plan `orch-plan-20260624-190611-1` を確認した。
- routing decision と IntentPacket は Ticket thread に記録済み。
- `00001KVWSQM22` は planning/design artifact として implementation-ready であり、Backend internal Orchestrator の full implementation は non-goal として明示した。
- これ以降に worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVWSQM22-backend-orch-runtime` を作成し、multi-agent-workflow に接続する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T19:08:11Z -->
## Implementation report
Implementation orchestration update:
- worktree-workflow に従い、implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVWSQM22-backend-orch-runtime` と branch `work/00001KVWSQM22-backend-orch-runtime` を作成した。
- child worktree では tracked `.yoi` project records を見える状態にし、`.yoi/memory`、local/runtime/log/lock/secret-like `.yoi` paths を sparse-checkout で除外する方針にした。
- Coder Pod `yoi-coder-00001KVWSQM22-backend-orch-runtime` を sibling として起動した。write scope は child worktree に限定し、runtime workspace root `/home/hare/Projects/yoi` は startup 要件を満たすため non-recursive read のみにした。
- Coder には root/original workspace と orchestration worktree で read/write/validation/git operation を行わないよう明示した。
- この Ticket は full implementation ではなく design/planning artifact として進める。
---
<!-- event: implementation_report author: yoi-coder-00001KVWSQM22-backend-orch-runtime at: 2026-06-24T19:12:33Z -->
## Implementation report
Implementation report:
- 対象: Ticket `00001KVWSQM22`
- 作業ブランチ: `work/00001KVWSQM22-backend-orch-runtime`
- 作業場所: `/home/hare/Projects/yoi/.worktree/00001KVWSQM22-backend-orch-runtime`
- コミット: `f901b9bee37a03faf380894926241107362da360`
- コミットメッセージ: `docs: design backend internal orchestrator runtime`
変更ファイル:
- `docs/design/workspace-kanban-orchestrator-runtime.md`
- Kanban/API 操作から Backend internal Orchestrator Worker へ接続するための設計文書を追加。
- durable orchestration event の形、`ack` / `defer` / `fail`、idempotency/restart/retry 方針を整理。
- Backend internal Orchestrator が routing/control-plane Worker であり、Bash/filesystem/git/raw socket/session authority を持たないことを明記。
- Coder/Reviewer/helper など filesystem-capable work は typed spawn intent と `WorkerRuntimeRegistry` 経由で委譲する設計として整理。
- runtime/worker identity、API 形状、DB projection 方針、`display_ref` / `pod_name` が authority ではないことを明記。
- raw session は通常 projection ではなく bounded debug-only とする境界を整理。
- dependency/conflict/dirty workspace/missing requirement/runtime unavailable などの waiting/blocker reason を整理。
- `docs/README.md`
- 新しい設計文書を docs index に追加。
- `docs/design/overview.md`
- `workspace-server` を local Workspace control-plane seam として位置づけ、raw path/socket/Pod/session を browser/API authority にしない方針を追記。
検証:
- `git diff --check`: success
- docs-only 変更のため `cargo fmt` / `cargo test` / `cargo check` は実行していません。
残リスク / エスカレーション:
- 本 Ticket の意図どおり、設計・計画 artifact の作成までで停止しています。
- Backend internal Orchestrator 本体、Kanban UI 完成、spawn 実装、remote runtime protocol、raw session DB ingest、Ticket storage DB migration、permission/auth completion は未実装のまま non-goal として明記しました。
- merge / close は行っていません。
---
<!-- event: review author: yoi-reviewer-00001KVWSQM22-backend-orch-runtime at: 2026-06-24T19:15:09Z status: approve -->
## Review: approve
Review result: approve
確認範囲:
- child worktree `/home/hare/Projects/yoi/.worktree/00001KVWSQM22-backend-orch-runtime` のみ。
- Ticket `item.md` / `thread.md` / `artifacts/orchestration-plan.jsonl`、commit `f901b9b...` の docs diff、追加 design doc を read-only で確認。
- `git diff --check 8931227..f901b9b -- docs/...`: success。
承認根拠:
- 追加された `docs/design/workspace-kanban-orchestrator-runtime.md` は Ticket の単純コピーではなく、維持対象の設計 artifact として Core rule / durable events / ack-defer-fail / internal Orchestrator tool surface / spawn intent / API identity / DB projection / failure semantics / future implementation order まで具体化している。
- Kanban/API 操作から durable orchestration event、Backend internal Orchestrator Worker、`WorkerRuntimeRegistry`、typed spawn intent、filesystem-capable Coder/Reviewer/helper Worker への委譲までの鎖が明記されている。
- `ready -> queued` は human gate であり unattended scheduler / lease ではない、と明記されている。
- Backend internal Orchestrator は routing/control-plane Worker で、`Bash`、raw filesystem、git/worktree/build、raw socket/session path、raw session full ingest を持たない境界が明確。
- API authority は `runtime_id` + `worker_id` の opaque runtime-scoped identity に限定し、`worker-name@runtime-name` / `display_ref` / `pod_name` / path / socket / session を authority にしない方針が明記されている。
- DB/projection は surrogate `workers.id` + `UNIQUE(runtime_id, worker_id)`、run overview / lifecycle / usage aggregate 参照方針まで記述されている。
- failure/blocker/retry/ack/defer/fail semantics は future implementation Ticket の入力として十分な初期方針になっている。
- Non-goals も維持されており、full backend implementation、Kanban UI completion、remote protocol、raw session DB ingest、Ticket DB migration、permission/auth completion に踏み込んでいない。
Non-blocking notes:
- child worktree 側の Ticket thread には Orchestrator 側で記録した implementation_report が見えなかったが、Orchestrator 側 Ticket record には report を記録済みであり、レビューは commit/diff と Coder report に基づいて行った。
- 新規 design doc は diff 上 EOF newline が無いように見えたが、`git diff --check` は通っており blocker ではない。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T19:15:35Z -->
## Implementation report
Integration outcome:
- Coder `yoi-coder-00001KVWSQM22-backend-orch-runtime` が implementation branch `work/00001KVWSQM22-backend-orch-runtime` に design artifact を commit した。
- design commit: `f901b9be docs: design backend internal orchestrator runtime`
- Reviewer `yoi-reviewer-00001KVWSQM22-backend-orch-runtime` は read-only review で `approve`。durable orchestration event、ack/defer/fail、Backend internal Orchestrator tool surface、spawn intent、runtime/worker identity、DB projection、failure semantics、non-goals が future implementation Ticket の入力として十分に具体化されていることを確認した。
- Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration` で branch `work/00001KVWSQM22-backend-orch-runtime` を merge 済み。
- merge commit: `4e713fce merge: 00001KVWSQM22 backend orchestrator design`
Changed files:
- `docs/design/workspace-kanban-orchestrator-runtime.md`
- `docs/README.md`
- `docs/design/overview.md`
Validation in Orchestrator worktree:
- `git diff --check HEAD~1..HEAD`: success
- `git diff --check f367d732..HEAD -- docs .yoi/tickets/00001KVWSQM22`: success
Notes:
- docs/design-only change のため Cargo validation は実施していない。
- Reviewer non-blocking note: child worktree 側の Ticket thread には Orchestrator 側 report が見えなかったが、Orchestrator 側 Ticket record には report/review/outcome を記録済み。
- Backend internal Orchestrator 本体、Kanban UI completion、Coder/Reviewer spawn 実装、remote runtime protocol、raw session DB ingest、Ticket DB migration、permission/auth completion は non-goal のまま。
Next action:
- Mark Ticket done after this integration/validation evidence.
- Then stop related child Pods and remove only the child implementation worktree/branch.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T19:15:42Z from: inprogress to: done reason: merged_validated field: state -->
## State changed
Design/planning artifact is merged into the orchestration branch and validated.
Evidence:
- merge commit: `4e713fce merge: 00001KVWSQM22 backend orchestrator design`
- reviewer result: approve
- validation in `/home/hare/Projects/yoi/.worktree/orchestration` succeeded:
- `git diff --check HEAD~1..HEAD`
- `git diff --check f367d732..HEAD -- docs .yoi/tickets/00001KVWSQM22`
Closure is not performed here; this state records implementation/design completion after merge/validation.
---
+142
View File
@@ -0,0 +1,142 @@
---
title: 'Redesign Plugin WASM runtime API and execution model'
state: 'closed'
created_at: '2026-06-24T19:31:28Z'
updated_at: '2026-06-24T19:55:18Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-24T19:39:36Z'
---
## 背景
Yoi の Plugin API はまだ public release されていないにもかかわらず、実装上 `LegacyToolAdapter` / legacy raw `wasm` runtime と新しい `wasm-component` runtime が併存している。外部互換性を守る必要がない段階で legacy layer を残すと、Plugin runtime の責務境界、権限設計、service / ingress 実行モデル、host API の将来設計が曖昧になる。
現状の Plugin 実行モデルは、Pod process 内の Feature として Plugin を有効化し、Tool call / explicit ingress dispatch のタイミングで Wasm component の exported function を同期的に呼ぶ形である。Tool Plugin には使えるが、Discord Gateway / Slack Socket Mode のような長寿命 WebSocket integration、host-managed background service、非同期 ingress delivery、heartbeat / reconnect / backpressure を必要とする用途には足りない。
また、`host_api.websocket``open` / `send-text` / `recv(timeout)` / `close` の pull 型 API であり、Plugin service が WebSocket frame arrival を非同期 event として受け取る実行モデルになっていない。Service / Ingress の型と下地はあるが、実運用可能な event-driven Plugin runtime としては未完成である。
この Ticket では、未公開機能としての整理を前提に legacy runtime を削除し、要件を満たす WASM Plugin runtime API / execution model を設計し直す。
## 目的
- 未公開の legacy Plugin runtime / adapter を active design から削除する。
- Plugin runtime を `wasm-component` / Component Model 中心に一本化する。
- Tool Plugin、Service Plugin、Ingress Plugin の責務と実行モデルを明確化する。
- 長寿命 integration に必要な host-managed event runtime を設計する。
- WebSocket を `recv(timeout)` pull primitive ではなく、host-owned connection / event delivery / outbound command model として扱えるようにする。
- Plugin が hidden context injection や ambient authority を持たず、明示 grant / explicit history / domain operation の原則を保つ。
## 要件
### Legacy runtime cleanup
- public release 前であるため、`LegacyToolAdapter` / raw `wasm` runtime / `yoi-plugin-wasm-1` 互換 layer は active runtime から削除する方針にする。
- 新規 Plugin API は `wasm-component` runtime のみを正とする。
- manifest schema から legacy runtime を残す必要があるかを確認し、不要なら reject する。
- compatibility alias / fallback を作らない。
- tests / templates / docs から legacy wording を取り除く。
- migration は不要。既存 private fixture が必要なら fixture を更新する。
### Runtime model
- Plugin は Pod process 内の untrusted extension であり、ambient process / filesystem / network authority を持たない。
- Component call は bounded execution とし、fuel / memory / input / output limits を維持する。
- Tool Plugin は request-response 型の bounded operation として扱う。
- Service Plugin は host-managed lifecycle を持つ。
- start
- status
- stop
- failure / restart / disable semantics
- Ingress Plugin は host-managed event queue から配送される event を処理する。
- 同一 Plugin instance の concurrency policy を明確にする。
- v0 は per-plugin serial dispatch でよい。
- queue / backpressure / timeout / failure handling を定義する。
- `start()` が無限 loop / long polling / recv loop を実行するモデルにしない。
### Service / Ingress event runtime
- Plugin service 用の event queue を導入する方針にする。
- Host が event source を管理し、Plugin の `handle-ingress` 相当へ配送する。
- event delivery は bounded / observable / retry-safe にする。
- event は source / ingress name / payload / created_at / delivery attempt / correlation id を持つ。
- Plugin output は raw side effect ではなく command / result として返す。
- websocket send
- request dispatch
- domain event append
- diagnostic / status update
- output command の許可は manifest declaration + enablement grant で制御する。
### WebSocket runtime
- 現行 `host_api.websocket.open/send/recv/close` pull API を、長寿命 integration の正 API として扱わない。
- Discord Gateway などを実装できる host-owned WebSocket driver を設計する。
- Host が connection lifecycle を管理する。
- connect
- reader task
- incoming frame queue
- close / error detection
- reconnect / resume hook
- heartbeat support
- backpressure
- Incoming WS frame は Plugin service の ingress event として配送する。
- Plugin は必要な返信を output command として返し、Host が WebSocket send を実行する。
- binary / text / close / ping / pong の扱いを明確化する。
- guest-supplied handshake headers / auth injection は grant / secret ref / host policy 経由で扱う。
### Host API and authority
- Host API は explicit capability として扱う。
- request
- websocket service driver
- scoped fs
- future domain operations
- Plugin package declaration と enablement grant の両方が必要であることを維持する。
- Workspace filesystem scope / Pod tool authority を Plugin が自動継承しない。
- raw session / raw socket path / local runtime path を Plugin authority にしない。
- Plugin が prompt/context に隠し injection する API は作らない。
- MCP bridge は Plugin API と混同しない。
### Manifest / PDK / WIT
- `plugin.toml` の runtime / surface / service / ingress / host_api declaration を再整理する。
- `world tool``world instance` の責務を見直す。
- Service / Ingress 用の WIT / PDK API を、event-driven runtime に合う形へ更新する。
- WebSocket は low-level recv loop ではなく subscription / event / command model を表現できる API にする。
- PDK が長寿命 loop を推奨しない形にする。
- templates は新 API のみを生成する。
### Observability / status
- Plugin instance / service の status を backend / Pod から確認できるようにする。
- WebSocket connection status、last event、last error、queue depth、restart count、dropped event count などの diagnostic shape を設計する。
- Plugin service failure は fail closed とし、LLM-visible tool surface に曖昧な成功として出さない。
## Non-goals
- Discord Plugin の実装。
- Public plugin registry / install / update / signature policy の完成。
- Remote plugin execution runtime。
- MCP を Plugin runtime に統合すること。
- Plugin に unrestricted filesystem / shell / network authority を与えること。
- Legacy runtime 互換性の維持。
## 受け入れ条件
- `LegacyToolAdapter` / raw `wasm` runtime / `yoi-plugin-wasm-1` 互換 layer が active runtime から削除されている。
- `plugin.toml` / manifest validation は legacy runtime を新規 Plugin runtime として受け付けない。
- Plugin runtime は `wasm-component` / Component Model を正とする実装に一本化されている。
- Tool Plugin は bounded request-response operation として動作し、既存 Tool registration / execution tests が新 runtime model で通る。
- Service Plugin は host-managed lifecycle として `start` / `status` / `stop` / failure state を扱える。
- `start()` が long-running loop / polling loop / WebSocket recv loop を担わない実行モデルになっている。
- Ingress event は host-managed queue / dispatcher から Plugin instance に配送できる。
- Ingress dispatch には bounded queue、serial dispatch、backpressure、timeout、failure handling の実装または型境界がある。
- WebSocket は host-owned connection driver として扱われ、incoming frame を ingress event に変換できる。
- Plugin は WebSocket 返信・request dispatch・diagnostic update などの side effect を output command として返し、Host が grant 検査後に実行する。
- 現行 `open` / `send-text` / `recv(timeout)` / `close` pull API は、長寿命 integration の正 API から外れている、または互換目的で残す場合も public/recommended API として露出していない。
- Manifest / WIT / PDK / templates が新しい Component Model runtime、Service lifecycle、Ingress event、WebSocket command model に合わせて更新されている。
- Plugin package declaration と enablement grant の両方が必要な authority 境界が維持されている。
- Plugin は workspace filesystem scope、Pod tool authority、raw session、raw socket path、local runtime path を自動継承しない。
- Plugin instance / service / WebSocket connection の status と diagnostics を Pod/backend から確認できる。
- 関連 docs と tests から legacy runtime 前提の記述・fixture が削除または更新されている。
- `cargo test -p pod``cargo test -p yoi-plugin-pdk``cargo check -p yoi``git diff --check``nix build .#yoi --no-link` が通る。
+10
View File
@@ -0,0 +1,10 @@
Decomposed into the Plugin objective and concrete implementation Tickets:
- 00001KVXK0WD3 Remove legacy raw WASM Plugin runtime
- 00001KVXK0WDH Reject legacy Plugin runtime in manifest and CLI diagnostics
- 00001KVXK0WDQ Define Plugin Service lifecycle and ingress queue runtime
- 00001KVXK0WDX Add Plugin service output command model
- 00001KVXK0WE4 Add host-owned WebSocket driver for Plugin services
- 00001KVXK0WEA Update Plugin WIT PDK templates for service event runtime
The broad redesign record is no longer an implementation work item.
+135
View File
@@ -0,0 +1,135 @@
<!-- event: create author: "yoi ticket" at: 2026-06-24T19:31:28Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: hare at: 2026-06-24T19:39:33Z -->
## Intake summary
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-24T19:39:33Z from: planning to: ready reason: cli_state field: state -->
## State changed
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: workspace-panel at: 2026-06-24T19:39:36Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-24T19:40:42Z -->
## Decision
Routing decision: return_to_planning
Reason:
- Dashboard Queue により人間が Orchestrator routing を許可した queued Ticket として確認した。
- Ticket body は Plugin WASM runtime の全面 redesign を要求しているが、acceptance criteria が legacy runtime removal、Component Model only 化、Tool/Service/Ingress runtime、host-managed WebSocket driver、event queue/backpressure/retry、output command/grant checks、Manifest/WIT/PDK/templates 更新、status diagnostics、docs/tests/Nix validation まで含んでおり、単一の実装 Ticket としては broad multi-surface effort になっている。
- bounded context check で既存 `docs/design/plugin-component-model.md``docs/development/plugin-development.md` を確認したところ、現行 docs/code direction は raw core-Wasm runtime を explicit compatibility/transitional support として残し、`PluginInstanceRegistry` で legacy raw-wasm / `yoi:plugin/tool@1.0.0` component packages を adapter する方針を記録している。これは本 Ticket の「compatibility alias / fallback を作らない」「legacy runtime を active runtime から削除」と衝突する可能性がある。
- `TicketRelationQuery` は blocking relation 0 件、`TicketOrchestrationPlanQuery` は既存 plan 0 件だったが、関連 docs/code の recorded direction と Ticket 要件の間に未解決の design decision が残っている。
- これは risk flag だけの問題ではなく、実装前に release/pre-release compatibility 方針、WIT/manifest/service event API、WebSocket driver execution model、legacy removal scope、分割順序を固定しないと coder が project-wide API を決めてしまう種類の不足である。
Evidence checked:
- Ticket body / thread: `00001KVXHVCR5``item.md`, `thread.md`。thread は create、planning->ready、ready->queued のみで、詳細な design decision / split plan / implementation order は記録されていない。
- Relations / orchestration plan: relation 0 件、plan 0 件。
- Docs/code context:
- `docs/design/plugin-component-model.md`: Component Model migration、legacy raw `wasm` explicit/transitional support、`PluginInstanceRegistry` adapter 方針、host API grant boundary。
- `docs/development/plugin-development.md`: `wasm-component` preferred runtime だが raw core-Wasm runtime が compatibility/transitional support として説明されている。
- Grep context: `LegacyToolAdapter`, `wasm-component`, `host_api.websocket`, Service/Ingress/PluginInstance 周辺が複数 file に分散。
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean、inprogress Ticket は 0 件、implementation worktree/branch for this Ticket は未作成。
Missing decision / information:
- この Ticket を「一括実装 Ticket」として進めるのか、Objective + concrete Tickets に分割するのか。
- Legacy raw `wasm` / `yoi-plugin-wasm-1` を即削除するか、現行 docs の transitional/adapter 方針を明示的に撤回するか。
- v0 で実装する exact surface: Tool runtime cleanup のみ、Service/Ingress event queue boundary、WebSocket driver、WIT/PDK/templates、diagnostics/status のどこまでを同一 Ticket の done condition に含めるか。
- `plugin.toml` / WIT / PDK の concrete schema/API shape と、現行 `host_api.websocket` pull API を reject/remove/deprecate/internal-only のどれにするか。
- Wasmtime/component/service runtime の binary size / Nix / sandbox policy / execution limits に関する validation scope。
Why implementation latitude is insufficient:
- Coder の local tactic selection で legacy compatibility policy や public/pre-release Plugin API direction を固定すると、manifest/WIT/PDK/templates/docs/tests すべての public-facing shape を一度に決めてしまう。
- Service/Ingress event runtime と WebSocket driver は execution model と host authority boundary の設計そのものであり、単なる file-local implementation detail ではない。
- 既存 docs に recorded direction があり、Ticket がそれを上書きするかどうかを実装者判断にすると project design authority を越える。
Next planning question/action:
- この Ticket を Objective/roadmap context として扱い、以下のような concrete Tickets に分割するかを決める。
1. Legacy raw `wasm` runtime removal / manifest rejection / docs fixture cleanup。
2. Service/Ingress event queue and PluginInstance lifecycle boundary。
3. Host-owned WebSocket driver design/implementation。
4. WIT/PDK/templates update for event/command model。
5. Plugin status/diagnostics overview。
- あるいは、まず design/planning artifact Ticket に変更し、exact v0 schema/API/non-goals を決めてから実装 Ticket を切る。
Next action:
- `queued -> planning` に戻し、Intake/planning で split plan または explicit v0 scope decision を作る。
- Coder/Reviewer Pod は起動しない。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T19:40:53Z from: queued to: planning reason: missing_plugin_runtime_design_scope_and_split_decision field: state -->
## State changed
Queued Ticket is returned to planning before any implementation side effect.
Concrete missing decisions/information:
- Whether this broad Plugin runtime redesign is a single implementation Ticket or should become Objective context plus concrete implementation Tickets.
- Whether current recorded transitional/adapter support for raw `wasm` / `yoi-plugin-wasm-1` is explicitly revoked before public release.
- Exact v0 scope for legacy removal, Service/Ingress event queue, WebSocket driver, WIT/PDK/templates, and diagnostics/status.
- Concrete manifest/WIT/PDK/API shape and how current `host_api.websocket` pull API is handled.
- Validation and packaging scope for component/service runtime changes.
Checked context:
- Ticket body/thread; relation 0 件; orchestration plan 0 件。
- `docs/design/plugin-component-model.md` and `docs/development/plugin-development.md`, which currently describe raw core-Wasm as explicit compatibility/transitional support and mention adapter behavior.
- Workspace state is clean and no implementation worktree/branch was created.
Why implementation latitude is insufficient:
- These choices fix project-wide Plugin API, manifest, WIT, PDK, runtime authority, and service execution semantics. They conflict with or supersede existing recorded design direction and are not safe as coder-local tactic choices.
Next planning action:
- Decide split plan / Objective context or convert this to an explicit design/planning artifact Ticket with a bounded v0 API decision before implementation routing.
---
<!-- event: state_changed author: hare at: 2026-06-24T19:55:18Z from: planning to: closed reason: closed field: state -->
## State changed
Ticket を closed にしました。
---
<!-- event: close author: hare at: 2026-06-24T19:55:18Z status: closed -->
## 完了
Decomposed into the Plugin objective and concrete implementation Tickets:
- 00001KVXK0WD3 Remove legacy raw WASM Plugin runtime
- 00001KVXK0WDH Reject legacy Plugin runtime in manifest and CLI diagnostics
- 00001KVXK0WDQ Define Plugin Service lifecycle and ingress queue runtime
- 00001KVXK0WDX Add Plugin service output command model
- 00001KVXK0WE4 Add host-owned WebSocket driver for Plugin services
- 00001KVXK0WEA Update Plugin WIT PDK templates for service event runtime
The broad redesign record is no longer an implementation work item.
---
@@ -0,0 +1 @@
{"id":"orch-plan-20260624-201247-1","ticket_id":"00001KVXK0WD3","kind":"accepted_plan","accepted_plan":{"summary":"Ticket `00001KVXK0WD3` は dependency chain の先頭で implementation_ready。専用 worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WD3-remove-legacy-wasm-runtime` と branch `work/00001KVXK0WD3-remove-legacy-wasm-runtime` で、Pod runtime 内の `LegacyToolAdapter` / raw-WASM active execution path を削除し、Component Model path を唯一の active execution path にする。Manifest/CLI diagnostics rejection は後続 `00001KVXK0WDH` の範囲に残す。","branch":"work/00001KVXK0WD3-remove-legacy-wasm-runtime","worktree":"/home/hare/Projects/yoi/.worktree/00001KVXK0WD3-remove-legacy-wasm-runtime","role_plan":"Orchestrator: accept/routing, worktree creation, final integration/validation/cleanup. Coder: remove active legacy raw-WASM runtime path in dedicated child worktree. Reviewer: read-only review focusing on preserving component Tool execution, grants, discovery/enablement/ToolRegistry, and not implementing manifest/CLI rejection slice."},"author":"yoi-orchestrator","at":"2026-06-24T20:12:47Z"}
+41
View File
@@ -0,0 +1,41 @@
---
title: 'Remove legacy raw WASM Plugin runtime'
state: 'done'
created_at: '2026-06-24T19:51:56Z'
updated_at: '2026-06-24T20:51:02Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-24T20:11:56Z'
---
## 背景
Plugin platform roadmap は、public release 前に raw core-Wasm runtime を compatibility bridge として残す方針を撤回し、`wasm-component` / Component Model を正の Plugin runtime として一本化する。未公開機能に legacy layer を残すと、Manifest / WIT / PDK / runtime authority / Service execution model の境界が曖昧になる。
この Ticket は最初の実装 slice として、Pod runtime 内の legacy raw `wasm` execution path と `LegacyToolAdapter` を削除する。Manifest validation / CLI diagnostics の外向き rejection は別 Ticket で扱う。
## 要件
- `PluginInstanceRuntime::LegacyToolAdapter` 相当の active execution path を削除する。
- raw core-Wasm tool execution を Plugin instance runtime の active path から外す。
- `wasm-component` runtime を Plugin execution の唯一の active runtime path とする。
- legacy path 削除に伴い、不要になった adapter-only tests / fixtures / helper を削除または component runtime 用に更新する。
- Plugin Tool execution は Component Model path で維持する。
- Host API grant boundary、package discovery、enablement、digest pinning、ToolRegistry 登録の既存挙動を壊さない。
## Non-goals
- Manifest / CLI diagnostics で legacy runtime を拒否する外向き UX の完成。
- Service / Ingress event queue の実装。
- WebSocket driver の実装。
- WIT / PDK / templates の service event model 更新。
- Public registry / install / update policy。
## 受け入れ条件
- `LegacyToolAdapter` または同等の legacy raw-Wasm adapter が active runtime から削除されている。
- raw `wasm` runtime を通じた Plugin Tool execution path が使われていない。
- Component Model Plugin Tool execution の既存 tests が通る。
- Legacy runtime 削除に伴う dead code / dead tests / obsolete fixtures が整理されている。
- Plugin discovery / enablement / Tool registration / grant validation の既存 tests が通る。
- `cargo test -p pod``cargo check -p yoi``git diff --check``nix build .#yoi --no-link` が通る。
+273
View File
@@ -0,0 +1,273 @@
<!-- event: create author: "yoi ticket" at: 2026-06-24T19:51:56Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: hare at: 2026-06-24T19:55:29Z -->
## Intake summary
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-24T19:55:29Z from: planning to: ready reason: cli_state field: state -->
## State changed
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: workspace-panel at: 2026-06-24T20:11:56Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-24T20:13:18Z -->
## Decision
Routing decision: implementation_ready
Reason:
- Dashboard Queue により人間が Orchestrator routing を許可した queued Ticket として確認した。
- Ticket body は legacy raw `wasm` runtime redesign のうち最初の concrete slice として、Pod runtime 内の `PluginInstanceRuntime::LegacyToolAdapter` 相当 active execution path を削除し、`wasm-component` / Component Model path を唯一の active runtime path にする範囲に限定している。
- Manifest / CLI diagnostics rejection、Service / Ingress event queue、WebSocket driver、WIT / PDK / templates は明示的に non-goal / 後続 Ticket に分割されている。
- `TicketRelationQuery` は 1 件で、この Ticket は後続 Ticket から参照される dependency chain の先頭であり、blocking outgoing dependency はない。
- `TicketOrchestrationPlanQuery` は routing 前 plan 0 件。accepted plan `orch-plan-20260624-201247-1` を記録済み。
- bounded context check で `crates/pod/src/feature/plugin.rs``LegacyToolAdapter` / raw `wasm` active path、`crates/manifest/src/plugin.rs``crates/yoi/src/plugin_cli.rs` の legacy manifest/diagnostic fixtures、docs の transitional runtime 記述を確認した。Ticket は manifest rejection を後続 Ticket に分けているため、残る不確実性は local implementation / test update に収まる。
Evidence checked:
- Ticket body / thread: `item.md`, `thread.md`。thread は create、planning->ready、ready->queued のみで未解決 blocker は記録されていない。
- Relations / orchestration plan: relation 1 件(後続 Ticket がこの Ticket に依存する view)、routing 前 plan 0 件。
- Code/docs context: `crates/pod/src/feature/plugin.rs``LegacyToolAdapter` / raw WASM handling、`crates/manifest/src/plugin.rs` / `crates/yoi/src/plugin_cli.rs` の legacy runtime constants/tests、Plugin docs の current transition notes。
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean。inprogress Ticket は 0 件。
- Queue context: 他の queued Plugin follow-up Tickets は dependency chain 上でこの Ticket の完了後に進める。
IntentPacket:
Intent:
- Pod Plugin runtime の active execution path から legacy raw core-WASM Tool adapter を削除し、Plugin Tool execution を Component Model runtime path に一本化する。
Binding decisions / invariants:
- この Ticket では active runtime execution path を整理する。Manifest / CLI の外向き rejection UX は後続 `00001KVXK0WDH` の範囲として残す。
- Component Model Plugin Tool execution、Host API grant boundary、package discovery、enablement、digest pinning、ToolRegistry registration は維持する。
- raw core-WASM path を compatibility fallback として active execution に残さない。
- Service / Ingress event runtime、WebSocket driver、WIT/PDK/templates service event update は実装しない。
- broad Plugin redesign や public registry/install/update policy に範囲を広げない。
Requirements / acceptance criteria:
- `PluginInstanceRuntime::LegacyToolAdapter` または同等の legacy raw-WASM adapter が active runtime から削除される。
- raw `wasm` runtime を通じた Plugin Tool execution path が使われない。
- Component Model Plugin Tool execution tests が通る。
- Legacy runtime 削除に伴う dead code / dead tests / obsolete fixtures が整理される。
- Discovery / enablement / Tool registration / grant validation の既存挙動が壊れない。
Implementation latitude:
- `PluginInstanceRuntime` enum の shape を単一 component path に畳むか、名前を残して variant を整理するかは既存 code style に合わせてよい。
- Manifest layer の legacy constants/tests は、active runtime removal に必要な最小限だけ調整してよい。ただし user-facing rejection/diagnostic completion は後続 Ticket に残す。
- Docs の compatibility/transitional wording は、active runtime removal と矛盾する部分を最小限更新してよい。
Escalate if:
- Component Model path だけでは既存 Tool execution / grant validation / ToolRegistry registration を維持できない。
- Manifest/CLI outward rejection を同時実装しないと build/test が成立しない。
- Wasmtime/component runtime limits or sandbox boundaries を再設計する必要がある。
- Service/Ingress/WebSocket/WIT/PDK の設計変更が必要になる。
Validation:
- `cargo test -p pod`
- `cargo check -p yoi`
- `git diff --check`
- `nix build .#yoi --no-link`
- 必要に応じて focused Plugin tests / `cargo test -p manifest` / `cargo test -p yoi plugin_cli`
Current code/docs map:
- Primary: `crates/pod/src/feature/plugin.rs`
- Secondary: `crates/manifest/src/plugin.rs`, `crates/yoi/src/plugin_cli.rs`, Plugin docs/templates only as needed for active runtime removal consistency。
- Avoid: full service runtime, WebSocket driver, WIT/PDK event model, remote plugin registry, root/original workspace operations。
Critical risks / reviewer focus:
- legacy raw-WASM execution path accidentally remains as fallback。
- Component Model Tool execution regression。
- host API grant / digest pinning / enablement / ToolRegistry registration regression。
- scope creep into manifest rejection or Service/Ingress runtime beyond this slice。
Next action:
- `queued -> inprogress` を記録してから worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WD3-remove-legacy-wasm-runtime` を作成し、multi-agent-workflow で Coder/Reviewer sibling loop に進める。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T20:14:08Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked field: state -->
## State changed
Orchestrator acceptance: queued -> inprogress
- 直前確認で `TicketShow` は state `queued`、blocking outgoing relation は 0 件、accepted plan `orch-plan-20260624-201247-1` を確認した。
- 後続 queued Tickets (`00001KVXK0WDH`, `00001KVXK0WDQ`, `00001KVXK0WDX`, `00001KVXK0WE4`, `00001KVXK0WEA`) は dependency chain 上この Ticket またはその後続 prerequisites に blocked として orchestration plan に記録済み。
- routing decision と IntentPacket は Ticket thread に記録済み。
- これ以降に worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WD3-remove-legacy-wasm-runtime` を作成し、multi-agent-workflow に接続する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T20:14:56Z -->
## Implementation report
Implementation orchestration update:
- queue review で dependency chain を確認し、`00001KVXK0WD3` のみを先頭 prerequisite として受理した。
- 後続 queued Tickets (`00001KVXK0WDH`, `00001KVXK0WDQ`, `00001KVXK0WDX`, `00001KVXK0WE4`, `00001KVXK0WEA`) は relation/orchestration plan 上の dependency blocker として待機理由を記録した。
- worktree-workflow に従い、implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WD3-remove-legacy-wasm-runtime` と branch `work/00001KVXK0WD3-remove-legacy-wasm-runtime` を作成した。
- child worktree では tracked `.yoi` project records を見える状態にし、`.yoi/memory`、local/runtime/log/lock/secret-like `.yoi` paths を sparse-checkout で除外する方針にした。
- Coder Pod `yoi-coder-00001KVXK0WD3-legacy-wasm-removal` を sibling として起動した。write scope は child worktree に限定し、runtime workspace root `/home/hare/Projects/yoi` は startup 要件を満たすため non-recursive read のみにした。
- Coder には root/original workspace と orchestration worktree で read/write/validation/git operation を行わないよう明示した。
---
<!-- event: implementation_report author: yoi-coder-00001KVXK0WD3-legacy-wasm-removal at: 2026-06-24T20:43:50Z -->
## Implementation report
Implementation report:
実装概要:
- `PluginInstanceRuntime::LegacyToolAdapter` 相当の raw core-WASM fallback 実行経路を削除した。
- `wasm-component` の Tool world は `ComponentToolAdapter` として明示的に扱い、ToolRegistry 経由の実行は `run_plugin_component_tool` のみを使うよう変更した。
- raw `wasm` runtime metadata は static inspection / instance startup の両方で非対応として扱い、実行 fallback しないようにした。
- Component Model Plugin Tool / Component Instance 実行経路、host API grant boundary、ToolRegistry 登録経路は維持した。
- raw WASM 実行用の `wasmi` 依存・テスト・古い helper を削除した。
- `package.nix``cargoHash` を更新した。
- Component Model design doc の現状記述を active runtime に合わせて更新した。
変更ファイル:
- `crates/pod/src/feature/plugin.rs`
- `crates/pod/Cargo.toml`
- `Cargo.lock`
- `package.nix`
- `docs/design/plugin-component-model.md`
Validation:
- `cargo test -p pod feature::plugin --lib`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- `cargo test -p pod`: failed in unrelated prompt snapshot tests:
- `workflow::tests::workflow_usage_guidance_contract_snapshot_matches_prompt_resource`
- `tests::companion_prompt_matches_resource_snapshot`
Commit:
- `741d71327aec213e564d47a4b4c37141c727803c refactor: remove legacy wasm plugin runtime`
Residual risks / notes:
- Full `cargo test -p pod` failure appears unrelated to this Plugin runtime change and was not fixed here.
- Follow-up Ticket `00001KVXK0WDH` remains responsible for manifest / CLI legacy runtime rejection UX. This implementation only removes active runtime fallback and minimal static inspection consistency.
---
<!-- event: review author: yoi-reviewer-00001KVXK0WD3-legacy-wasm-removal at: 2026-06-24T20:48:55Z status: approve -->
## Review: approve
Review result: approve
確認範囲:
- child worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WD3-remove-legacy-wasm-runtime` のみで read-only inspection。
- Ticket / IntentPacket / `1b5a39db..HEAD` の diff を確認。
- reviewer では cargo/nix は再実行していない。`git diff --check 1b5a39db..HEAD` は問題なし。
承認根拠:
- Ticket / IntentPacket の核心である「Pod Plugin runtime の active execution path から legacy raw core-WASM Tool adapter を削除し、Component Model path に一本化」が満たされている。
- `crates/pod/src/feature/plugin.rs` から `LegacyToolAdapter` / `PluginWasmTool` / `run_plugin_wasm_tool` / `wasmi` host adapter 系が削除され、`crates/pod/Cargo.toml` からも `wasmi` dependency が削除されている。
- raw `wasm` runtime は static inspection で `eligible: false` かつ active execution path ではない diagnostic になり、`PluginInstanceRuntime::new` でも `PLUGIN_RUNTIME_WASM_KIND` が fail-closed するため、fallback 実行は残っていない。
- Component Model Tool path は `ComponentToolAdapter -> run_plugin_component_tool -> read_resolved_plugin_runtime_component -> wasmtime::component` に整理され、ToolRegistry 登録も既存の `ToolContribution` / `plugin_instance_tool_definition` 経由を維持している。
- Host API grant / digest binding / tool permission validation は `authorize_plugin_tool``authorize_plugin_host_api``validate_component_imports` 側で維持され、component import permission/grant check tests も残っている。
- Manifest / CLI rejection UX には踏み込んでおらず、raw `wasm` constants / manifest parsing が残っているのは後続 Ticket `00001KVXK0WDH` の範囲として許容できる。
- Service / Ingress / WebSocket / WIT / PDK runtime への不要な scope creep は見当たらない。
Non-blocking notes:
- 報告された `cargo test -p pod` 全体失敗は prompt snapshot 2 件で、今回 diff の変更対象外に見えるため blocker とは判断しない。ただし完了処理時には既知 unrelated failure として扱う根拠を残すか、別対応後に再実行するのが安全。
- `docs/development/plugin-development.md` には raw core-Wasm compatibility がまだ有効に読める文言が残っている。今回 Ticket の主目的は active runtime removal で、外向き rejection/docs 全面更新は後続範囲なので blocker ではないが、後続 Ticket で整理した方がよい。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T20:50:37Z -->
## Implementation report
Integration outcome:
- Coder `yoi-coder-00001KVXK0WD3-legacy-wasm-removal` が implementation branch `work/00001KVXK0WD3-remove-legacy-wasm-runtime` に実装を commit した。
- implementation commit: `741d7132 refactor: remove legacy wasm plugin runtime`
- Reviewer `yoi-reviewer-00001KVXK0WD3-legacy-wasm-removal` は read-only review で `approve`。legacy raw-WASM fallback execution path が削除され、Component Model Tool path、host API grant、digest binding、ToolRegistry registration が維持され、manifest/CLI rejection や Service/Ingress/WebSocket/WIT/PDK へ scope creep していないことを確認した。
- Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration` で branch `work/00001KVXK0WD3-remove-legacy-wasm-runtime` を merge 済み。
- merge commit: `29953111 merge: 00001KVXK0WD3 legacy wasm removal`
Implemented behavior:
- `crates/pod/src/feature/plugin.rs` から `LegacyToolAdapter` / raw core-WASM Tool execution helper / `wasmi` host adapter 系を削除。
- raw `wasm` runtime は static inspection と instance startup で fail-closed / non-executable diagnostic になり、active fallback execution path は残していない。
- Component Model Tool runtime path は `ComponentToolAdapter` / `run_plugin_component_tool` / `wasmtime::component` に整理。
- `crates/pod/Cargo.toml` から `wasmi` dependency を削除し、`Cargo.lock``package.nix` cargoHash を更新。
- `docs/design/plugin-component-model.md` の transitional runtime wording を active runtime removal に合わせて最小更新。
Validation in Orchestrator worktree:
- `cargo test -p pod feature::plugin --lib`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- `cargo test -p pod`: failed in two prompt guidance snapshot assertions that are outside this Plugin diff:
- `prompt::catalog::tests::pod_orchestration_guidance_section_renders_resource_body`
- `prompt::system::tests::pod_orchestration_guidance_is_included_for_pod_management_tools`
- Plugin focused tests within the same full run passed, including `core_wasm_is_not_silently_reinterpreted_as_component`, component execution, component grant/import, lifecycle, memory/table/output cap tests.
Notes:
- Full `cargo test -p pod` failure is recorded as an existing/unrelated prompt-resource assertion issue and not treated as this Ticket blocker because changed files do not touch prompt resources or those test modules, focused Plugin tests pass, reviewer approved, and `nix build .#yoi --no-link` succeeds.
- Follow-up `00001KVXK0WDH` remains responsible for external manifest/CLI rejection diagnostics and broader docs cleanup such as `docs/development/plugin-development.md` compatibility wording.
Next action:
- Mark Ticket done after this integration/validation evidence.
- Then stop related child Pods and remove only the child implementation worktree/branch.
- Dependency-unblocked queued Ticket `00001KVXK0WDH` can be re-routed next.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T20:50:45Z from: inprogress to: done reason: merged_validated_with_unrelated_full_suite_caveat field: state -->
## State changed
Implementation is merged into the orchestration branch and validated with one unrelated existing full-suite caveat.
Evidence:
- merge commit: `29953111 merge: 00001KVXK0WD3 legacy wasm removal`
- reviewer result: approve
- validation in `/home/hare/Projects/yoi/.worktree/orchestration`:
- `cargo test -p pod feature::plugin --lib`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- `cargo test -p pod`: failed only in prompt guidance snapshot assertions outside this Plugin diff; focused Plugin tests in the same run passed.
Closure is not performed here; this state records implementation completion after merge/review/focused validation.
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T20:51:02Z -->
## Implementation report
Correction:
- 直前の integration outcome / state-change body に記載した merge commit `29953111` は誤記。
- 正しい merge commit は `bedbb670 merge: 00001KVXK0WD3 legacy wasm removal`
- 実装 commit `741d7132`、review approve、validation results、Ticket done 判断には変更なし。
---
@@ -0,0 +1,2 @@
{"id":"orch-plan-20260624-201335-1","ticket_id":"00001KVXK0WDH","kind":"blocked_by","related_ticket":"00001KVXK0WD3","note":"Queue review: `00001KVXK0WDH` は manifest/CLI rejection slice だが、active legacy runtime path removal `00001KVXK0WD3` に depends_on している。`00001KVXK0WD3` を先に受理し、この Ticket は dependency completion 後に再 routing する。","author":"yoi-orchestrator","at":"2026-06-24T20:13:35Z"}
{"id":"orch-plan-20260624-205226-2","ticket_id":"00001KVXK0WDH","kind":"accepted_plan","accepted_plan":{"summary":"Ticket `00001KVXK0WDH` は prerequisite `00001KVXK0WD3` が done になったため implementation_ready。専用 worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WDH-plugin-manifest-reject-legacy` と branch `work/00001KVXK0WDH-plugin-manifest-reject-legacy` で、legacy raw `wasm` / `yoi-plugin-wasm-1` manifest/CLI diagnostics を拒否・整理する。Service/Ingress/WebSocket/WIT/PDK runtime work は後続 Tickets に残す。","branch":"work/00001KVXK0WDH-plugin-manifest-reject-legacy","worktree":"/home/hare/Projects/yoi/.worktree/00001KVXK0WDH-plugin-manifest-reject-legacy","role_plan":"Orchestrator: accept/routing, worktree creation, final integration/validation/cleanup. Coder: manifest/CLI/docs diagnostics implementation in dedicated child worktree. Reviewer: read-only review focusing on clear legacy rejection, component package non-regression, no runtime-scope creep."},"author":"yoi-orchestrator","at":"2026-06-24T20:52:26Z"}
@@ -0,0 +1,13 @@
{
"version": 1,
"relations": [
{
"ticket_id": "00001KVXK0WDH",
"kind": "depends_on",
"target": "00001KVXK0WD3",
"note": "Manifest/CLI rejection should follow removal of the active legacy runtime path.",
"author": "yoi ticket",
"at": "2026-06-24T19:55:30Z"
}
]
}
+41
View File
@@ -0,0 +1,41 @@
---
title: 'Reject legacy Plugin runtime in manifest and CLI diagnostics'
state: 'done'
created_at: '2026-06-24T19:51:56Z'
updated_at: '2026-06-24T21:20:45Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-24T20:11:58Z'
---
## 背景
Plugin runtime は public release 前に `wasm-component` へ一本化する。runtime implementation から legacy raw `wasm` path を削除しても、Manifest parser / validator / `yoi plugin check/list/show` が legacy runtime を曖昧に受け付けると、ユーザー向け API と実装方針がズレる。
この Ticket では、legacy runtime を外向き schema / diagnostics でも明示的に拒否し、docs / fixtures から transitional wording を消す。
## 要件
- `plugin.toml` の runtime validation で legacy raw `wasm` / `yoi-plugin-wasm-1` を新規 Plugin runtime として受け付けない。
- `runtime.kind = "wasm-component"` を正の runtime kind として扱う。
- `yoi plugin check` は legacy runtime を invalid / unsupported として分かりやすく報告する。
- `yoi plugin list/show` は legacy package を有効 Plugin として曖昧に表示しない。
- docs/design / docs/development / templates / examples から「raw core-Wasm compatibility bridge を残す」前提の記述を削除または撤回済み方針に更新する。
- Manifest schema / static inspection / error message tests を更新する。
## Non-goals
- Runtime internal の legacy adapter 削除そのもの。
- Service / Ingress event runtime の実装。
- WebSocket driver の実装。
- PDK API の全面更新。
## 受け入れ条件
- Legacy raw `wasm` runtime manifest が validation で拒否される。
- `yoi-plugin-wasm-1` ABI は current public/recommended Plugin API として表示されない。
- `yoi plugin check/list/show` が legacy runtime package を安全かつ明確に diagnostic する。
- Component Model package の check/list/show は regress しない。
- Plugin objective / design / development docs が Component Model only 方針と整合している。
- Legacy runtime 前提の fixtures が削除または rejected fixture として更新されている。
- `cargo test -p manifest``cargo test -p yoi``cargo check -p yoi``git diff --check``nix build .#yoi --no-link` が通る。
+306
View File
@@ -0,0 +1,306 @@
<!-- event: create author: "yoi ticket" at: 2026-06-24T19:51:56Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: hare at: 2026-06-24T19:55:29Z -->
## Intake summary
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-24T19:55:29Z from: planning to: ready reason: cli_state field: state -->
## State changed
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: workspace-panel at: 2026-06-24T20:11:58Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-24T20:52:56Z -->
## Decision
Routing decision: implementation_ready
Reason:
- Dashboard Queue により人間が Orchestrator routing を許可した queued Ticket として確認した。
- この Ticket は legacy Plugin runtime redesign chain の 2 番目の concrete slice で、manifest validation / CLI diagnostics / docs cleanup に範囲が限定されている。
- outgoing `depends_on``00001KVXK0WD3` だが、`00001KVXK0WD3` は done / merged / reviewed / validated 済み。`TicketShow` derived blockers は空で、implementation acceptance blocker は残っていない。
- incoming dependent `00001KVXK0WDQ` はこの Ticket 完了後に進めるべき後続であり、この Ticket の acceptance blocker ではない。
- `TicketOrchestrationPlanQuery` には以前の `blocked_by 00001KVXK0WD3` があるが、prerequisite 完了により解消済みとして扱い、accepted plan `orch-plan-20260624-205226-2` を記録した。
- bounded context check で current orchestration branch の `crates/manifest/src/plugin.rs`, `crates/yoi/src/plugin_cli.rs`, `docs/development/plugin-development.md`, `docs/design/plugin-component-model.md`, `docs/design/plugin-packages.md` 周辺に raw `wasm` / `yoi-plugin-wasm-1` / transitional wording が残っていることを確認した。Ticket の scope はこれらの outward schema/diagnostic/docs 整理として十分に具体的。
Evidence checked:
- Ticket body / thread: `item.md`, `thread.md`。未解決 planning question は記録されていない。
- Relations / orchestration plan: outgoing depends_on `00001KVXK0WD3` は done。incoming dependent `00001KVXK0WDQ` は後続。
- Related Ticket: `00001KVXK0WD3` は done。active legacy runtime fallback removal completed with corrected merge commit `bedbb670`
- Code/docs context: `crates/manifest/src/plugin.rs`, `crates/yoi/src/plugin_cli.rs`, `docs/development/plugin-development.md`, `docs/design/plugin-component-model.md`, `docs/design/plugin-packages.md`
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean。inprogress Ticket は 0 件。
IntentPacket:
Intent:
- Legacy raw `wasm` / `yoi-plugin-wasm-1` Plugin runtime を external manifest/schema/CLI/docs surface でも rejected/retired として扱い、Component Model `wasm-component` を正の public/recommended runtime に一本化する。
Binding decisions / invariants:
- `00001KVXK0WD3` の active runtime removal を前提にする。raw-WASM execution fallback を戻さない。
- `runtime.kind = "wasm-component"` が positive accepted runtime kind。
- legacy raw `wasm` / `yoi-plugin-wasm-1` は new/current Plugin package として validation/inspection/check/list/show 上曖昧に active 表示しない。
- Component Model package の `check/list/show`、package discovery、digest, grants, Tool schema diagnostics は regress させない。
- Service / Ingress runtime、WebSocket driver、WIT/PDK/templates service event update は実装しない。
Requirements / acceptance criteria:
- Legacy raw `wasm` runtime manifest が validation/check で明確に rejected/unsupported になる。
- `yoi plugin check` は legacy package を invalid/unsupported として bounded diagnostic し、exit behavior が current check semantics と整合する。
- `yoi plugin list/show` は legacy package を有効/active Plugin として曖昧に表示しない。
- Component Model packages の check/list/show は通る。
- docs/design / docs/development / templates/examples から raw core-Wasm compatibility bridge 前提を削除または撤回済み方針に更新する。
- Manifest schema / static inspection / error message tests を更新する。
Implementation latitude:
- `PLUGIN_RUNTIME_WASM_KIND` / `PLUGIN_RUNTIME_WASM_ABI` constants を削除するか、legacy rejection diagnostic 用に internal-only に残すかは code style と tests に合わせてよい。
- Legacy fixture は削除または rejected fixture に変換してよい。
- CLI human/json diagnostic wording は bounded and clear であれば具体文言は coder が選んでよい。
Escalate if:
- Manifest parser cannot reject legacy runtime without breaking component packages。
- Plugin list/show architecture cannot represent rejected package diagnostics without broader API redesign。
- Implementing rejection requires Service/Ingress/WebSocket/WIT/PDK changes。
- Existing closed Ticket constraints force raw core-Wasm compatibility to remain externally supported。
Validation:
- `cargo test -p manifest`
- `cargo test -p yoi`
- `cargo check -p yoi`
- `git diff --check`
- `nix build .#yoi --no-link`
- Add/run focused plugin CLI/check/list/show tests as appropriate。
Current code/docs map:
- Primary: `crates/manifest/src/plugin.rs`, `crates/yoi/src/plugin_cli.rs`
- Secondary: `docs/development/plugin-development.md`, `docs/design/plugin-component-model.md`, `docs/design/plugin-packages.md`, plugin templates/examples only as needed。
- Avoid: `crates/pod` active runtime implementation unless minimal diagnostic alignment is needed; Service/Ingress/WebSocket/WIT/PDK event model。
Critical risks / reviewer focus:
- legacy package still appears active/eligible in CLI inspection。
- component package check/list/show regression。
- raw-WASM execution fallback being reintroduced。
- diagnostic wording that implies compatibility support remains。
- scope creep into later service runtime Tickets。
Next action:
- `queued -> inprogress` を記録してから worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WDH-plugin-manifest-reject-legacy` を作成し、multi-agent-workflow で Coder/Reviewer sibling loop に進める。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T20:53:45Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked field: state -->
## State changed
Orchestrator acceptance: queued -> inprogress
- 直前確認で `TicketShow` は state `queued`、derived blockers は空。
- outgoing dependency `00001KVXK0WD3` は done / merged / reviewed / validated 済み。
- accepted plan `orch-plan-20260624-205226-2` を確認した。
- routing decision と IntentPacket は Ticket thread に記録済み。
- これ以降に worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WDH-plugin-manifest-reject-legacy` を作成し、multi-agent-workflow に接続する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T20:54:43Z -->
## Implementation report
Implementation orchestration update:
- prerequisite `00001KVXK0WD3` が done になったため、`00001KVXK0WDH` を再 routing して受理した。
- worktree-workflow に従い、implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WDH-plugin-manifest-reject-legacy` と branch `work/00001KVXK0WDH-plugin-manifest-reject-legacy` を作成した。
- child worktree では tracked `.yoi` project records を見える状態にし、`.yoi/memory`、local/runtime/log/lock/secret-like `.yoi` paths を sparse-checkout で除外する方針にした。
- Coder Pod `yoi-coder-00001KVXK0WDH-plugin-manifest-reject` を sibling として起動した。write scope は child worktree に限定し、runtime workspace root `/home/hare/Projects/yoi` は startup 要件を満たすため non-recursive read のみにした。
- Coder には root/original workspace と orchestration worktree で read/write/validation/git operation を行わないよう明示した。
---
<!-- event: implementation_report author: yoi-coder-00001KVXK0WDH-plugin-manifest-reject at: 2026-06-24T21:11:58Z -->
## Implementation report
Implementation report:
対象 Ticket:
- `00001KVXK0WDH` — Reject legacy Plugin runtime in manifest and CLI diagnostics
作業ブランチ / Worktree:
- Worktree: `/home/hare/Projects/yoi/.worktree/00001KVXK0WDH-plugin-manifest-reject-legacy`
- Branch: `work/00001KVXK0WDH-plugin-manifest-reject-legacy`
変更内容:
- `crates/manifest/src/plugin.rs`
- legacy raw `wasm` / `yoi-plugin-wasm-1` runtime を manifest validation で reject。
- `wasm-component` を現行 public runtime としてドキュメントコメントを更新。
- raw-WASM runtime module 読み込み helper を削除。
- legacy manifest rejection のテストを追加。
- `crates/yoi/src/plugin_cli.rs`
- CLI テスト fixture を `wasm-component` package へ移行。
- `yoi plugin check` が legacy raw-WASM package を `rejected` として返すテストを追加。
- `list/show` inspection 上で legacy package が active/eligible 表示されないことをテスト。
- `crates/pod/src/feature/plugin.rs`
- public manifest constant 依存を外し、legacy runtime 名は内部診断用 const に限定。
- 既存の runtime fallback rejection 経路を維持。
- `docs/development/plugin-development.md`
- raw-WASM runtime を「互換/推奨」ではなく retired/rejected として記述更新。
- `wasm-component` を supported/public runtime として明示。
- `docs/design/plugin-component-model.md`
- Component Model runtime を現行 supported runtime として更新。
- raw-WASM は bounded diagnostic 用の rejected metadata として整理。
- `docs/design/plugin-packages.md`
- package runtime 記述を `wasm-component` 中心に更新。
- legacy raw-WASM package は active/eligible ではなく rejected として表示されることを明記。
Validation:
- `cargo fmt --check`: success
- `cargo test -p manifest`: success
- `cargo test -p yoi`: success
- `cargo check -p yoi`: success
- `cargo test -p pod legacy_raw_wasm_runtime_is_rejected_without_fallback_execution`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- dirty tree warning は未コミット検証時点の通常 warning。ビルド自体は成功。
Commit:
- `6086099fe4807b5b12c8d13333f5544a4174e3d9 feat: reject legacy plugin runtime manifests`
Residual risks / notes:
- 既知の残リスクなし。
- raw-WASM execution fallback は再導入していない。
- legacy runtime 名は manifest/pod 内部の rejected diagnostic fixture/判定用としてのみ残している。
- Service/Ingress runtime、WebSocket driver、WIT/PDK/templates の service event 更新、broader runtime redesign は実施していない。
---
<!-- event: review author: yoi-reviewer-00001KVXK0WDH-plugin-manifest-reject at: 2026-06-24T21:18:53Z status: approve -->
## Review: approve
Review result: approve
確認範囲:
- child worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WDH-plugin-manifest-reject-legacy`
- branch `work/00001KVXK0WDH-plugin-manifest-reject-legacy`
- HEAD `6086099fe4807b5b12c8d13333f5544a4174e3d9`
- 実履歴上の親 `ef1d8d9a` → HEAD の差分をレビューした。指定 base `187e6426` はこの worktree では object missing だったが、HEAD の直前 commit は `ticket: accept plugin manifest rejection task` で、レビュー対象差分として妥当と判断した。
承認根拠:
- Ticket / IntentPacket の要件と照合済み。
- `crates/manifest/src/plugin.rs`:
- legacy `wasm` / `yoi-plugin-wasm-1` は public const ではなく internal rejection 用 const になっている。
- `validate_manifest``kind = "wasm"``legacy raw wasm ... retired; use wasm-component` として `Api/Manifest` diagnostic 付きで拒否している。
- `wasm-component` は正の runtime kind として `component` / `world` validation を維持している。
- legacy rejection test が追加され、discovery package には入らず diagnostic になることを確認している。
- `crates/yoi/src/plugin_cli.rs`:
- `plugin check` は manifest/discovery rejection を `status: "rejected"` として bounded diagnostic に載せ、既存 check semantics 通り rejected で Err を返す。
- list/show snapshot は discovery/resolution diagnostics から rejected item を構成し、legacy package を active/eligible として扱わない。
- component fixture は `wasm-component` / `plugin.component.wasm` に更新され、legacy fixture は rejected fixture に変換されている。
- `crates/pod/src/feature/plugin.rs`:
- manifest crate の legacy runtime public const 依存は削除済み。
- legacy name は internal diagnostic/rejection matching のみ。
- `PluginInstanceRuntime::new` は legacy `wasm` を即エラーにし、raw-WASM fallback 実行は戻っていない。
- docs:
- `docs/development/plugin-development.md`
- `docs/design/plugin-component-model.md`
- `docs/design/plugin-packages.md`
で raw core-Wasm compatibility 前提は撤回され、`wasm-component` が supported/public runtime と明記されている。
- `rg 'plugin\.wasm|kind = "wasm"|abi = "yoi-plugin-wasm-1"'` で残存箇所を確認。残りは rejection docs/tests/diagnostics と component build artifact 名の文脈のみで、active public compatibility 表現は見当たらない。
- 差分対象は manifest / CLI / pod diagnostic alignment / docs の 6 files に限定され、Service/Ingress/WebSocket/WIT/PDK runtime redesign への scope creep は確認していない。
Validation:
- reviewer は read-only 制約のため `cargo test` / `cargo check` / `nix build` は再実行していない。
- reviewer 側で `git diff --check HEAD^..HEAD`: success。
- Coder reported validation (`cargo fmt --check`, `cargo test -p manifest`, `cargo test -p yoi`, `cargo check -p yoi`, focused pod test, `nix build .#yoi --no-link`) は実装内容と整合しており、追加で疑う材料はない。
Non-blocking note:
- 親から指定された review base hash `187e6426` は誤記/存在しない hash だった。実レビューは child branch の実 parent `ef1d8d9a` を base として行われた。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T21:20:36Z -->
## Implementation report
Integration outcome:
- Coder `yoi-coder-00001KVXK0WDH-plugin-manifest-reject` が implementation branch `work/00001KVXK0WDH-plugin-manifest-reject-legacy` に実装を commit した。
- implementation commit: `6086099f feat: reject legacy plugin runtime manifests`
- Reviewer `yoi-reviewer-00001KVXK0WDH-plugin-manifest-reject` は read-only review で `approve`。legacy `wasm` / `yoi-plugin-wasm-1` manifest rejection、CLI check/list/show diagnostic behavior、component package non-regression、pod diagnostic alignment、docs update、scope creep なしを確認した。
- Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration` で branch `work/00001KVXK0WDH-plugin-manifest-reject-legacy` を merge 済み。
- merge commit: `449745ee merge: 00001KVXK0WDH plugin manifest rejection`
Implemented behavior:
- `crates/manifest/src/plugin.rs`:
- legacy raw `wasm` / `yoi-plugin-wasm-1` runtime を manifest validation で rejected diagnostic にする。
- `wasm-component` を supported public runtime として維持。
- `crates/yoi/src/plugin_cli.rs`:
- `yoi plugin check` は legacy package を `status: "rejected"` として bounded diagnostic に載せ、既存 check semantics 通り失敗扱いにする。
- list/show fixture は legacy package を active/eligible として表示しない。
- `crates/pod/src/feature/plugin.rs`:
- legacy runtime public constant dependency を外し、legacy runtime name は internal diagnostic/rejection matching のみに限定。
- raw-WASM fallback execution は再導入していない。
- docs:
- `docs/development/plugin-development.md`, `docs/design/plugin-component-model.md`, `docs/design/plugin-packages.md` で raw core-Wasm compatibility 前提を撤回し、`wasm-component` を supported/public runtime と明記。
Validation in Orchestrator worktree:
- `cargo fmt --check`: success
- `cargo test -p manifest`: success
- `cargo test -p yoi`: success
- `cargo check -p yoi`: success
- `cargo test -p pod legacy_raw_wasm_runtime_is_rejected_without_fallback_execution --lib`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
Notes:
- Reviewer non-blocking note: Orchestrator から渡した review base `187e6426` は誤記で、child branch の実 parent `ef1d8d9a` を base として review された。review scope は適切だった。
- Follow-up `00001KVXK0WDQ` は this Ticket completion により dependency-unblocked and can be re-routed next.
Next action:
- Mark Ticket done after this integration/validation evidence.
- Then stop related child Pods and remove only the child implementation worktree/branch.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T21:20:45Z from: inprogress to: done reason: merged_validated field: state -->
## State changed
Implementation is merged into the orchestration branch and validated.
Evidence:
- merge commit: `449745ee merge: 00001KVXK0WDH plugin manifest rejection`
- reviewer result: approve
- validation in `/home/hare/Projects/yoi/.worktree/orchestration` succeeded:
- `cargo fmt --check`
- `cargo test -p manifest`
- `cargo test -p yoi`
- `cargo check -p yoi`
- `cargo test -p pod legacy_raw_wasm_runtime_is_rejected_without_fallback_execution --lib`
- `git diff --check`
- `nix build .#yoi --no-link`
Closure is not performed here; this state records implementation completion after merge/validation.
---
@@ -0,0 +1,2 @@
{"id":"orch-plan-20260624-201335-1","ticket_id":"00001KVXK0WDQ","kind":"blocked_by","related_ticket":"00001KVXK0WDH","note":"Queue review: `00001KVXK0WDQ` は Service lifecycle / ingress queue runtime slice だが、Component Model-only runtime authority / manifest rejection slice `00001KVXK0WDH` に depends_on している。prerequisite completion 後に再 routing する。","author":"yoi-orchestrator","at":"2026-06-24T20:13:35Z"}
{"id":"orch-plan-20260624-212209-2","ticket_id":"00001KVXK0WDQ","kind":"accepted_plan","accepted_plan":{"summary":"Ticket `00001KVXK0WDQ` は prerequisites `00001KVXK0WD3` と `00001KVXK0WDH` が done になったため implementation_ready。専用 worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WDQ-plugin-service-lifecycle` と branch `work/00001KVXK0WDQ-plugin-service-lifecycle` で、Plugin Service lifecycle と ingress queue runtime を実装する。Output command model / WebSocket driver / WIT-PDK templates は後続 Tickets に残す。","branch":"work/00001KVXK0WDQ-plugin-service-lifecycle","worktree":"/home/hare/Projects/yoi/.worktree/00001KVXK0WDQ-plugin-service-lifecycle","role_plan":"Orchestrator: accept/routing, worktree creation, final integration/validation/cleanup. Coder: implement service lifecycle and ingress queue runtime in dedicated child worktree. Reviewer: read-only review focusing on bounded in-process event queue, no WebSocket driver/output command scope creep, grant/capability boundaries, and component Tool regression."},"author":"yoi-orchestrator","at":"2026-06-24T21:22:09Z"}
@@ -0,0 +1,13 @@
{
"version": 1,
"relations": [
{
"ticket_id": "00001KVXK0WDQ",
"kind": "depends_on",
"target": "00001KVXK0WDH",
"note": "Service runtime should build on Component Model-only plugin runtime authority.",
"author": "yoi ticket",
"at": "2026-06-24T19:55:30Z"
}
]
}
+46
View File
@@ -0,0 +1,46 @@
---
title: 'Define Plugin Service lifecycle and ingress queue runtime'
state: 'done'
created_at: '2026-06-24T19:51:56Z'
updated_at: '2026-06-24T21:51:13Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-24T20:12:00Z'
---
## 背景
Current Plugin instance support exposes `start` / `handle-ingress` / `status` / `stop`, but the execution model is still effectively synchronous function invocation. Service Plugins need a host-managed lifecycle and event queue so `start()` can return promptly, while external events are later delivered to `handle-ingress` without requiring a Plugin-owned polling loop.
This Ticket implements the first service runtime slice: lifecycle state, ingress queue, serial dispatch, backpressure, timeout, and failure semantics. WebSocket-specific event sources and output commands are handled by later Tickets.
## 要件
- Service Plugin lifecycle を host-managed state として扱う。
- ready / starting / running / stopping / stopped / failed 相当。
- `start()` は initialization only とし、long-running loop / polling loop / recv loop を担わない。
- Service Plugin ごとに bounded ingress queue を持つ。
- Ingress event は source / ingress name / payload / created_at / attempt / correlation id を持つ。
- v0 dispatch は per-plugin serial dispatch とする。
- dispatch timeout、Plugin failure、queue full、invalid event、stop 中 event の扱いを typed にする。
- queue / lifecycle / failure state は status diagnostics として取得できる。
- Existing Tool Plugin execution は service queue に巻き込まず、request-response operation として維持する。
## Non-goals
- WebSocket connection driver の実装。
- Plugin output command model の本実装。
- Discord / Slack など特定 integration。
- Concurrent per-plugin event execution。
- Durable cross-process event queue。
## 受け入れ条件
- Service Plugin instance が host-managed lifecycle state を持つ。
- `start()` が返った後でも ingress event を queue 経由で配送できる。
- Queue は bounded で、full / timeout / failed service が typed error / diagnostic として扱われる。
- 同一 Plugin instance への ingress dispatch は serial に処理される。
- `status()` または host diagnostics から lifecycle state / queue depth / last error / dispatch counters を確認できる。
- Service lifecycle / ingress queue の unit tests が追加されている。
- Tool Plugin execution tests が regress しない。
- `cargo test -p pod``cargo check -p yoi``git diff --check``nix build .#yoi --no-link` が通る。
+303
View File
@@ -0,0 +1,303 @@
<!-- event: create author: "yoi ticket" at: 2026-06-24T19:51:56Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: hare at: 2026-06-24T19:55:29Z -->
## Intake summary
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-24T19:55:29Z from: planning to: ready reason: cli_state field: state -->
## State changed
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: workspace-panel at: 2026-06-24T20:12:00Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-24T21:22:37Z -->
## Decision
Routing decision: implementation_ready
Reason:
- Dashboard Queue により人間が Orchestrator routing を許可した queued Ticket として確認した。
- `00001KVXK0WDQ` は Service lifecycle / ingress queue runtime の concrete slice で、WebSocket driver、output command model、WIT/PDK/templates update を non-goal として後続に分離している。
- outgoing `depends_on``00001KVXK0WDH` だが、`00001KVXK0WDH` は done / merged / reviewed / validated 済み。`TicketShow` derived blockers は空で、implementation acceptance blocker は残っていない。
- incoming dependents (`00001KVXK0WDX`, `00001KVXK0WE4`) はこの Ticket 完了後に進めるべき後続であり、この Ticket の acceptance blocker ではない。
- bounded context check で `crates/pod/src/feature/plugin.rs` の current `PluginInstanceRegistry`, `PluginInstanceHandle`, `PluginIngressEvent`, `PluginIngressDispatchReport`, `ComponentInstanceRuntime` lifecycle methods を確認した。Ticket は in-process service lifecycle / bounded ingress queue / serial dispatch / diagnostics に収まっており、残る不確実性は local implementation に閉じる。
Evidence checked:
- Ticket body / thread: `item.md`, `thread.md`。未解決 planning question は記録されていない。
- Relations / orchestration plan: outgoing depends_on `00001KVXK0WDH` は done。routing 前 plan は historical blocked_by `00001KVXK0WDH` のみで、prerequisite 完了により解消済み。accepted plan `orch-plan-20260624-212209-2` を記録済み。
- Related Tickets: `00001KVXK0WD3` / `00001KVXK0WDH` は done。
- Code context: `crates/pod/src/feature/plugin.rs` の service/ingress registration, instance registry, start/status/stop/handle-ingress, component runtime tests。
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean。inprogress Ticket は 0 件。
IntentPacket:
Intent:
- Plugin Service を host-managed lifecycle と bounded ingress queue を持つ in-process runtime として扱い、`start()` を initialization-only にし、後続 ingress events を queue 経由で serial dispatch できるようにする。
Binding decisions / invariants:
- Existing Tool Plugin execution は request-response operation として維持し、service queue に巻き込まない。
- v0 dispatch は per-plugin serial dispatch。concurrent per-plugin event execution は non-goal。
- Queue は bounded。full / timeout / failed service / stop 中 event / invalid event は typed error / diagnostic として扱う。
- `start()` は long-running loop / polling loop / recv loop を担わない。
- Durable cross-process event queue は non-goal。まず host-managed in-process queue/lifecycle として実装する。
- WebSocket driver と output command model は後続 Tickets (`00001KVXK0WE4`, `00001KVXK0WDX`) に残す。
- Component Model-only runtime authority and manifest rejection from prerequisites must not regress.
Requirements / acceptance criteria:
- Service Plugin instance が ready/starting/running/stopping/stopped/failed 相当の lifecycle state を持つ。
- `start()` return 後も ingress event を queue 経由で配送できる。
- Ingress event has source / ingress name / payload / created_at / attempt / correlation id.
- Queue depth / lifecycle state / last error / dispatch counters are visible in status diagnostics.
- Unit tests cover lifecycle start/stop/failure, bounded queue full, serial dispatch, timeout/failure diagnostics, stop-time event rejection, Tool execution regression.
- `cargo test -p pod`, `cargo check -p yoi`, `git diff --check`, `nix build .#yoi --no-link` are validation targets.
Implementation latitude:
- Choose exact structs/enums/names and whether queue processing is sync-step driven or background worker, as long as start returns promptly and dispatch remains host-managed/serial.
- Existing `PluginInstanceRegistry` may be extended or refactored if authority boundaries remain clear.
- Tests may use existing `test-ingress` runtime or focused component fixtures.
Escalate if:
- Implementation requires durable cross-process queue or scheduler semantics.
- WebSocket driver or output command model must be implemented to make lifecycle tests pass.
- Component Tool execution has to be routed through service queue.
- Host API grant/runtime authority or Component Model manifest behavior needs redesign.
Validation:
- `cargo test -p pod`
- `cargo check -p yoi`
- `git diff --check`
- `nix build .#yoi --no-link`
- Focused tests for service lifecycle/queue may be run during development.
Current code map:
- Primary: `crates/pod/src/feature/plugin.rs`
- Secondary only if needed: manifest service/ingress declarations and docs comments.
- Avoid: WebSocket driver, output command model, WIT/PDK/templates event update, remote runtime protocol, durable cross-process queue.
Critical risks / reviewer focus:
- accidentally making Service runtime a scheduler/durable queue.
- Tool execution regression or Tool path accidentally using Service queue.
- unbounded queue or hidden background loop.
- missing stop/failure/timeout diagnostics.
- scope creep into WebSocket/output commands/WIT-PDK.
Next action:
- `queued -> inprogress` を記録してから worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WDQ-plugin-service-lifecycle` を作成し、multi-agent-workflow で Coder/Reviewer sibling loop に進める。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T21:23:16Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked field: state -->
## State changed
Orchestrator acceptance: queued -> inprogress
- 直前確認で `TicketShow` は state `queued`、derived blockers は空。
- outgoing dependency `00001KVXK0WDH` は done / merged / reviewed / validated 済み。
- accepted plan `orch-plan-20260624-212209-2` を確認した。
- routing decision と IntentPacket は Ticket thread に記録済み。
- これ以降に worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WDQ-plugin-service-lifecycle` を作成し、multi-agent-workflow に接続する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T21:24:10Z -->
## Implementation report
Implementation orchestration update:
- prerequisite `00001KVXK0WDH` が done になったため、`00001KVXK0WDQ` を再 routing して受理した。
- worktree-workflow に従い、implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WDQ-plugin-service-lifecycle` と branch `work/00001KVXK0WDQ-plugin-service-lifecycle` を作成した。
- child worktree では tracked `.yoi` project records を見える状態にし、`.yoi/memory`、local/runtime/log/lock/secret-like `.yoi` paths を sparse-checkout で除外する方針にした。
- Coder Pod `yoi-coder-00001KVXK0WDQ-service-lifecycle` を sibling として起動した。write scope は child worktree に限定し、runtime workspace root `/home/hare/Projects/yoi` は startup 要件を満たすため non-recursive read のみにした。
- Coder には root/original workspace と orchestration worktree で read/write/validation/git operation を行わないよう明示した。
---
<!-- event: implementation_report author: yoi-coder-00001KVXK0WDQ-service-lifecycle at: 2026-06-24T21:43:56Z -->
## Implementation report
Implementation report:
対象 Ticket:
- `00001KVXK0WDQ` — Define Plugin Service lifecycle and ingress queue runtime
変更ファイル:
- `crates/pod/src/feature/plugin.rs`
主な実装:
- Plugin Service の host-managed lifecycle を追加。
- `Ready / Starting / Running / Stopping / Stopped / Failed`
- in-process bounded ingress queue を追加。
- v0 は per-plugin serial dispatch。
- Tool Plugin 実行は従来通り request-response のまま維持。
- `PluginIngressEvent` に以下を追加。
- `source`
- `ingress_name`
- `payload`
- `created_at`
- `attempt`
- `correlation_id`
- typed ingress dispatch error / diagnostic を追加。
- invalid event
- queue full
- service unavailable / failed / stopped
- dispatch timeout
- dispatch failure
- status diagnostics に以下を追加。
- lifecycle state
- queue depth / capacity
- last error
- dispatch counters
- Unit tests を追加・更新。
- lifecycle start/stop/failure
- bounded queue full
- serial dispatch
- timeout diagnostic
- dispatch failure diagnostic
- stopped service rejection
- invalid event rejection
- Tool execution regression
Commit:
- `4e138b7e365d940bf31397aa64cf14a58b6bc51a feat: add plugin service ingress queue`
Validation:
- `cargo test -p pod feature::plugin::tests`: success (`73 passed`)
- `cargo check -p yoi`: success
- `git diff --check`: success
- `git diff --check HEAD~1 HEAD`: success
- `nix build .#yoi --no-link`: success
- `cargo test -p pod`: failed in existing prompt guidance assertions outside this Ticket:
- `prompt::catalog::tests::pod_orchestration_guidance_section_renders_resource_body`
- `prompt::system::tests::pod_orchestration_guidance_is_included_for_pod_management_tools`
- failure: `assertion failed: rendered.contains("worktree status, diff, and test results")`
Residual risks / notes:
- Full `cargo test -p pod` remains blocked by unrelated prompt/resource snapshot assertions; Plugin-focused tests pass.
- v0 dispatch timeout is implemented as elapsed check after synchronous dispatch completion and typed diagnostic. Force-cancelling executing Wasm in a separate worker is broader runtime redesign and remains non-goal.
---
<!-- event: review author: yoi-reviewer-00001KVXK0WDQ-service-lifecycle at: 2026-06-24T21:49:35Z status: approve -->
## Review: approve
Review result: approve
確認範囲:
- child worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WDQ-plugin-service-lifecycle`
- branch `work/00001KVXK0WDQ-plugin-service-lifecycle`
- HEAD `4e138b7e365d940bf31397aa64cf14a58b6bc51a`
- base `f26c7e0d`
- diff `f26c7e0d..HEAD`
- Ticket `00001KVXK0WDQ``item.md` / `thread.md` / orchestration plan
判断:
- Ticket intent / IntentPacket に対して実装は妥当。
- 変更は `crates/pod/src/feature/plugin.rs` のみで、WebSocket driver / output command model / WIT/PDK/templates / durable queue への scope creep は見当たらない。
- Component Model-only runtime と legacy raw wasm rejection は維持されている。
主な確認根拠:
- `PluginInstanceLifecycleState``Ready / Starting / Running / Stopping / Stopped / Failed` に拡張され、start/stop/status/dispatch failure で lifecycle と diagnostics が更新される。
- `PluginIngressEvent``source / ingress_name / payload / created_at / attempt / correlation_id` が揃っている。
- `VecDeque` + capacity による in-process bounded queue、`QueueFull` / `InvalidEvent` / `ServiceFailed` / `ServiceStopped` / `DispatchTimeout` / `DispatchFailed` の typed error/diagnostic が追加されている。
- dispatch は `Mutex` 下の per-plugin serial dispatch で、concurrent per-plugin execution にはしていない。
- Tool execution path は `PluginInstanceTool::execute -> handle_tool` の request-response のままで、Service queue に流していない。
- status に `queue_depth / queue_capacity / last_error / dispatch_counters / diagnostics` が出る。
- unit tests は lifecycle、queue full、serial dispatch、timeout/failure、stopped rejection、invalid event、Tool regression をカバーしている。
検証:
- reviewer: `git diff --check f26c7e0d..HEAD`: success
- cargo/nix は read-only 指示のため再実行せず、coder 報告を確認対象として扱った。
Non-blocking notes:
- queue processing は sync-step driven で、現 public delivery は enqueue 直後に同じ lock 内で dispatch するため、通常経路では queue depth はほぼ 0 になる。Ticket の implementation latitude 内だが、後続の WebSocket driver 側で backpressure/queue depth をどう見せるかは改めて注意が必要。
- `cargo test -p pod` は coder 報告では既存の prompt guidance assertion 由来の失敗あり。Plugin-focused tests / `cargo check -p yoi` / nix build は成功報告で、本 Ticket の blocker とは判断しない。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-24T21:51:01Z -->
## Implementation report
Integration outcome:
- Coder `yoi-coder-00001KVXK0WDQ-service-lifecycle` が implementation branch `work/00001KVXK0WDQ-plugin-service-lifecycle` に実装を commit した。
- implementation commit: `4e138b7e feat: add plugin service ingress queue`
- Reviewer `yoi-reviewer-00001KVXK0WDQ-service-lifecycle` は read-only review で `approve`。host-managed in-process lifecycle/queue、bounded queue、serial dispatch、typed diagnostics、Tool path non-regression、scope creep なしを確認した。
- Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration` で branch `work/00001KVXK0WDQ-plugin-service-lifecycle` を merge 済み。
- merge commit: `000afbbe merge: 00001KVXK0WDQ plugin service lifecycle`
Implemented behavior:
- `crates/pod/src/feature/plugin.rs` に Plugin Service lifecycle state を追加。
- `Ready / Starting / Running / Stopping / Stopped / Failed`
- in-process bounded ingress queue と per-plugin serial dispatch を追加。
- `PluginIngressEvent``source`, `ingress_name`, `payload`, `created_at`, `attempt`, `correlation_id` を追加。
- `QueueFull`, `InvalidEvent`, `ServiceFailed`, `ServiceStopped`, `DispatchTimeout`, `DispatchFailed` など typed error/diagnostic を追加。
- status diagnostics に queue depth/capacity, last_error, dispatch_counters, lifecycle diagnostics を追加。
- Tool Plugin execution は従来の request-response path のまま維持。
Validation in Orchestrator worktree:
- `cargo test -p pod feature::plugin::tests`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- `cargo test -p pod`: failed only in known prompt guidance snapshot assertions outside this Plugin diff:
- `prompt::catalog::tests::pod_orchestration_guidance_section_renders_resource_body`
- `prompt::system::tests::pod_orchestration_guidance_is_included_for_pod_management_tools`
- Plugin-focused tests in the same run passed, including service lifecycle/queue/timeout/Tool regression coverage.
Notes:
- Reviewer non-blocking note: current queue processing is sync-step driven, and public delivery enqueues then dispatches immediately under the lock, so normal queue depth is usually 0. This remains within Ticket latitude, but follow-up WebSocket driver/backpressure work should revisit how queue depth is exposed.
- Follow-up `00001KVXK0WDX` is dependency-unblocked and can be re-routed next.
Next action:
- Mark Ticket done after this integration/validation evidence.
- Then stop related child Pods and remove only the child implementation worktree/branch.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-24T21:51:13Z from: inprogress to: done reason: merged_validated_with_unrelated_full_suite_caveat field: state -->
## State changed
Implementation is merged into the orchestration branch and validated with one unrelated known full-suite caveat.
Evidence:
- merge commit: `000afbbe merge: 00001KVXK0WDQ plugin service lifecycle`
- reviewer result: approve
- validation in `/home/hare/Projects/yoi/.worktree/orchestration`:
- `cargo test -p pod feature::plugin::tests`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- `cargo test -p pod`: failed only in prompt guidance snapshot assertions outside this Plugin diff; Plugin-focused tests passed.
Closure is not performed here; this state records implementation completion after merge/review/focused validation.
---
@@ -0,0 +1,2 @@
{"id":"orch-plan-20260624-201335-1","ticket_id":"00001KVXK0WDX","kind":"blocked_by","related_ticket":"00001KVXK0WDQ","note":"Queue review: `00001KVXK0WDX` は service output command model slice だが、output commands are returned by service ingress dispatch のため `00001KVXK0WDQ` に depends_on している。prerequisite completion 後に再 routing する。","author":"yoi-orchestrator","at":"2026-06-24T20:13:35Z"}
{"id":"orch-plan-20260625-054531-2","ticket_id":"00001KVXK0WDX","kind":"accepted_plan","accepted_plan":{"summary":"Ticket `00001KVXK0WDX` は prerequisite `00001KVXK0WDQ` が done になったため implementation_ready。専用 worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WDX-plugin-output-commands` と branch `work/00001KVXK0WDX-plugin-output-commands` で、Plugin Service ingress handler の output command envelope / validation / grant-check / diagnostics を追加する。WebSocket transport、HTTP request dispatch 完成、WIT/PDK/templates update は後続 Tickets に残す。","branch":"work/00001KVXK0WDX-plugin-output-commands","worktree":"/home/hare/Projects/yoi/.worktree/00001KVXK0WDX-plugin-output-commands","role_plan":"Orchestrator: accept/routing, worktree creation, final integration/validation/cleanup. Coder: implement service output command envelope and grant-check diagnostics in dedicated child worktree. Reviewer: read-only review focusing on command/result separation from ToolOutput, fail-closed grants, no WebSocket/HTTP transport scope creep, and service lifecycle non-regression."},"author":"yoi-orchestrator","at":"2026-06-25T05:45:31Z"}
@@ -0,0 +1,13 @@
{
"version": 1,
"relations": [
{
"ticket_id": "00001KVXK0WDX",
"kind": "depends_on",
"target": "00001KVXK0WDQ",
"note": "Output commands are returned by service ingress dispatch.",
"author": "yoi ticket",
"at": "2026-06-24T19:55:30Z"
}
]
}
+46
View File
@@ -0,0 +1,46 @@
---
title: 'Add Plugin service output command model'
state: 'done'
created_at: '2026-06-24T19:51:56Z'
updated_at: '2026-06-25T06:20:26Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-24T20:12:02Z'
---
## 背景
Service / Ingress Plugin が外部 event を処理した後、WebSocket send、HTTP request、diagnostic update などの side effect を直接 ambient authority で実行すると、grant boundary と observability が曖昧になる。Plugin は event handler の戻り値として output commands を返し、Host が manifest declaration / enablement grant / runtime policy を検査して実行する形にしたい。
この Ticket では WebSocket driver 実装前に、Service Plugin の output command envelope と grant check 境界を追加する。
## 要件
- `handle-ingress` / service event handler の戻り値に output command list を表現できる型を追加する。
- v0 command kind を最小集合で定義する。
- diagnostic/status update。
- host request dispatch placeholder。
- websocket send placeholder。
- Command は correlation id / source event id / command id / kind / payload / requested_at を持つ。
- Host が command ごとに manifest declaration と enablement grant を検査する。
- Unsupported / ungranted / malformed command は fail closed で diagnostic に残す。
- Command execution result は service status / run overview / diagnostics から追える形にする。
- Tool Plugin の ordinary ToolOutput path と混同しない。
## Non-goals
- WebSocket send の実 transport 実装。
- HTTP request dispatch command の完全実装。
- Domain operation command の完成。
- LLM history への hidden context injection。
- Unrestricted shell / filesystem command。
## 受け入れ条件
- Service Plugin ingress handler が output command envelope を返せる。
- Host が output command を parse / validate / grant-check する。
- Ungranted command は実行されず、typed diagnostic になる。
- Diagnostic/status update command のような safe v0 command が実行または記録される。
- WebSocket send / request dispatch は placeholder command として grant-check 可能で、実 transport が無くても安全に unsupported として扱える。
- Tool Plugin output と Service Plugin output command が型・docs・testsで区別されている。
- `cargo test -p pod``cargo check -p yoi``git diff --check``nix build .#yoi --no-link` が通る。
+299
View File
@@ -0,0 +1,299 @@
<!-- event: create author: "yoi ticket" at: 2026-06-24T19:51:56Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: hare at: 2026-06-24T19:55:29Z -->
## Intake summary
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-24T19:55:29Z from: planning to: ready reason: cli_state field: state -->
## State changed
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: workspace-panel at: 2026-06-24T20:12:02Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-25T05:46:02Z -->
## Decision
Routing decision: implementation_ready
Reason:
- ユーザーから「続けて」と明示 follow-up があり、queued dependency chain の次 Ticket として再確認した。
- `00001KVXK0WDX` は Service output command model の concrete slice で、WebSocket transport、HTTP request dispatch completion、Domain operation command completion、WIT/PDK/templates update は non-goal として後続に分離されている。
- outgoing `depends_on``00001KVXK0WDQ` だが、`00001KVXK0WDQ` は done / merged / reviewed / validated 済み。`TicketShow` derived blockers は空で、implementation acceptance blocker は残っていない。
- incoming dependent `00001KVXK0WE4` は WebSocket driver slice で、この Ticket 完了後に進めるべき後続であり、この Ticket の acceptance blocker ではない。
- bounded context check で current Plugin service lifecycle / ingress queue implementation が `crates/pod/src/feature/plugin.rs` に入り、event dispatch/status diagnostics の拡張点が存在することを確認した。Ticket の残る不確実性は command envelope / validation / grant-check / diagnostics の local implementation に閉じる。
Evidence checked:
- Ticket body / thread: `item.md`, `thread.md`。未解決 planning question は記録されていない。
- Relations / orchestration plan: outgoing depends_on `00001KVXK0WDQ` は done。routing 前 plan は historical blocked_by `00001KVXK0WDQ` のみで、prerequisite 完了により解消済み。accepted plan `orch-plan-20260625-054531-2` を記録済み。
- Related Tickets: `00001KVXK0WD3`, `00001KVXK0WDH`, `00001KVXK0WDQ` は done。
- Code context: `crates/pod/src/feature/plugin.rs` の Plugin Service lifecycle / bounded ingress queue / `PluginIngressEvent` / status diagnostics / component runtime。
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean。inprogress Ticket は 0 件。
IntentPacket:
Intent:
- Service Plugin ingress handler の戻り値として output command envelope を表現し、Host が command ごとに manifest declaration / enablement grant / runtime policy を fail-closed に検査し、結果を service diagnostics/status から追えるようにする。
Binding decisions / invariants:
- Service output command は Tool Plugin の ordinary `ToolOutput` path と型・処理経路・docs/tests で区別する。
- v0 command kind は最小集合に留める: diagnostic/status update, host request dispatch placeholder, websocket send placeholder。
- WebSocket send の実 transport 実装、HTTP request dispatch completion、Domain operation command completion は non-goal。
- Unsupported / ungranted / malformed command は実行せず typed diagnostic にする。
- Unrestricted shell / filesystem command や hidden LLM context injection は絶対に導入しない。
- Existing Plugin Service lifecycle / bounded ingress queue and Component Model-only runtime from prerequisites must not regress.
Requirements / acceptance criteria:
- `handle-ingress` / service event handler result can carry output command list.
- Command has correlation id / source event id / command id / kind / payload / requested_at.
- Host parses, validates, and grant-checks each command.
- Ungranted command is not executed and appears as typed diagnostic.
- Safe diagnostic/status update command is executed or recorded.
- WebSocket send / request dispatch placeholders are grant-checkable and safely unsupported without transport.
- Command execution result is visible from service status / diagnostics or run overview-equivalent state.
- Tests distinguish Tool Plugin output from Service output commands.
Implementation latitude:
- Exact Rust names/enums and JSON/envelope shape may follow existing plugin code style.
- Manifest declaration/grant mapping can be minimal v0 as long as it is explicit and fail-closed.
- Existing tests/fixtures may be extended in `crates/pod/src/feature/plugin.rs`; docs/comments may be updated if needed.
Escalate if:
- Implementing output commands requires actual WebSocket/HTTP transport.
- Current manifest/grant model cannot represent placeholder command grants without broader schema redesign.
- Command results must be persisted in a durable cross-process run overview to satisfy tests.
- Tool Plugin output must be routed through Service command processing.
Validation:
- `cargo test -p pod`
- `cargo check -p yoi`
- `git diff --check`
- `nix build .#yoi --no-link`
- Focused plugin service command tests during development are expected.
Current code map:
- Primary: `crates/pod/src/feature/plugin.rs`
- Secondary only if necessary: manifest grant declarations/tests/docs comments。
- Avoid: WebSocket transport driver, HTTP request dispatch completion, WIT/PDK/templates service event update, durable cross-process queue, remote runtime protocol。
Critical risks / reviewer focus:
- output commands becoming ambient authority.
- ToolOutput and Service output commands being conflated.
- ungranted/malformed commands partially executing.
- placeholders accidentally performing network I/O。
- scope creep into WebSocket driver or PDK/WIT updates.
Next action:
- `queued -> inprogress` を記録してから worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WDX-plugin-output-commands` を作成し、multi-agent-workflow で Coder/Reviewer sibling loop に進める。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-25T05:46:44Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked field: state -->
## State changed
Orchestrator acceptance: queued -> inprogress
- 直前確認で `TicketShow` は state `queued`、derived blockers は空。
- outgoing dependency `00001KVXK0WDQ` は done / merged / reviewed / validated 済み。
- accepted plan `orch-plan-20260625-054531-2` を確認した。
- routing decision と IntentPacket は Ticket thread に記録済み。
- これ以降に worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WDX-plugin-output-commands` を作成し、multi-agent-workflow に接続する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-25T05:47:43Z -->
## Implementation report
Implementation orchestration update:
- prerequisite `00001KVXK0WDQ` が done になったため、`00001KVXK0WDX` を再 routing して受理した。
- worktree-workflow に従い、implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WDX-plugin-output-commands` と branch `work/00001KVXK0WDX-plugin-output-commands` を作成した。
- child worktree では tracked `.yoi` project records を見える状態にし、`.yoi/memory`、local/runtime/log/lock/secret-like `.yoi` paths を sparse-checkout で除外する方針にした。
- Coder Pod `yoi-coder-00001KVXK0WDX-output-commands` を sibling として起動した。write scope は child worktree に限定し、runtime workspace root `/home/hare/Projects/yoi` は startup 要件を満たすため non-recursive read のみにした。
- Coder には root/original workspace と orchestration worktree で read/write/validation/git operation を行わないよう明示した。
---
<!-- event: implementation_report author: yoi-coder-00001KVXK0WDX-output-commands at: 2026-06-25T06:12:58Z -->
## Implementation report
Implementation report:
対象 Ticket:
- `00001KVXK0WDX` — Add Plugin service output command model
変更ファイル:
- `crates/pod/src/feature/plugin.rs`
主な実装:
- Service/Ingress handler の戻り値から `output_commands` を読む Service output command model を追加。
- command envelope に以下を追加。
- `correlation_id`
- `source_event_id`
- `command_id`
- `kind`
- `payload`
- `requested_at`
- v0 command kind を追加。
- `diagnostic_status_update`
- `host_request_dispatch`
- `websocket_send`
- Host 側で envelope parse / field validation / `requested_at` RFC3339 validation / `source_event_id` と ingress event correlation id の照合 / payload size bound / kind-specific payload validation / request・websocket grant check を行うようにした。
- malformed / ungranted / unsupported command は typed diagnostic として記録し、実行しない。
- `diagnostic_status_update` は安全に `component_status` / diagnostics に記録する。
- `host_request_dispatch` / `websocket_send` は grant-check 可能だが、v0 では transport 実行せず `Unsupported` として記録する。
- Service output command results を `PluginIngressDispatchReport``PluginInstanceStatus` から見えるように追加。
- ordinary Tool Plugin `ToolOutput` path と Service output command path が混ざらないよう、型・コメント・テストを分離。
- focused tests を追加。
- diagnostic/status command の記録
- ungranted request command の reject と status side effect 非実行
- request/websocket placeholder の grant-check + unsupported 記録
- malformed envelope の reject
Commit:
- `755d460f0d4e2dccb3f75d9ad980b1e701464a4e feat: add plugin service output commands`
Validation:
- `cargo fmt --check`: success
- `cargo test -p pod service_output_command -- --nocapture`: success (`4 passed`)
- `cargo test -p pod feature::plugin::tests`: success (`77 passed`)
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- `cargo test -p pod`: failed in known prompt guidance snapshot assertions outside this Ticket (`prompt::catalog::tests::pod_orchestration_guidance_section_renders_resource_body`, `prompt::system::tests::pod_orchestration_guidance_is_included_for_pod_management_tools`), with Plugin tests passing.
Residual risks / notes:
- WebSocket send transport と host request dispatch の実実行は Ticket non-goal に従い未実装で、grant-check 後に `Unsupported` diagnostic として記録する。
- Service output command schema は Rust 側 JSON envelope として追加した。WIT/PDK/templates 更新は non-goal として触っていない。
- Full `cargo test -p pod` の 2 failures は今回の Plugin diff 外の既知 prompt snapshot failure と一致している。
---
<!-- event: review author: yoi-reviewer-00001KVXK0WDX-output-commands at: 2026-06-25T06:18:37Z status: approve -->
## Review: approve
Review result: approve
確認結果:
- Ticket `00001KVXK0WDX` の IntentPacket / 受け入れ条件に対して、実装 commit `755d460f feat: add plugin service output commands` は妥当。
承認根拠:
- 差分は `crates/pod/src/feature/plugin.rs` のみで、scope creep は見当たらない。
- Service/Ingress handler output の `output_commands` を読む専用経路が追加され、ordinary Tool Plugin `ToolOutput` path とは型・処理・report/status field が分離されている。
- envelope は `correlation_id` / `source_event_id` / `command_id` / `kind` / `payload` / `requested_at` を持ち、unknown fields reject、id / RFC3339 / `source_event_id == ingress correlation_id` / payload size / kind-specific payload を検証している。
- `host_request_dispatch``websocket_send` は既存の manifest declaration + enabled grant / allowlist 検査に乗っており、実行段階では `Unsupported` result を返すだけで HTTP/WebSocket transport 実装に踏み込んでいない。
- malformed / ungranted command は `ServiceOutputCommandRejected` diagnostic になり、mixed batch でも安全側に倒して部分的な status update を実行しないテストがある。
- `diagnostic_status_update` は bounded payload/message 経由で `component_status` / diagnostics / `output_command_results` に記録され、外部 side effect や hidden LLM context injection はない。
- Service command results は `PluginIngressDispatchReport``PluginInstanceStatus.output_command_results` から追跡可能。
- 追加テストは recorded diagnostic、ungranted rejection、placeholder unsupported、malformed envelope を押さえている。
検証:
- Ticket `item.md` / `thread.md` / orchestration plan / relations を確認。
- child worktree で `git status` / `git log` / `git diff --stat` / diff 内容を read-only 確認。
- read-only 指示のため cargo/nix 等は再実行していない。
Non-blocking note:
- Coder 報告では `cargo test -p pod` が既知の prompt guidance snapshot assertion でのみ失敗、Plugin tests / `cargo check -p yoi` / `git diff --check` / `nix build .#yoi --no-link` は成功。最終統合時に既知 failure 扱いだけ確認すること。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-25T06:20:17Z -->
## Implementation report
Integration outcome:
- Coder `yoi-coder-00001KVXK0WDX-output-commands` が implementation branch `work/00001KVXK0WDX-plugin-output-commands` に実装を commit した。
- implementation commit: `755d460f feat: add plugin service output commands`
- Reviewer `yoi-reviewer-00001KVXK0WDX-output-commands` は read-only review で `approve`。Service output command path と ToolOutput path の分離、fail-closed validation/grant checks、placeholder command の non-transport behavior、diagnostic/status recording、scope creep なしを確認した。
- Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration` で branch `work/00001KVXK0WDX-plugin-output-commands` を merge 済み。
- merge commit: `d2aa92a7 merge: 00001KVXK0WDX plugin output commands`
Implemented behavior:
- `crates/pod/src/feature/plugin.rs` に Service output command envelope handling を追加。
- `output_commands``correlation_id`, `source_event_id`, `command_id`, `kind`, `payload`, `requested_at` を持つ。
- v0 command kinds:
- `diagnostic_status_update`
- `host_request_dispatch`
- `websocket_send`
- Host-side validation:
- envelope parse / unknown field rejection
- id and RFC3339 validation
- `source_event_id` と ingress event correlation id の照合
- payload size bound
- kind-specific payload validation
- manifest declaration + enablement grant / allowlist checks
- malformed / ungranted / unsupported commands は typed diagnostic になり、実行しない。
- `diagnostic_status_update` は bounded payload/message で component status / diagnostics / output command results に記録。
- `host_request_dispatch` / `websocket_send` は grant-check 後に `Unsupported` として記録し、HTTP/WebSocket transport には踏み込まない。
- `PluginIngressDispatchReport``PluginInstanceStatus.output_command_results` から command result を追跡可能。
Validation in Orchestrator worktree:
- `cargo fmt --check`: success
- `cargo test -p pod service_output_command -- --nocapture`: success
- `cargo test -p pod feature::plugin::tests`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- `cargo test -p pod`: failed only in known prompt guidance snapshot assertions outside this Plugin diff:
- `prompt::catalog::tests::pod_orchestration_guidance_section_renders_resource_body`
- `prompt::system::tests::pod_orchestration_guidance_is_included_for_pod_management_tools`
- Plugin-focused tests in the same run passed.
Notes:
- Full `cargo test -p pod` failure is recorded as the same known unrelated prompt-resource assertion issue seen in previous Plugin Tickets, not a blocker for this Ticket.
- Follow-up `00001KVXK0WE4` is dependency-unblocked and can be re-routed next.
Next action:
- Mark Ticket done after this integration/validation evidence.
- Then stop related child Pods and remove only the child implementation worktree/branch.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-25T06:20:26Z from: inprogress to: done reason: merged_validated_with_unrelated_full_suite_caveat field: state -->
## State changed
Implementation is merged into the orchestration branch and validated with one unrelated known full-suite caveat.
Evidence:
- merge commit: `d2aa92a7 merge: 00001KVXK0WDX plugin output commands`
- reviewer result: approve
- validation in `/home/hare/Projects/yoi/.worktree/orchestration`:
- `cargo fmt --check`: success
- `cargo test -p pod service_output_command -- --nocapture`: success
- `cargo test -p pod feature::plugin::tests`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- `cargo test -p pod`: failed only in prompt guidance snapshot assertions outside this Plugin diff; Plugin-focused tests passed.
Closure is not performed here; this state records implementation completion after merge/review/focused validation.
---
@@ -0,0 +1,3 @@
{"id":"orch-plan-20260624-201335-1","ticket_id":"00001KVXK0WE4","kind":"blocked_by","related_ticket":"00001KVXK0WDQ","note":"Queue review: `00001KVXK0WE4` は WebSocket driver slice だが、service ingress queue `00001KVXK0WDQ` と output command model `00001KVXK0WDX` に depends_on している。両 prerequisite completion 後に再 routing する。","author":"yoi-orchestrator","at":"2026-06-24T20:13:35Z"}
{"id":"orch-plan-20260624-201341-2","ticket_id":"00001KVXK0WE4","kind":"blocked_by","related_ticket":"00001KVXK0WDX","note":"Queue review: `00001KVXK0WE4` は output command dispatch/grant model `00001KVXK0WDX` にも depends_on している。`00001KVXK0WDQ` と `00001KVXK0WDX` の両方が完了するまで acceptance しない。","author":"yoi-orchestrator","at":"2026-06-24T20:13:41Z"}
{"id":"orch-plan-20260625-062148-3","ticket_id":"00001KVXK0WE4","kind":"accepted_plan","accepted_plan":{"summary":"Ticket `00001KVXK0WE4` は prerequisites `00001KVXK0WDQ` と `00001KVXK0WDX` が done になったため implementation_ready。専用 worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WE4-plugin-websocket-driver` と branch `work/00001KVXK0WE4-plugin-websocket-driver` で、Plugin Service 用 host-owned WebSocket driver を追加し、incoming text frames を ingress queue に入れ、output command の websocket send を grant-check 後に送信する。Discord protocol/full reconnect/secret-store/browser WebSocket/WIT-PDK template update は non-goals。","branch":"work/00001KVXK0WE4-plugin-websocket-driver","worktree":"/home/hare/Projects/yoi/.worktree/00001KVXK0WE4-plugin-websocket-driver","role_plan":"Orchestrator: accept/routing, worktree creation, final integration/validation/cleanup. Coder: implement host-owned WebSocket driver slice in dedicated child worktree. Reviewer: read-only review focusing on host-owned connection authority, ingress queue integration, output command send execution, grant checks, and no protocol-specific/reconnect/scope creep."},"author":"yoi-orchestrator","at":"2026-06-25T06:21:48Z"}
@@ -0,0 +1,21 @@
{
"version": 1,
"relations": [
{
"ticket_id": "00001KVXK0WE4",
"kind": "depends_on",
"target": "00001KVXK0WDQ",
"note": "WebSocket frames are delivered through the service ingress queue.",
"author": "yoi ticket",
"at": "2026-06-24T19:55:30Z"
},
{
"ticket_id": "00001KVXK0WE4",
"kind": "depends_on",
"target": "00001KVXK0WDX",
"note": "WebSocket sends are executed through output commands.",
"author": "yoi ticket",
"at": "2026-06-24T19:55:30Z"
}
]
}
+48
View File
@@ -0,0 +1,48 @@
---
title: 'Add host-owned WebSocket driver for Plugin services'
state: 'done'
created_at: '2026-06-24T19:51:56Z'
updated_at: '2026-06-25T07:06:30Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-24T20:12:03Z'
---
## 背景
Current `host_api.websocket` is a pull primitive: Plugin code must explicitly call `recv(timeout)` while an exported function is running. That is insufficient for Discord Gateway / Slack Socket Mode style long-lived integrations. Service Plugins need Host-owned WebSocket connections whose incoming frames are queued as ingress events and whose outgoing sends are requested through output commands.
This Ticket adds the WebSocket-specific event source and command executor on top of the Service ingress queue and output command model.
## 要件
- Host-owned WebSocket connection driver を追加する。
- Plugin manifest / grant で WebSocket subscription / target authority を表現できる。
- Host が connect / reader task / close / error detection を管理する。
- Incoming text frame を Plugin ingress event に変換し、service ingress queue に enqueue する。
- Close / error / reconnect-needed も ingress event または status diagnostic として扱う。
- Plugin からの WebSocket send は output command として受け取り、grant check 後に Host が送信する。
- v0 で扱う frame kind を明確にする。
- text frame required。
- binary / ping / pong / close の扱いを diagnostic / unsupported / control handling として定義する。
- Connection status、last frame time、last error、queue drops、send failures を diagnostics に出す。
- Existing `recv(timeout)` pull API は long-lived integration の recommended path にしない。
## Non-goals
- Discord protocol implementation。
- Full reconnect / resume policy の完成。
- Secret store / auth injection の全面設計。
- Binary frame application payload support。
- Browser-facing WebSocket API。
## 受け入れ条件
- Host-owned WebSocket driver が Service Plugin に紐づく connection を管理できる。
- Incoming text frame が Plugin ingress queue に event として入る。
- Plugin output command から WebSocket text send が実行される。
- Ungranted send / unauthorized target は fail closed で diagnostic になる。
- Connection close / error が service status diagnostics に反映される。
- Existing pull `recv(timeout)` API が docs/templates の recommended service integration path から外れている。
- WebSocket driver / ingress / send command の tests が追加されている。
- `cargo test -p pod``cargo check -p yoi``git diff --check``nix build .#yoi --no-link` が通る。
+288
View File
@@ -0,0 +1,288 @@
<!-- event: create author: "yoi ticket" at: 2026-06-24T19:51:56Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: hare at: 2026-06-24T19:55:30Z -->
## Intake summary
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-24T19:55:30Z from: planning to: ready reason: cli_state field: state -->
## State changed
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: workspace-panel at: 2026-06-24T20:12:03Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-25T06:22:18Z -->
## Decision
Routing decision: implementation_ready
Reason:
- ユーザーから「続けて」と明示 follow-up があり、queued dependency chain の次 Ticket として再確認した。
- `00001KVXK0WE4` は host-owned WebSocket driver の concrete slice で、Discord protocol、full reconnect/resume policy、secret store/auth injection、browser-facing WebSocket API、WIT/PDK templates update は non-goal として分離されている。
- outgoing `depends_on``00001KVXK0WDQ``00001KVXK0WDX` だが、両方とも done / merged / reviewed / validated 済み。`TicketShow` derived blockers は空で、implementation acceptance blocker は残っていない。
- incoming dependent `00001KVXK0WEA` は WIT/PDK/templates update で、この Ticket 完了後に進めるべき後続であり、この Ticket の acceptance blocker ではない。
- bounded context check で current `crates/pod/src/feature/plugin.rs` に existing pull `host_api.websocket` helpers、WebSocket grants/allowlist、Plugin Service lifecycle / ingress queue、Service output command model が存在することを確認した。Ticket の残る不確実性は host-owned connection driver / frame-to-ingress / output command send integration の local implementation に閉じる。
Evidence checked:
- Ticket body / thread: `item.md`, `thread.md`。未解決 planning question は記録されていない。
- Relations / orchestration plan: outgoing depends_on `00001KVXK0WDQ``00001KVXK0WDX` は done。routing 前 plan は historical blocked_by records 2 件で、prerequisites 完了により解消済み。accepted plan `orch-plan-20260625-062148-3` を記録済み。
- Related Tickets: `00001KVXK0WDQ` and `00001KVXK0WDX` are done.
- Code context: `crates/pod/src/feature/plugin.rs` の WebSocket allowlist/grants, existing pull API helpers, Plugin Service ingress queue, output command model。
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean。inprogress Ticket は 0 件。
IntentPacket:
Intent:
- Service Plugin に紐づく host-owned WebSocket connection driver を追加し、incoming text frames を Plugin ingress queue に event として enqueue し、Service output command の `websocket_send` を grant-check 後に Host が送信できるようにする。
Binding decisions / invariants:
- Connection ownership is Host-side. Plugin code does not run a long-lived recv loop as recommended service path.
- Existing pull `host_api.websocket.recv(timeout)` API may remain for compatibility/internal bounded use, but docs/recommended service integration path must move away from it.
- WebSocket target authority is manifest declaration + enabled grant / allowlist. Unauthorized target/send fails closed.
- v0 handles text frames. Binary application payload is unsupported/diagnostic. ping/pong/close handling should be bounded/control/diagnostic, not app payload.
- Incoming frames are converted to service ingress events using existing bounded queue and lifecycle diagnostics.
- Outgoing sends use Service output command path from `00001KVXK0WDX`; do not bypass its validation/grant boundary.
- Full reconnect/resume policy, Discord/Slack protocol logic, secret injection design, browser-facing WebSocket API, WIT/PDK/templates update are non-goals.
Requirements / acceptance criteria:
- Host-owned WebSocket driver can manage a connection associated with a Service Plugin.
- Incoming text frames enqueue Plugin ingress events.
- Close/error/reconnect-needed states become ingress events or status diagnostics.
- Plugin output command can trigger WebSocket text send after grant checks.
- Ungranted send / unauthorized target is fail-closed diagnostic.
- Connection status, last frame time, last error, queue drops, send failures are visible in diagnostics/status.
- Tests cover driver lifecycle, incoming frame to ingress, send command execution/rejection, close/error diagnostics, and frame kind handling.
Implementation latitude:
- Exact struct names and whether tests use mock WebSocket client/connection or local bounded fake are coder choices.
- v0 may model reconnect-needed as diagnostic instead of automatic reconnect.
- Keep integration in `crates/pod/src/feature/plugin.rs` unless a small helper module split is clearly cleaner.
Escalate if:
- Real network tests or external services are required.
- Secret store/auth injection becomes necessary.
- Durable cross-process queue or scheduler semantics are required.
- Implementing this requires WIT/PDK/template changes rather than Rust-side host runtime only.
- Browser-facing WebSocket API or protocol-specific Discord/Slack behavior is needed.
Validation:
- `cargo test -p pod`
- `cargo check -p yoi`
- `git diff --check`
- `nix build .#yoi --no-link`
- Focused WebSocket driver / service ingress tests during development.
Current code map:
- Primary: `crates/pod/src/feature/plugin.rs`
- Secondary only if necessary: docs/development recommended path wording。
- Avoid: WIT/PDK/templates update (reserved for `00001KVXK0WEA`), protocol-specific integrations, full reconnect policy, secret store, browser WebSocket API。
Critical risks / reviewer focus:
- hidden ambient network authority or bypassing manifest/grant allowlist。
- pull `recv(timeout)` remaining as recommended long-lived service path。
- output command send bypassing Service command validation。
- unbounded reader task/queue or dropped diagnostics。
- tests that require real external network。
- scope creep into PDK/templates or protocol-specific behavior。
Next action:
- `queued -> inprogress` を記録してから worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WE4-plugin-websocket-driver` を作成し、multi-agent-workflow で Coder/Reviewer sibling loop に進める。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-25T06:23:16Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked field: state -->
## State changed
Orchestrator acceptance: queued -> inprogress
- 直前確認で `TicketShow` は state `queued`、derived blockers は空。
- outgoing dependencies `00001KVXK0WDQ``00001KVXK0WDX` は done / merged / reviewed / validated 済み。
- accepted plan `orch-plan-20260625-062148-3` を確認した。
- routing decision と IntentPacket は Ticket thread に記録済み。
- これ以降に worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WE4-plugin-websocket-driver` を作成し、multi-agent-workflow に接続する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-25T06:24:16Z -->
## Implementation report
Implementation orchestration update:
- prerequisite `00001KVXK0WDX` が done になったため、`00001KVXK0WE4` を再 routing して受理した。
- worktree-workflow に従い、implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WE4-plugin-websocket-driver` と branch `work/00001KVXK0WE4-plugin-websocket-driver` を作成した。
- child worktree では tracked `.yoi` project records を見える状態にし、`.yoi/memory`、local/runtime/log/lock/secret-like `.yoi` paths を sparse-checkout で除外する方針にした。
- Coder Pod `yoi-coder-00001KVXK0WE4-websocket-driver` を sibling として起動した。write scope は child worktree に限定し、runtime workspace root `/home/hare/Projects/yoi` は startup 要件を満たすため non-recursive read のみにした。
- Coder には root/original workspace と orchestration worktree で read/write/validation/git operation を行わないよう明示した。
---
<!-- event: implementation_report author: yoi-coder-00001KVXK0WE4-websocket-driver at: 2026-06-25T06:56:37Z -->
## Implementation report
Implementation report:
- Ticket: `00001KVXK0WE4`
- Branch/worktree: `work/00001KVXK0WE4-plugin-websocket-driver` / `/home/hare/Projects/yoi/.worktree/00001KVXK0WE4-plugin-websocket-driver`
- Commit: `f2c51ffe399fc4eaa0ab4e4052867d55e534b229`
- Commit message: `feat: add plugin websocket service driver`
変更ファイル:
- `crates/pod/src/feature/plugin.rs`
- Service Plugin 用の host-owned WebSocket driver を追加。
- `ingress.sources``websocket:wss://...` / `ws(s)://...` を Service WebSocket 接続として扱う実装を追加。
- incoming text frame を既存の bounded Service ingress queue へ `websocket_text` event として enqueue。
- close/error を `websocket_close` / `websocket_error` ingress event と status diagnostic に反映。
- `websocket_send` Service output command を host-owned connection 経由の text send に接続。
- manifest declaration + enabled grant / allowlist を通らない open/send は fail-closed。
- status に connection state、last frame time、last error、received/sent count、queue drops、send failures を追加。
- driver lifecycle / incoming / send success / send rejection / send failure / close-error diagnostics / binary-like frame error の focused tests を追加。
- `docs/development/plugin-development.md`
- Service Plugin の推奨 WebSocket integration を、guest long-lived recv loop ではなく host-owned Service WebSocket driver + `websocket_send` output command に更新。
検証結果:
- `cargo test -p pod --lib websocket_`: success (`14 passed`)
- `cargo test -p pod --lib service_output_command`: success (`4 passed`)
- `cargo check -p pod`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- Nix dirty warning は worktree の ignored `target/` によるもので、tracked changes はなし。
- `cargo test -p pod`: failed only in known prompt guidance snapshot tests outside this Ticket (`438 passed; 2 failed`)
- `prompt::catalog::tests::pod_orchestration_guidance_section_renders_resource_body`
- `prompt::system::tests::pod_orchestration_guidance_is_included_for_pod_management_tools`
Residual risks / notes:
- v0 は自動 reconnect/resume policy を実装せず、close/error は diagnostic/event 化に留めている。
- Service WebSocket source の manifest 表現は既存 `ingress.sources` の文字列規約として追加。WIT/PDK/templates 更新は Ticket `00001KVXK0WEA` 側に残している。
- 実ネットワーク test は non-goal のため、mock WebSocket client/connection による bounded focused tests で検証している。
---
<!-- event: review author: yoi-reviewer-00001KVXK0WE4-websocket-driver at: 2026-06-25T07:03:47Z status: approve -->
## Review: approve
Review result: approve
読み取り専用で Ticket / IntentPacket / `62e467c0..HEAD` diff を確認した。Ticket review 記録・merge・close・検証コマンド実行は reviewer では行っていない。
確認した証拠:
- 変更範囲は `crates/pod/src/feature/plugin.rs``docs/development/plugin-development.md` のみで、WIT / PDK / templates へのスコープ拡大はない。
- Host-owned Service WebSocket driver:
- Service/Ingress 有効時に `ingress.sources` から `websocket:` / `ws(s)://` source を subscription 化している。
- instance start 後に host 側で connection start し、reader thread を生成している。
- authority / fail-closed:
- open は `host_api.websocket` permission と manifest/grant allowlist を通した既存 validation を再利用している。
- `websocket_send` は Service output command envelope の validation/grant check 経由でのみ実行され、URL scheme/credentials/static target/manifest+grant allowlist を検査している。
- ingress queue / diagnostics:
- text frame は `websocket_text` event として既存 `deliver_ingress` path に渡され、失敗時は queue drop と diagnostic を記録する。
- close/error は `websocket_close` / `websocket_error` event と status diagnostic に反映される。
- status には connection state, last frame, last error, received/sent counts, queue drops, send failures が出る。
- output command send:
- `PluginServiceOutputCommandKind::WebSocketSend` が追加され、実行時は host-owned connection の `send_text` に接続され、send failure diagnostic も記録する。
- docs:
- `recv(timeout)` は bounded/internal/tool-style use として残しつつ、Service Plugin では host-owned Service WebSocket driver + `websocket_send` を推奨する文面に更新されている。
- tests:
- mock/fake client による incoming text/close、send success/failure、unauthorized target、receive error/binary相当 diagnostic のテストが追加されている。
検証状況:
- reviewer は cargo/nix 等を実行していない(read-only / no writes 指示のため)。
- diff・対象コード・Ticket acceptance を静的確認し、coder 報告の `cargo check` / focused tests / nix build 結果を前提証跡として扱った。
Non-blocking notes:
- `docs/development/plugin-development.md` の “Current status” 付近にはまだ Service/Ingress/WebSocket を future work と読む余地が残っているが、本 Ticket の受け入れ条件である「pull recv を recommended service path にしない」は満たしている。後続の docs/PDK/templates Ticket で整理するとよい。
- close/error event の enqueue 失敗は text frame のような queue-drop counter には載せていないが、status diagnostic は記録されるため今回の acceptance 上は blocker ではない。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-25T07:06:19Z -->
## Implementation report
Integration outcome:
- Coder `yoi-coder-00001KVXK0WE4-websocket-driver` が implementation branch `work/00001KVXK0WE4-plugin-websocket-driver` に実装を commit した。
- implementation commit: `f2c51ffe feat: add plugin websocket service driver`
- Reviewer `yoi-reviewer-00001KVXK0WE4-websocket-driver` は read-only review で `approve`。host-owned connection authority、manifest/grant allowlist、ingress queue integration、`websocket_send` command validation、status diagnostics、scope creep なしを確認した。
- Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration` で branch `work/00001KVXK0WE4-plugin-websocket-driver` を merge 済み。
- merge commit: `b9e786e1 merge: 00001KVXK0WE4 plugin websocket driver`
Implemented behavior:
- `crates/pod/src/feature/plugin.rs` に Service Plugin 用 host-owned WebSocket driver を追加。
- `ingress.sources``websocket:wss://...` / `ws(s)://...` source を Service WebSocket connection として扱う。
- incoming text frame を existing bounded Service ingress queue へ `websocket_text` event として enqueue。
- close/error は `websocket_close` / `websocket_error` event と status diagnostic に反映。
- `websocket_send` Service output command は host-owned connection 経由の text send に接続。
- manifest declaration + enabled grant / allowlist を通らない open/send は fail-closed。
- status に connection state, last frame time, last error, received/sent counts, queue drops, send failures を追加。
- `docs/development/plugin-development.md` の Service Plugin 推奨 WebSocket integration を host-owned driver + `websocket_send` output command に更新。
Validation in Orchestrator worktree:
- `cargo test -p pod --lib websocket_`: success
- `cargo test -p pod --lib service_output_command`: success
- `cargo check -p pod`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- `cargo test -p pod`: failed only in known prompt guidance snapshot assertions outside this Plugin diff:
- `prompt::catalog::tests::pod_orchestration_guidance_section_renders_resource_body`
- `prompt::system::tests::pod_orchestration_guidance_is_included_for_pod_management_tools`
- Plugin/WebSocket focused tests in the same run passed.
Notes:
- Reviewer non-blocking note: `docs/development/plugin-development.md` の “Current status” 付近にはまだ Service/Ingress/WebSocket を future work と読む余地が残る。後続 `00001KVXK0WEA` の docs/PDK/templates update で整理するのがよい。
- Reviewer non-blocking note: close/error event enqueue failure は text frame と同じ queue-drop counter には載らないが、status diagnostic は記録されるため blocker ではない。
- Follow-up `00001KVXK0WEA` is dependency-unblocked and can be re-routed next.
Next action:
- Mark Ticket done after this integration/validation evidence.
- Then stop related child Pods and remove only the child implementation worktree/branch.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-25T07:06:30Z from: inprogress to: done reason: merged_validated_with_unrelated_full_suite_caveat field: state -->
## State changed
Implementation is merged into the orchestration branch and validated with one unrelated known full-suite caveat.
Evidence:
- merge commit: `b9e786e1 merge: 00001KVXK0WE4 plugin websocket driver`
- reviewer result: approve
- validation in `/home/hare/Projects/yoi/.worktree/orchestration`:
- `cargo test -p pod --lib websocket_`: success
- `cargo test -p pod --lib service_output_command`: success
- `cargo check -p pod`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- `cargo test -p pod`: failed only in prompt guidance snapshot assertions outside this Plugin diff; Plugin/WebSocket-focused tests passed.
Closure is not performed here; this state records implementation completion after merge/review/focused validation.
---
@@ -0,0 +1,2 @@
{"id":"orch-plan-20260624-201335-1","ticket_id":"00001KVXK0WEA","kind":"blocked_by","related_ticket":"00001KVXK0WE4","note":"Queue review: `00001KVXK0WEA` は WIT/PDK/templates finishing slice で、implemented WebSocket event/command model `00001KVXK0WE4` に depends_on している。prerequisite completion 後に再 routing する。","author":"yoi-orchestrator","at":"2026-06-24T20:13:35Z"}
{"id":"orch-plan-20260625-070739-2","ticket_id":"00001KVXK0WEA","kind":"accepted_plan","accepted_plan":{"summary":"Ticket `00001KVXK0WEA` は prerequisite `00001KVXK0WE4` が done になったため implementation_ready。専用 worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WEA-plugin-pdk-service-events` と branch `work/00001KVXK0WEA-plugin-pdk-service-events` で、Plugin WIT / Rust PDK / embedded templates / docs を Component Model-only Service ingress event + output command + host-owned WebSocket model に合わせて更新する。Runtime実装の再設計や protocol-specific integration は non-goal。","branch":"work/00001KVXK0WEA-plugin-pdk-service-events","worktree":"/home/hare/Projects/yoi/.worktree/00001KVXK0WEA-plugin-pdk-service-events","role_plan":"Orchestrator: accept/routing, worktree creation, final integration/validation/cleanup. Coder: update WIT/PDK/templates/docs/authoring CLI fixtures in dedicated child worktree. Reviewer: read-only review focusing on PDK/template consistency with implemented runtime, no legacy raw-wasm compatibility, and no runtime reimplementation."},"author":"yoi-orchestrator","at":"2026-06-25T07:07:39Z"}
@@ -0,0 +1,13 @@
{
"version": 1,
"relations": [
{
"ticket_id": "00001KVXK0WEA",
"kind": "depends_on",
"target": "00001KVXK0WE4",
"note": "Authoring templates should reflect the implemented WebSocket event/command model.",
"author": "yoi ticket",
"at": "2026-06-24T19:55:30Z"
}
]
}
+43
View File
@@ -0,0 +1,43 @@
---
title: 'Update Plugin WIT PDK templates for service event runtime'
state: 'done'
created_at: '2026-06-24T19:51:56Z'
updated_at: '2026-06-25T07:57:15Z'
assignee: null
queued_by: 'workspace-panel'
queued_at: '2026-06-24T20:12:05Z'
---
## 背景
Runtime implementation が Component Model only、host-managed Service lifecycle、Ingress queue、output command、host-owned WebSocket driver へ移るなら、WIT / PDK / templates / docs もその実行モデルを正として表現する必要がある。PDK が long-running `start()` loop や explicit `recv(timeout)` polling を推奨すると、runtime 方針と authoring UX がズレる。
この Ticket は implementation runtime が揃った後、外部 authoring API を新 model に合わせて更新する仕上げ slice とする。
## 要件
- WIT world / interfaces を新しい Service lifecycle / Ingress event / output command model に合わせて更新する。
- PDK API は `start()` で long-running loop を書かせない形にする。
- PDK は ingress event handler から output command を返す authoring model を提供する。
- WebSocket integration template は Host-owned subscription / ingress event / send command pattern を示す。
- Tool template は bounded request-response Tool Plugin として維持する。
- `plugin.toml` template は `wasm-component` runtime のみを生成する。
- `yoi plugin new` / `check` / `pack` が新 templates / schema と整合する。
- Docs は legacy raw `wasm` runtime、polling WebSocket loop、ambient authority を推奨しない。
## Non-goals
- Runtime internal queue / WebSocket driver の本実装。
- Discord-specific PDK abstraction。
- Public plugin registry。
- MCP bridge integration。
## 受け入れ条件
- WIT files が Component Model only runtime と Service event/command model を表現している。
- `yoi-plugin-pdk` が Tool Plugin と Service/Ingress Plugin の新 API を提供する。
- Templates は legacy raw `wasm` runtime を生成しない。
- Service/WebSocket template は polling `recv(timeout)` loop ではなく ingress event / output command pattern を使う。
- `yoi plugin new rust-component-tool` と service-oriented template が check/pack できる。
- Development docs が新 authoring model と authority boundary を説明している。
- `cargo test -p yoi-plugin-pdk``cargo test -p yoi``cargo check -p yoi``git diff --check``nix build .#yoi --no-link` が通る。
+363
View File
@@ -0,0 +1,363 @@
<!-- event: create author: "yoi ticket" at: 2026-06-24T19:51:56Z -->
## 作成
LocalTicketBackend によって作成されました。
---
<!-- event: intake_summary author: hare at: 2026-06-24T19:55:30Z -->
## Intake summary
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-24T19:55:30Z from: planning to: ready reason: cli_state field: state -->
## State changed
Marked ready by `yoi ticket state`.
---
<!-- event: state_changed author: workspace-panel at: 2026-06-24T20:12:05Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: yoi-orchestrator at: 2026-06-25T07:08:09Z -->
## Decision
Routing decision: implementation_ready
Reason:
- ユーザーから「続けて」と明示 follow-up があり、queued dependency chain の最終 Ticket として再確認した。
- `00001KVXK0WEA` は WIT / PDK / templates / docs を、既に実装済みの Component Model-only runtime、Service lifecycle / ingress queue、Service output commands、host-owned WebSocket driver に合わせる finishing slice である。
- outgoing `depends_on``00001KVXK0WE4` だが、`00001KVXK0WE4` は done / merged / reviewed / validated 済み。`TicketShow` derived blockers は空で、implementation acceptance blocker は残っていない。
- bounded context check で `resources/plugin/wit/*`, `crates/plugin-pdk`, `resources/plugin/templates/rust-component-tool`, `crates/yoi/src/plugin_cli.rs`, `docs/development/plugin-development.md` 周辺に authoring-facing surfaces があることを確認した。Ticket は runtime 実装ではなく authoring surface alignment に限定されており、残る不確実性は local implementation / fixture update に閉じる。
Evidence checked:
- Ticket body / thread: `item.md`, `thread.md`。未解決 planning question は記録されていない。
- Relations / orchestration plan: outgoing depends_on `00001KVXK0WE4` は done。routing 前 plan は historical blocked_by `00001KVXK0WE4` のみで、prerequisite 完了により解消済み。accepted plan `orch-plan-20260625-070739-2` を記録済み。
- Related Tickets: `00001KVXK0WD3`, `00001KVXK0WDH`, `00001KVXK0WDQ`, `00001KVXK0WDX`, `00001KVXK0WE4` are done.
- Code/docs context: `resources/plugin/wit/*.wit`, `crates/plugin-pdk`, `resources/plugin/templates/rust-component-tool`, `crates/yoi/src/plugin_cli.rs`, `docs/development/plugin-development.md`
- Workspace state: `/home/hare/Projects/yoi/.worktree/orchestration` は clean。inprogress Ticket は 0 件。
IntentPacket:
Intent:
- Plugin authoring surface (WIT, Rust PDK, embedded templates, docs, `yoi plugin new/check/pack` fixtures) を、Component Model-only runtime と Service ingress event / output command / host-owned WebSocket model に合わせて更新する。
Binding decisions / invariants:
- `plugin.toml` template は `wasm-component` runtime のみを生成する。
- Service/WebSocket authoring pattern は polling `recv(timeout)` loop ではなく、ingress event handler + output command (`websocket_send`) を正とする。
- Tool Plugin authoring support must continue to work.
- Runtime implementation from previous Tickets is not redesigned here; this Ticket updates WIT/PDK/templates/docs/tests to match it.
- No raw core-Wasm compatibility template or legacy runtime alias is introduced.
- No protocol-specific Discord/Slack integration, secret store/auth injection, or full reconnect policy is implemented.
Requirements / acceptance criteria:
- WIT expresses Service ingress event payloads and output command model enough for authoring/tests.
- `yoi-plugin-pdk` exposes ergonomic Tool and Service/Ingress helpers aligned with runtime JSON envelopes.
- Embedded templates include current Tool template and a service-oriented template or equivalent examples using ingress event / output command pattern.
- `yoi plugin new` / `check` / `pack` are consistent with new templates/schema.
- Docs no longer recommend long-running `recv(timeout)` loop for Service WebSocket integration.
- Tests cover PDK helpers, template generation/check/pack, and docs/manifest fixture consistency.
Implementation latitude:
- Exact WIT/interface names and Rust PDK helper APIs may follow existing PDK style, as long as runtime envelopes and docs are consistent.
- If a full new `plugin new` template name is too large, coder may add minimal service template/example plus CLI support needed to satisfy acceptance, but must keep scope bounded.
- Template wasm build/check may use existing test helpers and temporary target dirs.
Escalate if:
- WIT/PDK update requires changing runtime JSON envelope semantics from previous Tickets.
- `yoi plugin new/check/pack` requires broad CLI redesign.
- Real WebSocket network/protocol integration or secret handling is needed.
- Legacy raw-WASM compatibility has to be restored for templates/tests.
Validation:
- `cargo test -p yoi-plugin-pdk`
- `cargo test -p yoi plugin` or focused plugin CLI/template tests
- `cargo check -p yoi`
- `git diff --check`
- `nix build .#yoi --no-link`
- Template cargo-check if applicable, with cleanup of generated template artifacts.
Current code/docs map:
- Primary: `resources/plugin/wit/*.wit`, `crates/plugin-pdk`, `resources/plugin/templates/rust-component-tool`, possible new template under `resources/plugin/templates/`, `crates/yoi/src/plugin_cli.rs`, `docs/development/plugin-development.md`
- Secondary: manifest tests/fixtures only as needed.
- Avoid: Pod runtime reimplementation, WebSocket driver changes unless minor doc/test alignment, protocol-specific integrations, secret store/auth injection, raw-WASM compatibility。
Critical risks / reviewer focus:
- PDK/template API drift from runtime JSON envelopes。
- Tool template regression while adding Service support。
- reintroducing `recv(timeout)` as recommended Service pattern。
- template generation/check/pack writing outside destination or leaving build artifacts。
- accidental legacy raw-WASM runtime compatibility in examples。
Next action:
- `queued -> inprogress` を記録してから worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WEA-plugin-pdk-service-events` を作成し、multi-agent-workflow で Coder/Reviewer sibling loop に進める。
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-25T07:08:46Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked field: state -->
## State changed
Orchestrator acceptance: queued -> inprogress
- 直前確認で `TicketShow` は state `queued`、derived blockers は空。
- outgoing dependency `00001KVXK0WE4` は done / merged / reviewed / validated 済み。
- accepted plan `orch-plan-20260625-070739-2` を確認した。
- routing decision と IntentPacket は Ticket thread に記録済み。
- これ以降に worktree-workflow で `/home/hare/Projects/yoi/.worktree/00001KVXK0WEA-plugin-pdk-service-events` を作成し、multi-agent-workflow に接続する。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-25T07:09:39Z -->
## Implementation report
Implementation orchestration update:
- prerequisite `00001KVXK0WE4` が done になったため、`00001KVXK0WEA` を再 routing して受理した。
- worktree-workflow に従い、implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVXK0WEA-plugin-pdk-service-events` と branch `work/00001KVXK0WEA-plugin-pdk-service-events` を作成した。
- child worktree では tracked `.yoi` project records を見える状態にし、`.yoi/memory`、local/runtime/log/lock/secret-like `.yoi` paths を sparse-checkout で除外する方針にした。
- Coder Pod `yoi-coder-00001KVXK0WEA-pdk-service-events` を sibling として起動した。write scope は child worktree に限定し、runtime workspace root `/home/hare/Projects/yoi` は startup 要件を満たすため non-recursive read のみにした。
- Coder には root/original workspace と orchestration worktree で read/write/validation/git operation を行わないよう明示した。
---
<!-- event: implementation_report author: yoi-coder-00001KVXK0WEA-pdk-service-events at: 2026-06-25T07:38:44Z -->
## Implementation report
Implementation report:
対象 Ticket:
- `00001KVXK0WEA` — Update Plugin WIT PDK templates for service event runtime
実装概要:
- Plugin authoring surface を Component Model-only / Service ingress event / output command model に合わせて更新した。
変更ファイル:
- `resources/plugin/wit/yoi-plugin-instance-v1.wit`
- Service ingress event JSON と `output_commands` / `websocket_send` の authoring 向け説明を追加。
- `resources/plugin/wit/yoi-plugin-tool-v1.wit`
- current runtime 前提の説明に整理。
- `crates/plugin-pdk/src/lib.rs`
- `PluginIngressEvent``ingress_name`, `created_at`, `attempt`, `correlation_id` を追加。
- `ServiceOutput`, `ServiceOutputCommand`, `ServiceOutputCommandKind` を追加。
- `PluginIngressEvent::websocket_text()`, `websocket_send()`, `ServiceOutput::websocket_send()` を追加。
- `Plugin::handle_ingress` の戻り値を `ServiceOutput` に変更。
- `crates/plugin-pdk/tests/template.rs`
- PDK helper / WIT / template consistency tests を追加・更新。
- `resources/plugin/templates/rust-component-instance/*`
- Service-oriented template に更新。
- guest-side `recv(timeout)` loop ではなく、`handle_ingress` で ingress event を受け、`ServiceOutput::websocket_send` で output command を返す構成に変更。
- `plugin.toml``runtime.kind = "wasm-component"` のみ。
- Tool handler も残し、Tool Plugin authoring support を維持。
- `crates/yoi/src/plugin_cli.rs`, `crates/yoi/src/main.rs`
- `yoi plugin new rust-component-service <path>` を追加。
- service template の `new` / `check` / `pack` consistency を unit test で確認。
- CLI usage を更新。
- `docs/development/plugin-development.md`
- Service/Ingress は host-owned ingress event + output command model として記述。
- Service WebSocket integration で long-running guest `recv(timeout)` loop を推奨しない内容に更新。
Validation:
- `cargo test -p yoi-plugin-pdk`: success
- `cargo test -p yoi plugin`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success (`nix-exit:0`)
- Template cargo-check: success
- `cargo test -p yoi-plugin-pdk` 内で `rust-component-tool` / `rust-component-instance``cargo check --target wasm32-unknown-unknown --offline --target-dir <temp>` を実行し、temporary target dir を使用。
Commit:
- `7a4fd975268b655dca2f0861b8f2bd767c1e914f feat: update plugin service authoring templates`
Residual risks / notes:
- Runtime envelope の再設計は行っていない。
- protocol-specific integration / secret injection / reconnect policy / runtime reimplementation は範囲外として未実装。
---
<!-- event: review author: yoi-reviewer-00001KVXK0WEA-pdk-service-events at: 2026-06-25T07:44:48Z status: request_changes -->
## Review: request changes
Review result: request_changes
実装は概ね Ticket の方向に沿っているが、Service WebSocket template の manifest が runtime の authority boundary と不整合で、生成テンプレートの中心例が実行時に `websocket_send` できない状態に見える。
Blocker:
- Service template が `websocket_send` に必要な manifest 権限/target を宣言していない。
- `resources/plugin/templates/rust-component-instance/src/lib.rs``ServiceOutput::websocket_send(...)` を返す Service/WebSocket 例になっている。
- `resources/plugin/templates/rust-component-instance/plugin.toml``surface/tool/service/ingress``tool/service/ingress` permission はあるが、`{ kind = "host_api", api = "websocket" }` がない。
-`plugin.toml` には matching `[[websocket]]` target declaration もない。
- runtime 側の `websocket_send` 処理は manifest permissions の `host_api.websocket`、grants permissions の `host_api.websocket`、manifest の `[[websocket]]` target declaration、enablement 側の usable websocket grant を要求する。
- そのため生成された service template は `yoi plugin check/pack` は通っても、実行時に `requested host_api.websocket permission is missing` / `manifest host_api.websocket target declaration is missing` 系で拒否されるはず。
Required fixes:
- `resources/plugin/templates/rust-component-instance/plugin.toml` に少なくとも以下を追加する。
- `permissions``{ kind = "host_api", api = "websocket" }`
- `[[websocket]]` target declaration(例: `scheme = "wss"`, `host = "example.com"`, `path_prefixes = ["/socket"]`
- template/CLI tests に、service template が `host_api.websocket` permission と matching `[[websocket]]` declaration を含むことの assertion を追加する。
- 必要なら README/docs で、enablement grants も同じ WebSocket target を明示的に許可する必要があることを補足する。
Good points:
- WIT は `handle-ingress`、ingress event envelope、`output_commands` / `websocket_send` 例を記述している。
- PDK は `PluginIngressEvent``ServiceOutput``ServiceOutputCommand``websocket_text()` / `websocket_send()` helper を追加し、runtime envelope 形状に概ね合っている。
- Service template は `recv(timeout)` polling loop を推奨しておらず、`handle_ingress` + `ServiceOutput::websocket_send` の形になっている。
- template `runtime.kind``wasm-component` で、raw `wasm` template 復活は見当たらない。
- `yoi plugin new rust-component-service` は既存の path-safety 方針上に追加されており、広い CLI redesign や runtime reimplementation は見当たらない。
Validation:
- reviewer は read-only 指示のため cargo/nix/git diff validation は再実行していない。
- Ticket item/thread、IntentPacket、`cd200630..HEAD` の変更、WIT / PDK / template / CLI / docs / runtime envelope 関連箇所を静的確認した。
---
<!-- event: implementation_report author: yoi-coder-00001KVXK0WEA-pdk-service-events at: 2026-06-25T07:53:08Z -->
## Implementation report
Follow-up implementation report after review request_changes:
対応内容:
- Reviewer blocker に対応し、Service template の `websocket_send` output command に必要な runtime authority を追加した。
変更点:
- `resources/plugin/templates/rust-component-instance/plugin.toml`
- `permissions``{ kind = "host_api", api = "websocket" }` を追加。
- matching target として `[[websocket]]` を追加。
- `scheme = "wss"`
- `host = "example.com"`
- `path_prefixes = ["/socket"]`
- `crates/plugin-pdk/tests/template.rs`
- service template manifest に `host_api.websocket` permission が含まれることを検証。
- `[[websocket]]` target が `wss://example.com/socket` に対応していることを検証。
- `crates/yoi/src/plugin_cli.rs`
- `yoi plugin new rust-component-service ...` で生成される manifest に `host_api.websocket` permission / `[[websocket]]` declaration が含まれることを CLI test に追加。
- `resources/plugin/templates/rust-component-instance/README.md`
- manifest 側の `host_api.websocket` / `[[websocket]]` declaration と、enablement grants 側でも同じ WebSocket target を明示許可する必要があることを追記。
- `docs/development/plugin-development.md`
- Service ingress / output command の minimal manifest example に `host_api.websocket` permission と `[[websocket]]` target を追加。
- `websocket_send` output command には manifest declaration だけでなく runtime enablement grants も必要であることを明記。
Validation:
- `cargo test -p yoi-plugin-pdk`: success
- `cargo test -p yoi plugin`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success (`nix-exit:0`)
Commit:
- `6c8998878dc1d59ad70da3280efd503a9a060ebb fix: declare plugin service websocket authority`
Residual risks / notes:
- なし。runtime envelope の再設計、raw wasm compatibility、protocol-specific integration、secret/reconnect policy、runtime reimplementation は行っていない。
---
<!-- event: review author: yoi-reviewer-00001KVXK0WEA-pdk-service-events at: 2026-06-25T07:55:26Z status: approve -->
## Review: approve
Review result: approve
前回 blocker は解消されている。
確認した証拠:
- `resources/plugin/templates/rust-component-instance/plugin.toml`
- `permissions``{ kind = "host_api", api = "websocket" }` が追加済み。
- `[[websocket]]` target が追加済みで、`wss://example.com/socket` と一致する `scheme = "wss"`, `host = "example.com"`, `path_prefixes = ["/socket"]` を宣言している。
- `runtime.kind = "wasm-component"` のままで、raw `wasm` 互換は導入されていない。
- Service template README:
- `websocket_send` には manifest の `host_api.websocket` と matching `[[websocket]]` target が必要で、enablement grants も同じ target を許可する必要がある、と明記済み。
- Development docs:
- Service ingress/output command 節で `websocket_send` に必要な `host_api.websocket` permission / `[[websocket]]` target / enablement grant 境界を説明している。
- polling `recv(timeout)` loop ではなく ingress event / output command pattern を推奨する説明を維持している。
- Tests:
- PDK template test が service template manifest の websocket permission と matching target を assert するよう更新されている。
- CLI `plugin new rust-component-service` test も生成 manifest に websocket permission / `[[websocket]]` / host / path_prefix が含まれることを assert している。
リスク確認:
- 前回懸念した「template は `ServiceOutput::websocket_send` を返すのに manifest authority が足りない」不整合は修正済み。
- 今回差分は template/docs/tests の authority declaration 補正に閉じており、runtime redesign、protocol-specific integration、secret injection、raw-WASM compatibility の追加は見当たらない。
検証:
- reviewer は read-only 指定のため `cargo` / `nix` / `git diff --check` は再実行していない。
- `7a4fd975..HEAD` と fix commit `6c899887` の静的レビューのみ実施。
---
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-25T07:57:03Z -->
## Implementation report
Integration outcome:
- Coder `yoi-coder-00001KVXK0WEA-pdk-service-events` が implementation branch `work/00001KVXK0WEA-plugin-pdk-service-events` に実装と review fix を commit した。
- implementation commit: `7a4fd975 feat: update plugin service authoring templates`
- fix commit: `6c899887 fix: declare plugin service websocket authority`
- Reviewer `yoi-reviewer-00001KVXK0WEA-pdk-service-events` は初回 review で request_changes。Service template が `ServiceOutput::websocket_send` を返す一方で `host_api.websocket` permission と matching `[[websocket]]` target declaration を持たない点を blocker とした。
- Coder は service template manifest / tests / docs を修正し、follow-up review は `approve`。template authority declaration、enablement grants guidance、runtime envelope alignment、raw-WASM compatibility 非復活を確認した。
- Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration` で branch `work/00001KVXK0WEA-plugin-pdk-service-events` を merge 済み。
- merge commit: `8d4fee23 merge: 00001KVXK0WEA plugin pdk service events`
Implemented behavior:
- WIT docs/descriptionsを Service ingress event JSON と `output_commands` / `websocket_send` authoring に合わせて更新。
- `crates/plugin-pdk``PluginIngressEvent` 拡張、`ServiceOutput` / `ServiceOutputCommand` / `ServiceOutputCommandKind``websocket_text` / `websocket_send` helpers を追加。
- `Plugin::handle_ingress` returns `ServiceOutput`
- `resources/plugin/templates/rust-component-instance` を service-oriented template に更新し、`handle_ingress` + `ServiceOutput::websocket_send` pattern を示す。
- service template `plugin.toml``runtime.kind = "wasm-component"``host_api.websocket` permission、matching `[[websocket]]` target を含む。
- `yoi plugin new rust-component-service <path>` を追加し、new/check/pack consistency tests を更新。
- docs は long-running guest `recv(timeout)` loop ではなく host-owned ingress event + output command model を推奨し、enablement grants でも matching websocket target が必要と明記。
Validation in Orchestrator worktree:
- `cargo test -p yoi-plugin-pdk`: success
- `cargo test -p yoi plugin`: success
- `cargo check -p yoi`: success
- `git diff --check`: success
- `nix build .#yoi --no-link`: success
- Template cargo-check is covered by `cargo test -p yoi-plugin-pdk` using temporary target dirs.
Notes:
- This Ticket completes the queued Plugin runtime redesign chain slices that were split from the original broad redesign request.
- No runtime envelope redesign, protocol-specific integration, secret injection, reconnect policy, runtime reimplementation, or raw-WASM compatibility was introduced.
Next action:
- Mark Ticket done after this integration/validation evidence.
- Then stop related child Pods and remove only the child implementation worktree/branch.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-25T07:57:15Z from: inprogress to: done reason: merged_validated field: state -->
## State changed
Implementation is merged into the orchestration branch and validated.
Evidence:
- merge commit: `8d4fee23 merge: 00001KVXK0WEA plugin pdk service events`
- reviewer result: approve after requested changes were fixed
- validation in `/home/hare/Projects/yoi/.worktree/orchestration` succeeded:
- `cargo test -p yoi-plugin-pdk`
- `cargo test -p yoi plugin`
- `cargo check -p yoi`
- `git diff --check`
- `nix build .#yoi --no-link`
Closure is not performed here; this state records implementation completion after merge/validation.
---
+3
View File
@@ -0,0 +1,3 @@
workspace_id = "0197a949-4b6b-7f2a-9d9a-1f87e3a4c5b6"
created_at = "2026-06-23T00:00:00Z"
display_name = "yoi"
+1
View File
@@ -31,6 +31,7 @@ Podの状態から純粋に再現可能で、且つ揮発性の無い操作で
明示的に指示されない限り、読み取り以外の操作は控えること。
基本はworktree上の一時的なブランチでコミットを重ね、メインブランチに取り込む運用をしている。
Orchestrator の cwd が orchestration 用ブランチ/worktree の場合、通常作業では親ブランチの dirty state を気にしない。
コミットメッセージは適当に`<prefix>: *簡潔な1行*`で書いている。
外部の参考プロジェクトは必要に応じてローカルの外部 checkout からReadすること。
Generated
+28 -67
View File
@@ -2913,7 +2913,6 @@ dependencies = [
"tracing",
"tungstenite",
"uuid",
"wasmi",
"wasmtime",
"wat",
"workflow",
@@ -3035,6 +3034,7 @@ dependencies = [
"serde",
"serde_json",
"tokio",
"ts-rs",
"uuid",
]
@@ -4034,12 +4034,6 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
@@ -4052,16 +4046,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "string-interner"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23de088478b31c349c9ba67816fa55d9355232d63c3afea8bf513e31f0f1d2c0"
dependencies = [
"hashbrown 0.15.5",
"serde",
]
[[package]]
name = "string_cache"
version = "0.8.9"
@@ -4696,6 +4680,28 @@ dependencies = [
"toml",
]
[[package]]
name = "ts-rs"
version = "12.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "756050066659291d47a554a9f558125db17428b073c5ffce1daf5dcb0f7231d8"
dependencies = [
"thiserror 2.0.18",
"ts-rs-macros",
]
[[package]]
name = "ts-rs-macros"
version = "12.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d90eea51bc7988ef9e674bf80a85ba6804739e535e9cab48e4bb34a8b652aa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
"termcolor",
]
[[package]]
name = "ttf-parser"
version = "0.25.1"
@@ -5073,56 +5079,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "wasmi"
version = "0.51.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb321403ce594274827657a908e13d1d9918aa02257b8bf8391949d9764023ff"
dependencies = [
"spin",
"wasmi_collections",
"wasmi_core",
"wasmi_ir",
"wasmparser 0.228.0",
]
[[package]]
name = "wasmi_collections"
version = "0.51.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9b8e98e45a2a534489f8225e765cbf1cb9a3078072605e58158910cf4749172"
dependencies = [
"string-interner",
]
[[package]]
name = "wasmi_core"
version = "0.51.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c25f375c0cdf14810eab07f532f61f14d4966f09c747a55067fdf3196e8512e6"
dependencies = [
"libm",
]
[[package]]
name = "wasmi_ir"
version = "0.51.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624e2a68a4293ecb8f564260b68394b29cf3b3edba6bce35532889a2cb33c3d9"
dependencies = [
"wasmi_core",
]
[[package]]
name = "wasmparser"
version = "0.228.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4abf1132c1fdf747d56bbc1bb52152400c70f336870f968b85e89ea422198ae3"
dependencies = [
"bitflags 2.11.0",
"indexmap",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
@@ -5987,6 +5943,7 @@ dependencies = [
"manifest",
"memory",
"pod",
"pod-store",
"project-record",
"serde",
"serde_json",
@@ -6027,6 +5984,7 @@ version = "0.1.0"
dependencies = [
"async-trait",
"axum",
"chrono",
"manifest",
"pod-store",
"project-record",
@@ -6034,12 +5992,15 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
"sha2 0.11.0",
"tempfile",
"thiserror 2.0.18",
"ticket",
"tokio",
"toml",
"tower",
"tracing",
"uuid",
]
[[package]]
+4 -1
View File
@@ -16,7 +16,10 @@ pub mod ticket_role;
pub use runtime_command::PodRuntimeCommand;
pub use pod_client::PodClient;
pub use spawn::{SpawnConfig, SpawnError, SpawnReady, spawn_pod};
pub use spawn::{
PodProcessLaunchConfig, PodProcessLaunchOptions, SpawnConfig, SpawnError, SpawnReady,
spawn_pod, spawn_pod_with_options,
};
pub use ticket_role::{
TicketRef, TicketRoleLaunchContext, TicketRoleLaunchError, TicketRoleLaunchOptions,
TicketRoleLaunchPlan, TicketRoleLaunchResult, TicketRolePreRunWarning, launch_ticket_role_pod,
+51 -18
View File
@@ -23,7 +23,7 @@ const READY_PREFIX: &str = "YOI-READY\t";
const READY_TIMEOUT: Duration = Duration::from_secs(20);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpawnConfig {
pub struct PodProcessLaunchConfig {
pub runtime_command: PodRuntimeCommand,
/// `pod.name` として使う識別子。runtime ディレクトリ
/// (`manifest::paths::pod_runtime_dir`) の解決と、ready 行に乗る
@@ -32,9 +32,6 @@ pub struct SpawnConfig {
/// Optional reusable Profile selector. Pod identity is always supplied
/// separately with `--pod`; profile selection must not imply a name.
pub profile: Option<String>,
/// Process-local Ticket role marker supplied only by Ticket role launches.
/// This does not alter prompts, manifests, or Ticket claim records.
pub ticket_role: Option<String>,
/// Explicit runtime workspace root. The child receives it via
/// `--workspace` so startup does not infer workspace identity from the
/// parent process cwd.
@@ -48,6 +45,28 @@ pub struct SpawnConfig {
pub resume_from: Option<Uuid>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PodProcessLaunchOptions {
/// Extra child CLI arguments supplied by an upper resolver layer. The
/// low-level launch config intentionally does not model Ticket IDs,
/// Ticket roles, orchestration roles, executable authority, or raw
/// browser-provided profile/cwd/workspace inputs.
pub extra_args: Vec<String>,
}
impl PodProcessLaunchOptions {
pub fn with_hidden_arg(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
self.extra_args.extend([name.into(), value.into()]);
self
}
pub fn is_empty(&self) -> bool {
self.extra_args.is_empty()
}
}
pub type SpawnConfig = PodProcessLaunchConfig;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpawnReady {
pub pod_name: String,
@@ -112,7 +131,7 @@ impl From<io::Error> for SpawnError {
}
}
fn runtime_args(config: &SpawnConfig) -> Vec<String> {
fn runtime_args(config: &PodProcessLaunchConfig, options: &PodProcessLaunchOptions) -> Vec<String> {
let mut args = vec![
"--workspace".to_string(),
config.workspace_root.display().to_string(),
@@ -130,9 +149,7 @@ fn runtime_args(config: &SpawnConfig) -> Vec<String> {
args.extend(["--profile".to_string(), profile.clone()]);
}
}
if let Some(ticket_role) = &config.ticket_role {
args.extend(["--ticket-role".to_string(), ticket_role.clone()]);
}
args.extend(options.extra_args.clone());
args
}
@@ -140,7 +157,21 @@ fn runtime_args(config: &SpawnConfig) -> Vec<String> {
///
/// `progress` は ready 行を見つけるまでに観測した stderr の各行で呼ばれる
/// (ready 行自体は除外される)。UI の表示更新や E2E ログ取得に使う。
pub async fn spawn_pod<F>(config: SpawnConfig, mut progress: F) -> Result<SpawnReady, SpawnError>
pub async fn spawn_pod<F>(
config: PodProcessLaunchConfig,
progress: F,
) -> Result<SpawnReady, SpawnError>
where
F: FnMut(&str),
{
spawn_pod_with_options(config, PodProcessLaunchOptions::default(), progress).await
}
pub async fn spawn_pod_with_options<F>(
config: PodProcessLaunchConfig,
options: PodProcessLaunchOptions,
mut progress: F,
) -> Result<SpawnReady, SpawnError>
where
F: FnMut(&str),
{
@@ -158,7 +189,7 @@ where
.stdout(Stdio::null())
.stderr(Stdio::from(stderr_file))
.process_group(0);
for arg in runtime_args(&config) {
for arg in runtime_args(&config, &options) {
command.arg(arg);
}
let mut child = command
@@ -332,12 +363,11 @@ mod tests {
use super::*;
use std::ffi::OsString;
fn base_config() -> SpawnConfig {
SpawnConfig {
fn base_config() -> PodProcessLaunchConfig {
PodProcessLaunchConfig {
runtime_command: PodRuntimeCommand::new("/bin/yoi", vec![OsString::from("pod")]),
pod_name: "explicit-pod".to_string(),
profile: Some("project:companion".to_string()),
ticket_role: None,
workspace_root: PathBuf::from("/work/other-project"),
cwd: None,
resume_from: None,
@@ -347,7 +377,7 @@ mod tests {
#[test]
fn runtime_args_keep_workspace_pod_and_profile_separate() {
assert_eq!(
runtime_args(&base_config()),
runtime_args(&base_config(), &PodProcessLaunchOptions::default()),
vec![
"--workspace",
"/work/other-project",
@@ -364,7 +394,7 @@ mod tests {
let mut config = base_config();
config.resume_from = Some(Uuid::nil());
assert_eq!(
runtime_args(&config),
runtime_args(&config, &PodProcessLaunchOptions::default()),
vec![
"--workspace",
"/work/other-project",
@@ -377,13 +407,16 @@ mod tests {
}
#[test]
fn runtime_args_do_not_include_child_cwd() {
fn runtime_args_include_upper_resolver_extra_args_without_child_cwd() {
let mut config = base_config();
config.ticket_role = Some("orchestrator".to_string());
config.cwd = Some(PathBuf::from("/work/main/.worktree/orchestration/yoi"));
assert_eq!(
runtime_args(&config),
runtime_args(
&config,
&PodProcessLaunchOptions::default()
.with_hidden_arg("--ticket-role", "orchestrator"),
),
vec![
"--workspace",
"/work/other-project",
+54 -22
View File
@@ -14,7 +14,10 @@ use thiserror::Error;
pub use ticket::config::TicketRole;
use ticket::config::{TicketConfig, TicketConfigError, TicketRoleLaunchConfigError};
use crate::{PodClient, PodRuntimeCommand, SpawnConfig, SpawnError, SpawnReady, spawn_pod};
use crate::{
PodClient, PodProcessLaunchConfig, PodProcessLaunchOptions, PodRuntimeCommand, SpawnError,
SpawnReady, spawn_pod_with_options,
};
const MAX_FIELD_CHARS: usize = 8_000;
const MAX_POD_NAME_CHARS: usize = 80;
@@ -170,20 +173,24 @@ impl TicketRoleLaunchPlan {
pub fn spawn_config(
&self,
runtime_command: PodRuntimeCommand,
) -> Result<SpawnConfig, TicketRoleLaunchError> {
) -> Result<PodProcessLaunchConfig, TicketRoleLaunchError> {
if self.profile == "inherit" {
return Err(TicketRoleLaunchError::UnsupportedInheritProfile);
}
Ok(SpawnConfig {
Ok(PodProcessLaunchConfig {
runtime_command,
pod_name: self.pod_name.clone(),
profile: Some(self.profile.clone()),
ticket_role: Some(self.role.as_str().to_string()),
workspace_root: self.workspace_root.clone(),
cwd: self.cwd.clone(),
resume_from: None,
})
}
pub fn spawn_options(&self) -> PodProcessLaunchOptions {
PodProcessLaunchOptions::default()
.with_hidden_arg("--ticket-role", self.role.as_str().to_string())
}
}
/// Result of executing a Ticket role launch.
@@ -191,9 +198,28 @@ impl TicketRoleLaunchPlan {
pub struct TicketRoleLaunchResult {
pub plan: TicketRoleLaunchPlan,
pub ready: SpawnReady,
/// Evidence that the spawned worker accepted the initial Run request.
/// This is intentionally distinct from process readiness: a socket
/// snapshot only proves that the runtime is reachable, not that the
/// worker operation was durably queued/started.
pub acceptance_evidence: TicketRoleLaunchAcceptanceEvidence,
pub pre_run_warnings: Vec<TicketRolePreRunWarning>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TicketRoleLaunchAcceptanceEvidence {
pub pod_name: String,
pub accepted_run_segments: usize,
pub event: TicketRoleLaunchAcceptanceEvent,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TicketRoleLaunchAcceptanceEvent {
UserMessage,
UserSendInvokeStart,
TurnStart,
}
/// Non-fatal diagnostic produced by bounded pre-run launch actions.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TicketRolePreRunWarning {
@@ -369,7 +395,9 @@ where
F: FnMut(&str),
{
let plan = plan_ticket_role_launch(context)?;
let ready = spawn_pod(plan.spawn_config(runtime_command)?, progress).await?;
let spawn_config = plan.spawn_config(runtime_command)?;
let spawn_options = plan.spawn_options();
let ready = spawn_pod_with_options(spawn_config, spawn_options, progress).await?;
let mut client = PodClient::connect(&ready.socket_path)
.await
.map_err(|source| TicketRoleLaunchError::Connect {
@@ -377,10 +405,17 @@ where
source,
})?;
let pre_run_warnings = run_pre_run_options_then_send_run(&mut client, &plan, &options).await?;
wait_for_run_acceptance(&mut client, &plan.run_segments, RUN_ACCEPTANCE_TIMEOUT).await?;
let acceptance_event =
wait_for_run_acceptance(&mut client, &plan.run_segments, RUN_ACCEPTANCE_TIMEOUT).await?;
let acceptance_evidence = TicketRoleLaunchAcceptanceEvidence {
pod_name: ready.pod_name.clone(),
accepted_run_segments: plan.run_segments.len(),
event: acceptance_event,
};
Ok(TicketRoleLaunchResult {
plan,
ready,
acceptance_evidence,
pre_run_warnings,
})
}
@@ -471,18 +506,20 @@ async fn wait_for_run_acceptance(
client: &mut PodClient,
expected_segments: &[Segment],
timeout: Duration,
) -> Result<(), TicketRoleLaunchError> {
) -> Result<TicketRoleLaunchAcceptanceEvent, TicketRoleLaunchError> {
let wait = async {
loop {
let Some(event) = client.next_event().await else {
return Err(TicketRoleLaunchError::RunAcceptanceClosed);
};
match event {
Event::UserMessage { segments } if segments == expected_segments => return Ok(()),
Event::UserMessage { segments } if segments == expected_segments => {
return Ok(TicketRoleLaunchAcceptanceEvent::UserMessage);
}
Event::InvokeStart {
kind: InvokeKind::UserSend,
}
| Event::TurnStart { .. } => return Ok(()),
} => return Ok(TicketRoleLaunchAcceptanceEvent::UserSendInvokeStart),
Event::TurnStart { .. } => return Ok(TicketRoleLaunchAcceptanceEvent::TurnStart),
Event::Error { code, message } => {
return Err(TicketRoleLaunchError::RunRejected { code, message });
}
@@ -547,9 +584,6 @@ fn append_operation_targets(out: &mut String, context: &TicketRoleLaunchContext)
if context.role != TicketRole::Orchestrator {
return;
}
if context.original_workspace_root.is_none() && context.target_workspace_root.is_none() {
return;
}
out.push_str("\nOrchestrator operation targets:\n");
push_bounded_bullet(
@@ -557,13 +591,6 @@ fn append_operation_targets(out: &mut String, context: &TicketRoleLaunchContext)
"implementation_worktree_root",
&context.implementation_worktree_root().display().to_string(),
);
if context.target_workspace_root.is_some() {
push_bounded_bullet(
out,
"merge_target_workspace_root",
&context.target_workspace_root().display().to_string(),
);
}
}
fn default_pod_name(role: TicketRole, ticket: Option<&TicketRef>) -> String {
@@ -706,6 +733,7 @@ mod tests {
context_tokens: 0,
},
status: PodStatus::Idle,
in_flight: protocol::InFlightSnapshot::default(),
}
}
@@ -1035,8 +1063,12 @@ workflow = "ticket-review-workflow"
.unwrap();
assert_eq!(spawn.pod_name, "reviewer-fixed");
assert_eq!(spawn.profile.as_deref(), Some("builtin:default"));
assert_eq!(spawn.ticket_role.as_deref(), Some("reviewer"));
assert_eq!(spawn.workspace_root, temp.path());
assert!(spawn.cwd.is_none());
assert_eq!(
plan.spawn_options().extra_args,
vec!["--ticket-role".to_string(), "reviewer".to_string()]
);
}
#[test]
@@ -1152,7 +1184,7 @@ workflow = "ticket-review-workflow"
assert!(text.contains("Orchestrator operation targets:"));
assert!(text.contains("implementation_worktree_root"));
assert!(text.contains("merge_target_workspace_root"));
assert!(!text.contains("merge_target_workspace_root"));
assert!(!text.contains("Workspace routing context:"));
assert!(!text.contains("role_workspace_root"));
assert!(!text.contains("role_cwd"));
+52 -6
View File
@@ -63,7 +63,7 @@ impl ResolvedAuth {
}
}
fn header_value_for_diagnostics(headers: &HeaderMap, name: &'static HeaderName) -> Option<String> {
fn header_value_for_diagnostics(headers: &HeaderMap, name: &str) -> Option<String> {
headers
.get(name)
.and_then(|value| value.to_str().ok())
@@ -74,10 +74,25 @@ fn header_value_for_diagnostics(headers: &HeaderMap, name: &'static HeaderName)
fn response_header_diagnostics(headers: &HeaderMap) -> serde_json::Value {
serde_json::json!({
"content_type": header_value_for_diagnostics(headers, &CONTENT_TYPE),
"content_encoding": header_value_for_diagnostics(headers, &CONTENT_ENCODING),
"transfer_encoding": header_value_for_diagnostics(headers, &TRANSFER_ENCODING),
"content_length": header_value_for_diagnostics(headers, &CONTENT_LENGTH),
"content_type": header_value_for_diagnostics(headers, CONTENT_TYPE.as_str()),
"content_encoding": header_value_for_diagnostics(headers, CONTENT_ENCODING.as_str()),
"transfer_encoding": header_value_for_diagnostics(headers, TRANSFER_ENCODING.as_str()),
"content_length": header_value_for_diagnostics(headers, CONTENT_LENGTH.as_str()),
})
}
fn request_header_diagnostics(headers: &HeaderMap) -> serde_json::Value {
serde_json::json!({
"content_type": header_value_for_diagnostics(headers, CONTENT_TYPE.as_str()),
"content_encoding": header_value_for_diagnostics(headers, CONTENT_ENCODING.as_str()),
"accept": header_value_for_diagnostics(headers, ACCEPT.as_str()),
"openai_beta": header_value_for_diagnostics(headers, "openai-beta"),
"session_id_present": headers.contains_key("session-id"),
"thread_id_present": headers.contains_key("thread-id"),
"legacy_session_id_present": headers.contains_key("session_id"),
"legacy_thread_id_present": headers.contains_key("thread_id"),
"x_client_request_id_present": headers.contains_key("x-client-request-id"),
"chatgpt_account_id_present": headers.contains_key("chatgpt-account-id"),
})
}
@@ -211,6 +226,11 @@ impl<S: Scheme> HttpTransport<S> {
let value = HeaderValue::from_str(cache_key).map_err(|e| {
ClientError::Config(format!("invalid Codex conversation header: {e}"))
})?;
// Codex CLI sends hyphenated session/thread headers to the
// ChatGPT Codex backend. Keep the legacy underscore header for
// existing traces/backends while exposing the current Codex shape.
headers.insert(HeaderName::from_static("session-id"), value.clone());
headers.insert(HeaderName::from_static("thread-id"), value.clone());
headers.insert(HeaderName::from_static("session_id"), value.clone());
headers.insert(HeaderName::from_static("x-client-request-id"), value);
}
@@ -451,6 +471,7 @@ impl<S: Scheme + Clone + 'static> LlmClient for HttpTransport<S> {
json!({
"elapsed_ms": headers_started.elapsed().as_millis() as u64,
"headers_len": headers.len(),
"headers": request_header_diagnostics(&headers),
}),
);
headers
@@ -486,6 +507,7 @@ impl<S: Scheme + Clone + 'static> LlmClient for HttpTransport<S> {
json!({
"elapsed_ms": stream_headers_started.elapsed().as_millis() as u64,
"headers_len": headers.len(),
"headers": request_header_diagnostics(&headers),
}),
);
@@ -520,15 +542,19 @@ impl<S: Scheme + Clone + 'static> LlmClient for HttpTransport<S> {
return Err(error);
}
};
let final_request_headers = request_header_diagnostics(&headers);
let body_compression = request_body.encoding().to_string();
emit_transport_trace(
&request,
"transport_body_encode_done",
json!({
"elapsed_ms": encode_started.elapsed().as_millis() as u64,
"encoding": request_body.encoding(),
"encoding": body_compression.as_str(),
"body_compression": body_compression.as_str(),
"raw_json_bytes": request_body.raw_json_bytes(),
"wire_bytes": request_body.wire_bytes(),
"request_shape": body_shape.clone(),
"headers": final_request_headers.clone(),
}),
);
@@ -586,6 +612,8 @@ impl<S: Scheme + Clone + 'static> LlmClient for HttpTransport<S> {
"context_length_exceeded": context_length_exceeded,
"provider_usage_absent": context_length_exceeded,
"request_shape": body_shape.clone(),
"request_headers": final_request_headers.clone(),
"body_compression": body_compression.as_str(),
}),
);
return Err(error);
@@ -817,10 +845,26 @@ mod tests {
let encoded = transport.encode_request_body(&body, &mut headers).unwrap();
assert_eq!(headers.get(ACCEPT).unwrap(), "text/event-stream");
assert_eq!(headers.get("session-id").unwrap(), "segment-123");
assert_eq!(headers.get("thread-id").unwrap(), "segment-123");
assert_eq!(headers.get("session_id").unwrap(), "segment-123");
assert_eq!(headers.get("x-client-request-id").unwrap(), "segment-123");
assert_eq!(headers.get(CONTENT_ENCODING).unwrap(), "zstd");
let diagnostics = request_header_diagnostics(&headers);
assert_eq!(diagnostics["content_type"], "application/json");
assert_eq!(diagnostics["content_encoding"], "zstd");
assert_eq!(diagnostics["accept"], "text/event-stream");
assert!(diagnostics["session_id_present"].as_bool().unwrap());
assert!(diagnostics["thread_id_present"].as_bool().unwrap());
assert!(diagnostics["legacy_session_id_present"].as_bool().unwrap());
assert!(
diagnostics["x_client_request_id_present"]
.as_bool()
.unwrap()
);
assert!(diagnostics["chatgpt_account_id_present"].as_bool().unwrap());
let RequestBody::CompressedJson {
bytes: compressed,
raw_json_bytes,
@@ -850,6 +894,8 @@ mod tests {
let encoded = transport.encode_request_body(&body, &mut headers).unwrap();
assert_eq!(headers.get(ACCEPT).unwrap(), "text/event-stream");
assert!(headers.get("session-id").is_none());
assert!(headers.get("thread-id").is_none());
assert!(headers.get("session_id").is_none());
assert!(headers.get("x-client-request-id").is_none());
assert!(headers.get(CONTENT_ENCODING).is_none());
+69 -192
View File
@@ -560,13 +560,14 @@ impl PluginPackageManifest {
}
}
pub const PLUGIN_RUNTIME_WASM_KIND: &str = "wasm";
pub const PLUGIN_RUNTIME_WASM_ABI: &str = "yoi-plugin-wasm-1";
/// Manifest runtime kind for WebAssembly Component Model Tool packages.
const LEGACY_PLUGIN_RUNTIME_WASM_KIND: &str = "wasm";
const LEGACY_PLUGIN_RUNTIME_WASM_ABI: &str = "yoi-plugin-wasm-1";
/// Manifest runtime kind for current WebAssembly Component Model packages.
///
/// Component runtime manifests must set `component` to the packaged component
/// artifact path and `world` to [`PLUGIN_COMPONENT_TOOL_WORLD`]. Raw core-Wasm
/// packages remain explicit `kind = "wasm"` plus `abi = "yoi-plugin-wasm-1"`.
/// artifact path and `world` to [`PLUGIN_COMPONENT_TOOL_WORLD`] or
/// [`PLUGIN_COMPONENT_INSTANCE_WORLD`]. Legacy raw core-Wasm manifests are
/// intentionally rejected by validation; `wasm-component` is the public runtime.
pub const PLUGIN_RUNTIME_COMPONENT_KIND: &str = "wasm-component";
pub const PLUGIN_COMPONENT_TOOL_WORLD: &str = "yoi:plugin/tool@1.0.0";
pub const PLUGIN_COMPONENT_INSTANCE_WORLD: &str = "yoi:plugin/instance@1.0.0";
@@ -1060,158 +1061,6 @@ pub fn resolve_plugin_config_for_startup(
snapshot
}
/// Load the recorded WASM runtime module for a resolved plugin package.
///
/// Restore and execution paths use this helper instead of reading arbitrary
/// package paths directly so module selection remains tied to the resolved
/// package identity, runtime manifest entry, and deterministic package digest.
pub fn read_resolved_plugin_runtime_module(
record: &ResolvedPluginRecord,
limits: &PluginDiscoveryLimits,
) -> Result<Vec<u8>, PluginDiagnostic> {
let runtime = record.manifest.runtime.as_ref().ok_or_else(|| {
PluginDiagnostic::new(
PluginDiagnosticKind::Missing,
PluginDiagnosticPhase::Manifest,
"resolved plugin package does not declare a WASM runtime",
)
.with_source(record.source)
.with_identity(&record.identity)
.with_package(&record.package_label)
.with_digest(&record.digest)
})?;
if runtime.kind != PLUGIN_RUNTIME_WASM_KIND {
return Err(PluginDiagnostic::new(
PluginDiagnosticKind::Api,
PluginDiagnosticPhase::Manifest,
"plugin runtime kind is unsupported",
)
.with_source(record.source)
.with_identity(&record.identity)
.with_package(&record.package_label)
.with_digest(&record.digest));
}
if runtime.abi.as_deref() != Some(PLUGIN_RUNTIME_WASM_ABI) {
return Err(PluginDiagnostic::new(
PluginDiagnosticKind::Api,
PluginDiagnosticPhase::Manifest,
"plugin WASM ABI is unsupported",
)
.with_source(record.source)
.with_identity(&record.identity)
.with_package(&record.package_label)
.with_digest(&record.digest));
}
let entry = runtime.entry.as_deref().ok_or_else(|| {
PluginDiagnostic::new(
PluginDiagnosticKind::Missing,
PluginDiagnosticPhase::Manifest,
"plugin WASM runtime entry is required",
)
.with_source(record.source)
.with_identity(&record.identity)
.with_package(&record.package_label)
.with_digest(&record.digest)
})?;
let metadata = fs::metadata(&record.package_path).map_err(|error| {
PluginDiagnostic::new(
PluginDiagnosticKind::Io,
PluginDiagnosticPhase::Discovery,
format!(
"resolved plugin package metadata could not be read: {}",
safe_io_error(&error)
),
)
.with_source(record.source)
.with_identity(&record.identity)
.with_package(&record.package_label)
.with_digest(&record.digest)
})?;
if !metadata.is_file() {
return Err(PluginDiagnostic::new(
PluginDiagnosticKind::Malformed,
PluginDiagnosticPhase::Discovery,
"resolved plugin package is not a regular file",
)
.with_source(record.source)
.with_identity(&record.identity)
.with_package(&record.package_label)
.with_digest(&record.digest));
}
if metadata.len() > limits.max_package_size_bytes {
return Err(PluginDiagnostic::new(
PluginDiagnosticKind::Bounds,
PluginDiagnosticPhase::Discovery,
"resolved plugin package exceeds the configured package size bound",
)
.with_source(record.source)
.with_identity(&record.identity)
.with_package(&record.package_label)
.with_digest(&record.digest));
}
let bytes = fs::read(&record.package_path).map_err(|error| {
PluginDiagnostic::new(
PluginDiagnosticKind::Io,
PluginDiagnosticPhase::Discovery,
format!(
"resolved plugin package content could not be read: {}",
safe_io_error(&error)
),
)
.with_source(record.source)
.with_identity(&record.identity)
.with_package(&record.package_label)
.with_digest(&record.digest)
})?;
let archive = parse_stored_zip(&bytes, &record.package_label, record.source, limits)?;
let actual_digest = deterministic_digest(&archive.files);
if !digest_matches(&record.digest, &actual_digest) {
return Err(PluginDiagnostic::new(
PluginDiagnosticKind::Digest,
PluginDiagnosticPhase::Resolution,
"resolved plugin package digest does not match current package content",
)
.with_source(record.source)
.with_identity(&record.identity)
.with_package(&record.package_label)
.with_digest(actual_digest));
}
validate_manifest_path(
entry,
&archive,
&record.package_label,
record.source,
&record.manifest.id,
)?;
let normalized = normalize_archive_path(entry).ok_or_else(|| {
PluginDiagnostic::new(
PluginDiagnosticKind::Traversal,
PluginDiagnosticPhase::Manifest,
"plugin manifest references a path outside the package root",
)
.with_source(record.source)
.with_identity(&record.identity)
.with_package(&record.package_label)
.with_digest(&record.digest)
})?;
archive.files.get(&normalized).cloned().ok_or_else(|| {
PluginDiagnostic::new(
PluginDiagnosticKind::Missing,
PluginDiagnosticPhase::Manifest,
"plugin runtime module entry is missing from the package",
)
.with_source(record.source)
.with_identity(&record.identity)
.with_package(&record.package_label)
.with_digest(&record.digest)
})
}
/// Reads the WebAssembly Component Model artifact selected by a resolved plugin
/// package manifest while preserving package digest pinning.
pub fn read_resolved_plugin_runtime_component(
@@ -2046,38 +1895,21 @@ fn validate_manifest(
}
if let Some(runtime) = &manifest.runtime {
match runtime.kind.as_str() {
PLUGIN_RUNTIME_WASM_KIND => {
if runtime.abi.as_deref() != Some(PLUGIN_RUNTIME_WASM_ABI) {
return Err(PluginDiagnostic::new(
PluginDiagnosticKind::Api,
PluginDiagnosticPhase::Manifest,
"plugin WASM ABI is unsupported",
)
.with_source(source)
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
.with_package(label));
}
let Some(entry) = runtime.entry.as_deref() else {
return Err(PluginDiagnostic::new(
PluginDiagnosticKind::Missing,
PluginDiagnosticPhase::Manifest,
"plugin WASM runtime entry is required",
)
.with_source(source)
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
.with_package(label));
};
if runtime.component.is_some() || runtime.world.is_some() {
return Err(PluginDiagnostic::new(
PluginDiagnosticKind::Malformed,
PluginDiagnosticPhase::Manifest,
"plugin WASM runtime must not declare component metadata",
)
.with_source(source)
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
.with_package(label));
}
validate_manifest_path(entry, archive, label, source, &manifest.id)?;
LEGACY_PLUGIN_RUNTIME_WASM_KIND => {
return Err(PluginDiagnostic::new(
PluginDiagnosticKind::Api,
PluginDiagnosticPhase::Manifest,
format!(
"legacy raw wasm plugin runtime `{LEGACY_PLUGIN_RUNTIME_WASM_KIND}` / `{}` is retired; use `{PLUGIN_RUNTIME_COMPONENT_KIND}`",
runtime
.abi
.as_deref()
.unwrap_or(LEGACY_PLUGIN_RUNTIME_WASM_ABI)
),
)
.with_source(source)
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
.with_package(label));
}
PLUGIN_RUNTIME_COMPONENT_KIND => {
if runtime.abi.is_some() || runtime.entry.is_some() {
@@ -2844,6 +2676,51 @@ description = "bad"
assert!(err.message.contains("service/ingress"));
}
#[test]
fn legacy_raw_wasm_runtime_manifest_is_rejected() {
let temp = TempDir::new().unwrap();
let workspace = temp.path().join("workspace");
let plugins = workspace.join(".yoi/plugins");
fs::create_dir_all(&plugins).unwrap();
let manifest = r#"
schema_version = 1
id = "legacy"
name = "Legacy"
version = "0.1.0"
surfaces = ["tool"]
[runtime]
kind = "wasm"
entry = "plugin.wasm"
abi = "yoi-plugin-wasm-1"
[[tools]]
name = "Echo"
description = "legacy"
input_schema = { type = "object" }
"#;
write_stored_zip(
&plugins.join("legacy.yoi-plugin"),
&[
("plugin.toml", manifest.as_bytes().to_vec(), 0),
("plugin.wasm", b"not wasm".to_vec(), 0),
],
);
let report = discover_plugins(&PluginDiscoveryOptions::new(&workspace));
assert!(report.packages.is_empty());
let diagnostic = report
.diagnostics
.iter()
.find(|diag| diag.kind == PluginDiagnosticKind::Api)
.unwrap();
assert_eq!(diagnostic.phase, PluginDiagnosticPhase::Manifest);
assert_eq!(diagnostic.identity.as_deref(), Some("project:legacy"));
assert!(diagnostic.message.contains("legacy raw wasm"));
assert!(diagnostic.message.contains(PLUGIN_RUNTIME_COMPONENT_KIND));
}
#[test]
fn discovers_valid_user_and_workspace_packages() {
let temp = TempDir::new().unwrap();
@@ -3521,9 +3398,9 @@ version = "1.0.0"
surfaces = ["tool"]
[runtime]
kind = "wasm"
entry = "plugin.wasm"
abi = "yoi-plugin-wasm-1"
kind = "wasm-component"
component = "plugin.component.wasm"
world = "yoi:plugin/tool@1.0.0"
[[permissions]]
kind = "host_api"
+178 -3
View File
@@ -468,15 +468,48 @@ mod tests {
assert!(error.message().len() <= MAX_ERROR_MESSAGE_BYTES + "".len());
}
#[test]
fn service_output_helper_builds_runtime_command_envelope() {
let event = PluginIngressEvent {
kind: "websocket_text".to_string(),
source: "websocket:wss://example.test/socket".to_string(),
ingress_name: "example_ws".to_string(),
payload: json!({"text":"ping"}),
created_at: "2026-06-25T00:00:00Z".to_string(),
attempt: 1,
correlation_id: "event-1".to_string(),
};
assert_eq!(event.websocket_text(), Some("ping"));
let output =
ServiceOutput::websocket_send(&event, "reply-1", "wss://example.test/socket", "pong")
.unwrap();
let value = serde_json::to_value(output).unwrap();
assert_eq!(value["accepted"], true);
assert_eq!(value["output_commands"][0]["source_event_id"], "event-1");
assert_eq!(value["output_commands"][0]["command_id"], "reply-1");
assert_eq!(value["output_commands"][0]["kind"], "websocket_send");
assert_eq!(value["output_commands"][0]["payload"]["text"], "pong");
assert_eq!(
value["output_commands"][0]["requested_at"],
"2026-06-25T00:00:00Z"
);
}
#[test]
fn wit_constants_match_current_world() {
assert!(TOOL_WIT.contains("package yoi:plugin@1.0.0"));
assert!(TOOL_WIT.contains("world tool"));
assert!(TOOL_WIT.contains("export call"));
assert_eq!(TOOL_WORLD, "yoi:plugin/tool@1.0.0");
assert!(HOST_WIT.contains("interface https"));
assert!(HOST_WIT.contains("interface request"));
assert!(HOST_WIT.contains("interface websocket"));
assert!(HOST_WIT.contains("interface fs"));
assert!(HOST_WIT.contains("%list: func"));
assert!(INSTANCE_WIT.contains("world instance"));
assert!(INSTANCE_WIT.contains("export handle-ingress"));
assert!(INSTANCE_WIT.contains("websocket_send"));
}
}
@@ -488,12 +521,48 @@ pub const PLUGIN_INSTANCE_WORLD: &str = "yoi:plugin/instance@1.0.0";
pub const INSTANCE_WIT: &str =
include_str!("../../../resources/plugin/wit/yoi-plugin-instance-v1.wit");
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct PluginIngressEvent {
pub kind: String,
pub source: String,
#[serde(default)]
pub ingress_name: String,
#[serde(default)]
pub payload: Value,
#[serde(default)]
pub created_at: String,
#[serde(default = "default_attempt")]
pub attempt: u32,
#[serde(default)]
pub correlation_id: String,
}
impl PluginIngressEvent {
/// Return the text payload carried by a host-owned WebSocket ingress event.
pub fn websocket_text(&self) -> Option<&str> {
self.payload.get("text").and_then(Value::as_str)
}
/// Build a `websocket_send` output command that replies through the
/// host-owned Service WebSocket driver. The host still validates the target
/// URL and matching grants before sending.
pub fn websocket_send(
&self,
command_id: impl Into<String>,
url: impl Into<String>,
text: impl Into<String>,
) -> Result<ServiceOutputCommand> {
ServiceOutputCommand::new(
self,
command_id,
ServiceOutputCommandKind::WebSocketSend,
serde_json::json!({ "url": url.into(), "text": text.into() }),
)
}
}
fn default_attempt() -> u32 {
1
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -519,12 +588,118 @@ impl PluginStatus {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ServiceOutputCommandKind {
DiagnosticStatusUpdate,
HostRequestDispatch,
#[serde(rename = "websocket_send")]
WebSocketSend,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ServiceOutputCommand {
pub correlation_id: String,
pub source_event_id: String,
pub command_id: String,
pub kind: ServiceOutputCommandKind,
pub payload: Value,
pub requested_at: String,
}
impl ServiceOutputCommand {
pub fn new(
event: &PluginIngressEvent,
command_id: impl Into<String>,
kind: ServiceOutputCommandKind,
payload: impl Serialize,
) -> Result<Self> {
let command_id = sanitize_command_id(command_id.into());
if command_id.is_empty() {
return Err(ToolError::invalid_output(
"service output command_id must not be empty",
));
}
let source_event_id = event.correlation_id.clone();
if source_event_id.is_empty() {
return Err(ToolError::invalid_output(
"service output command requires ingress event correlation_id",
));
}
let requested_at = if event.created_at.is_empty() {
"1970-01-01T00:00:00Z".to_string()
} else {
event.created_at.clone()
};
Ok(Self {
correlation_id: sanitize_command_id(format!("{source_event_id}:{command_id}")),
source_event_id,
command_id,
kind,
payload: serde_json::to_value(payload).map_err(ToolError::serialization)?,
requested_at,
})
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ServiceOutput {
#[serde(default)]
pub accepted: bool,
#[serde(default, skip_serializing_if = "Value::is_null")]
pub data: Value,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub output_commands: Vec<ServiceOutputCommand>,
}
impl ServiceOutput {
pub fn accepted(data: impl Serialize) -> Result<Self> {
Ok(Self {
accepted: true,
data: serde_json::to_value(data).map_err(ToolError::serialization)?,
output_commands: Vec::new(),
})
}
pub fn empty() -> Self {
Self {
accepted: true,
data: Value::Null,
output_commands: Vec::new(),
}
}
pub fn with_command(mut self, command: ServiceOutputCommand) -> Self {
self.output_commands.push(command);
self
}
pub fn websocket_send(
event: &PluginIngressEvent,
command_id: impl Into<String>,
url: impl Into<String>,
text: impl Into<String>,
) -> Result<Self> {
Ok(Self::empty().with_command(event.websocket_send(command_id, url, text)?))
}
}
fn sanitize_command_id(value: String) -> String {
bounded_text(
value
.chars()
.map(|ch| if ch.is_control() { '-' } else { ch })
.collect(),
128,
)
}
/// Rust-facing instance Plugin contract. Hosts call `start` once, then route
/// Tool/Ingress surfaces through the same mutable instance.
pub trait Plugin: Sized + 'static {
fn start(config: Value) -> Result<Self>;
fn handle_tool(&mut self, name: &str, input: Value) -> Result<ToolOutput>;
fn handle_ingress(&mut self, name: &str, event: PluginIngressEvent) -> Result<Value>;
fn handle_ingress(&mut self, name: &str, event: PluginIngressEvent) -> Result<ServiceOutput>;
fn status(&self) -> Result<PluginStatus> {
Ok(PluginStatus::ready(Value::Null))
}
+84 -2
View File
@@ -12,6 +12,14 @@ const TEMPLATE_PLUGIN: &str =
include_str!("../../../resources/plugin/templates/rust-component-tool/plugin.toml");
const TEMPLATE_README: &str =
include_str!("../../../resources/plugin/templates/rust-component-tool/README.md");
const SERVICE_TEMPLATE_CARGO: &str =
include_str!("../../../resources/plugin/templates/rust-component-instance/Cargo.toml");
const SERVICE_TEMPLATE_LIB: &str =
include_str!("../../../resources/plugin/templates/rust-component-instance/src/lib.rs");
const SERVICE_TEMPLATE_PLUGIN: &str =
include_str!("../../../resources/plugin/templates/rust-component-instance/plugin.toml");
const SERVICE_TEMPLATE_README: &str =
include_str!("../../../resources/plugin/templates/rust-component-instance/README.md");
const SAMPLE_LIB: &str = include_str!("../../../docs/examples/plugin-component-tool/lib.rs");
const PDK_CARGO: &str = include_str!("../Cargo.toml");
@@ -25,7 +33,7 @@ fn rust_component_tool_template_has_expected_files() {
cargo["dependencies"]["yoi-plugin-pdk"]["path"].as_str(),
Some("../../../../crates/plugin-pdk")
);
assert!(TEMPLATE_CARGO.contains("rev = \"<pinned-yoi-revision>\""));
assert!(TEMPLATE_CARGO.contains("rev = \"<pinned-yoi-commit-sha>\""));
let plugin: Value = toml::from_str(TEMPLATE_PLUGIN).expect("template plugin.toml parses");
assert_eq!(plugin["schema_version"].as_integer(), Some(1));
@@ -45,6 +53,71 @@ fn rust_component_tool_template_has_expected_files() {
assert!(TEMPLATE_README.contains("Component Model Tool Plugin"));
}
#[test]
fn rust_component_service_template_has_event_output_pattern() {
let cargo: Value = toml::from_str(SERVICE_TEMPLATE_CARGO).expect("service Cargo.toml parses");
assert_eq!(cargo["package"]["edition"].as_str(), Some("2024"));
assert_eq!(cargo["lib"]["crate-type"][0].as_str(), Some("cdylib"));
assert_eq!(
cargo["dependencies"]["yoi-plugin-pdk"]["path"].as_str(),
Some("../../../../crates/plugin-pdk")
);
assert!(SERVICE_TEMPLATE_CARGO.contains("rev = \"<pinned-yoi-commit-sha>\""));
let plugin: Value =
toml::from_str(SERVICE_TEMPLATE_PLUGIN).expect("service plugin.toml parses");
assert_eq!(plugin["schema_version"].as_integer(), Some(1));
assert_eq!(plugin["runtime"]["kind"].as_str(), Some("wasm-component"));
assert_eq!(
plugin["runtime"]["world"].as_str(),
Some("yoi:plugin/instance@1.0.0")
);
assert!(
plugin["permissions"]
.as_array()
.expect("permissions array")
.iter()
.any(|permission| permission["kind"].as_str() == Some("host_api")
&& permission["api"].as_str() == Some("websocket"))
);
assert_eq!(
plugin["services"].as_array().expect("services array").len(),
1
);
let ingress = &plugin["ingresses"].as_array().expect("ingresses array")[0];
assert!(
ingress["event_kinds"]
.as_array()
.expect("event kinds")
.iter()
.any(|kind| kind.as_str() == Some("websocket_text"))
);
assert!(
ingress["sources"]
.as_array()
.expect("sources")
.iter()
.any(|source| source.as_str() == Some("websocket:wss://example.com/socket"))
);
let websocket = &plugin["websocket"].as_array().expect("websocket targets")[0];
assert_eq!(websocket["scheme"].as_str(), Some("wss"));
assert_eq!(websocket["host"].as_str(), Some("example.com"));
assert!(
websocket["path_prefixes"]
.as_array()
.expect("websocket path prefixes")
.iter()
.any(|prefix| prefix.as_str() == Some("/socket"))
);
assert!(SERVICE_TEMPLATE_LIB.contains("world: \"instance\""));
assert!(SERVICE_TEMPLATE_LIB.contains("PluginIngressEvent"));
assert!(SERVICE_TEMPLATE_LIB.contains("ServiceOutput::websocket_send"));
assert!(!SERVICE_TEMPLATE_LIB.contains("recv(timeout"));
assert!(SERVICE_TEMPLATE_README.contains("output command"));
assert!(!SERVICE_TEMPLATE_PLUGIN.contains("kind = \"wasm\""));
}
#[test]
fn documented_sample_uses_pdk_component_path() {
assert!(SAMPLE_LIB.contains("use yoi_plugin_pdk::wit_bindgen"));
@@ -57,8 +130,17 @@ fn documented_sample_uses_pdk_component_path() {
#[test]
fn embedded_template_cargo_checks_for_wasm_target() {
cargo_check_template("rust-component-tool");
}
#[test]
fn embedded_service_template_cargo_checks_for_wasm_target() {
cargo_check_template("rust-component-instance");
}
fn cargo_check_template(template: &str) {
let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let template_dir = crate_dir.join("../../resources/plugin/templates/rust-component-tool");
let template_dir = crate_dir.join(format!("../../resources/plugin/templates/{template}"));
let manifest_path = template_dir.join("Cargo.toml");
let lock_path = template_dir.join("Cargo.lock");
let _ = fs::remove_file(&lock_path);
+36 -1
View File
@@ -4,12 +4,17 @@ use std::fs::{DirBuilder, File, OpenOptions};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt};
use std::path::{Path, PathBuf};
use std::thread;
use std::time::{Duration, Instant};
use fs4::fs_std::FileExt;
use manifest::{ScopeRule, paths};
use serde::{Deserialize, Serialize};
use session_store::SegmentId;
const LOCK_WAIT_TIMEOUT: Duration = Duration::from_secs(10);
const LOCK_WAIT_POLL_INTERVAL: Duration = Duration::from_millis(25);
/// On-disk representation of the allocation table.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LockFile {
@@ -119,7 +124,37 @@ impl LockFileGuard {
.truncate(false)
.mode(0o600)
.open(path)?;
FileExt::lock_exclusive(&file)?;
let started = Instant::now();
loop {
match FileExt::try_lock_exclusive(&file) {
Ok(true) => break,
Ok(false) => {
if started.elapsed() >= LOCK_WAIT_TIMEOUT {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
format!(
"timed out waiting for pod registry lock `{}`",
path.display()
),
));
}
thread::sleep(LOCK_WAIT_POLL_INTERVAL);
}
Err(error) if error.kind() == io::ErrorKind::WouldBlock => {
if started.elapsed() >= LOCK_WAIT_TIMEOUT {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
format!(
"timed out waiting for pod registry lock `{}`",
path.display()
),
));
}
thread::sleep(LOCK_WAIT_POLL_INTERVAL);
}
Err(error) => return Err(error),
}
}
let mut this = Self {
file,
data: LockFile::default(),
-1
View File
@@ -37,7 +37,6 @@ workflow-crate = { package = "workflow", path = "../workflow" }
uuid = { workspace = true, features = ["v7"] }
session-metrics = { workspace = true }
arc-swap = "1.9.1"
wasmi = { version = "0.51.1", default-features = false, features = ["std", "extra-checks"] }
wasmtime = { version = "45.0.2", default-features = false, features = ["std", "runtime", "cranelift", "component-model"] }
tungstenite = { version = "0.28.0", default-features = false, features = ["handshake", "native-tls", "url"] }
tokio-tungstenite = { version = "0.28.0", default-features = false, features = ["native-tls", "connect"] }
+30 -6
View File
@@ -900,12 +900,36 @@ async fn controller_loop<C, St>(
pending = Some(PendingRun::Resume);
}
Method::Cancel => {
let _ = event_tx.send(Event::Error {
code: ErrorCode::NotRunning,
message: "Pod is not running".into(),
});
}
Method::Cancel => match shared_state.get_status() {
PodStatus::Paused => match pod.cancel_paused_turn() {
Ok(()) => {
set_controller_status(
&shared_state,
&runtime_dir,
&event_tx,
PodStatus::Idle,
)
.await;
}
Err(error) => {
let _ = event_tx.send(Event::Error {
code: worker_error_code(&error),
message: error.to_string(),
});
}
},
PodStatus::Idle => {
let _ = event_tx.send(Event::Error {
code: ErrorCode::NotRunning,
message: "Pod is not running".into(),
});
}
PodStatus::Running => {
// Running turns receive Cancel through drive_turn; this is
// only reachable across a defensive race window.
let _ = cancel_tx.try_send(());
}
},
Method::Pause => {
// Already paused → idempotent no-op. Otherwise the
File diff suppressed because it is too large Load Diff
+130 -8
View File
@@ -235,8 +235,24 @@ impl InFlightInner {
self.remove_first_text_matching(&text);
}
}
LoggedItem::Reasoning { text, .. } => {
self.remove_first_thinking_matching(text);
LoggedItem::Reasoning {
text,
summary,
encrypted_content,
..
} => {
let mut removed = false;
if !text.is_empty() {
removed |= self.remove_first_thinking_matching(text);
}
for summary_text in summary {
if !summary_text.is_empty() {
removed |= self.remove_first_thinking_matching(summary_text);
}
}
if !removed && encrypted_content.is_some() {
self.remove_first_empty_finished_thinking();
}
}
LoggedItem::ToolCall { call_id, .. } => {
self.remove_tool_call(call_id);
@@ -255,21 +271,39 @@ impl InFlightInner {
}
}
fn remove_first_text_matching(&mut self, committed: &str) {
fn remove_first_text_matching(&mut self, committed: &str) -> bool {
if let Some(index) = self.blocks.iter().position(|block| match block {
TrackedBlock::Text { text, .. } => text == committed,
_ => false,
}) {
self.blocks.remove(index);
true
} else {
false
}
}
fn remove_first_thinking_matching(&mut self, committed: &str) {
fn remove_first_thinking_matching(&mut self, committed: &str) -> bool {
if let Some(index) = self.blocks.iter().position(|block| match block {
TrackedBlock::Thinking { text, .. } => text == committed,
_ => false,
}) {
self.blocks.remove(index);
true
} else {
false
}
}
fn remove_first_empty_finished_thinking(&mut self) -> bool {
if let Some(index) = self.blocks.iter().position(|block| match block {
TrackedBlock::Thinking { text, finished, .. } => text.is_empty() && *finished,
_ => false,
}) {
self.blocks.remove(index);
true
} else {
false
}
}
@@ -304,10 +338,16 @@ impl TrackedBlock {
})
}
}
TrackedBlock::Thinking { text, finished, .. } => Some(InFlightBlock::Thinking {
text: text.clone(),
finished: *finished,
}),
TrackedBlock::Thinking { text, finished, .. } => {
if text.is_empty() && *finished {
None
} else {
Some(InFlightBlock::Thinking {
text: text.clone(),
finished: *finished,
})
}
}
TrackedBlock::ToolCall {
id,
name,
@@ -474,4 +514,86 @@ mod tests {
let guard = in_flight.snapshot_guard();
assert!(snapshot_from_guard(&guard).is_empty());
}
#[test]
fn committed_reasoning_summary_clears_matching_in_flight_thinking_blocks() {
let (event_tx, _) = broadcast::channel(16);
let in_flight = InFlightEvents::new(event_tx);
let first = in_flight.thinking_start();
in_flight.thinking_delta(first, "summary A".into());
in_flight.thinking_done(first, "".into());
let second = in_flight.thinking_start();
in_flight.thinking_delta(second, "summary B".into());
in_flight.thinking_done(second, "".into());
in_flight.clear_for_committed_item_then(
&LoggedItem::Reasoning {
text: String::new(),
summary: vec!["summary A".into(), "summary B".into()],
encrypted_content: Some("opaque".into()),
signature: None,
},
|| (),
);
let guard = in_flight.snapshot_guard();
assert!(snapshot_from_guard(&guard).is_empty());
}
#[test]
fn committed_encrypted_only_reasoning_clears_empty_finished_thinking_block() {
let (event_tx, _) = broadcast::channel(16);
let in_flight = InFlightEvents::new(event_tx);
let first = in_flight.thinking_start();
in_flight.thinking_done(first, "".into());
let second = in_flight.thinking_start();
in_flight.thinking_delta(second, "still running".into());
in_flight.clear_for_committed_item_then(
&LoggedItem::Reasoning {
text: String::new(),
summary: Vec::new(),
encrypted_content: Some("opaque".into()),
signature: None,
},
|| (),
);
let guard = in_flight.snapshot_guard();
assert_eq!(
snapshot_from_guard(&guard).blocks,
vec![InFlightBlock::Thinking {
text: "still running".into(),
finished: false,
}]
);
}
#[test]
fn snapshot_omits_empty_finished_thinking_blocks() {
let (event_tx, _) = broadcast::channel(16);
let in_flight = InFlightEvents::new(event_tx);
let empty_finished = in_flight.thinking_start();
in_flight.thinking_done(empty_finished, "".into());
let empty_running = in_flight.thinking_start();
let visible_finished = in_flight.thinking_start();
in_flight.thinking_delta(visible_finished, "visible".into());
in_flight.thinking_done(visible_finished, "".into());
let guard = in_flight.snapshot_guard();
assert_eq!(
snapshot_from_guard(&guard).blocks,
vec![
InFlightBlock::Thinking {
text: String::new(),
finished: false,
},
InFlightBlock::Thinking {
text: "visible".into(),
finished: true,
}
]
);
assert_ne!(empty_running, empty_finished);
}
}
+20
View File
@@ -1863,6 +1863,26 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
Ok(())
}
/// Abandon a paused/interrupted turn without resuming it.
///
/// This uses the same explicit interrupt preparation as the next fresh
/// `run` would have used, then clears the worker's interrupted marker so
/// future input is treated as a normal new turn instead of a resume.
/// The explicit `PausedTurnAbandoned` marker preserves durable lifecycle
/// semantics without claiming another `run` / `resume` completed.
pub fn cancel_paused_turn(&mut self) -> Result<(), PodError> {
if !self.worker().last_run_interrupted() {
return Ok(());
}
self.apply_interrupt_prep()?;
self.worker_mut().set_last_run_interrupted(false);
self.commit_entry(LogEntry::PausedTurnAbandoned {
ts: segment_log::now_millis(),
})?;
Ok(())
}
/// Validate explicit workflow invocations without reading dependency
/// bodies. Called from `Pod::run` entry so an invalid slug aborts
/// the turn before any session-log commit or interrupt-prep side
+1 -1
View File
@@ -739,7 +739,7 @@ compact_system = "PREFIX\n{% include \"$yoi/internal/compact_system\" %}"
assert!(rendered.contains("spawned Pod notifications are background signals"));
assert!(rendered.contains("does not need to keep a turn open"));
assert!(rendered.contains("Do not use `sleep` or polling loops"));
assert!(rendered.contains("worktree status, diff, and test results"));
assert!(rendered.contains("worktree state, diff, and test results"));
assert!(rendered.contains("not scheduler or auto-maintain authorization"));
assert!(rendered.contains("bypass user/workflow authorization"));
}

Some files were not shown because too many files have changed in this diff Show More