From 1abce888ae703e27fc24da1fd1319b47ac34d10e Mon Sep 17 00:00:00 2001 From: Hare Date: Sun, 14 Jun 2026 03:41:40 +0900 Subject: [PATCH] chore: capture current pod and panel followups --- .../artifacts/orchestration-plan.jsonl | 2 + .yoi/tickets/00001KV10SN02/item.md | 4 +- .yoi/tickets/00001KV10SN02/thread.md | 101 +++++++++++++++++ .yoi/tickets/00001KV12W2RT/artifacts/.gitkeep | 0 .../00001KV12W2RT/artifacts/relations.json | 21 ++++ .yoi/tickets/00001KV12W2RT/item.md | 104 ++++++++++++++++++ .yoi/tickets/00001KV12W2RT/thread.md | 7 ++ crates/pod/src/pod.rs | 97 ++++++++++++++-- crates/pod/src/spawn/tool.rs | 2 +- 9 files changed, 328 insertions(+), 10 deletions(-) create mode 100644 .yoi/tickets/00001KV10SN02/artifacts/orchestration-plan.jsonl create mode 100644 .yoi/tickets/00001KV12W2RT/artifacts/.gitkeep create mode 100644 .yoi/tickets/00001KV12W2RT/artifacts/relations.json create mode 100644 .yoi/tickets/00001KV12W2RT/item.md create mode 100644 .yoi/tickets/00001KV12W2RT/thread.md diff --git a/.yoi/tickets/00001KV10SN02/artifacts/orchestration-plan.jsonl b/.yoi/tickets/00001KV10SN02/artifacts/orchestration-plan.jsonl new file mode 100644 index 00000000..bd41546a --- /dev/null +++ b/.yoi/tickets/00001KV10SN02/artifacts/orchestration-plan.jsonl @@ -0,0 +1,2 @@ +{"id":"orch-plan-20260613-175205-1","ticket_id":"00001KV10SN02","kind":"accepted_plan","accepted_plan":{"summary":"Implement remaining critical-path E2E coverage after the Panel harness: keep existing isolated Panel tests, add wheel/drag-capture regression coverage and a minimal single-Pod rewind PTY regression, preserve binary/env/tmp isolation, and validate with focused E2E plus packaging/build checks.","branch":"ticket-00001KV10SN02-e2e-critical-path","worktree":"/home/hare/Projects/yoi/.worktree/e2e-critical-path","role_plan":"Orchestrator creates a dedicated child worktree and delegates to a Coder Pod with write scope for `tests/e2e`, relevant TUI/runtime crates, root Cargo files if needed, and this Ticket record. Reviewer focus: no real LLM/provider calls, fixture isolation, opt-in production boundary, PTY path coverage, and no broad runtime/API drift."},"author":"orchestrator","at":"2026-06-13T17:52:05Z"} +{"id":"orch-plan-20260613-175419-2","ticket_id":"00001KV10SN02","kind":"waiting_capacity_note","note":"Implementation accepted and worktree created, but Coder Pod spawn is blocked by current Orchestrator delegation scope: child launch validation requires readable workspace root `/home/hare/Projects/yoi`, which this Pod cannot re-delegate. Wait for Orchestrator restore/restart with appropriate delegation or explicit direct-implementation authorization.","author":"orchestrator","at":"2026-06-13T17:54:19Z"} diff --git a/.yoi/tickets/00001KV10SN02/item.md b/.yoi/tickets/00001KV10SN02/item.md index 3907f569..3710e158 100644 --- a/.yoi/tickets/00001KV10SN02/item.md +++ b/.yoi/tickets/00001KV10SN02/item.md @@ -1,8 +1,8 @@ --- title: 'E2E: close remaining critical-path gaps after panel harness' -state: 'queued' +state: 'inprogress' created_at: '2026-06-13T17:34:41Z' -updated_at: '2026-06-13T17:51:04Z' +updated_at: '2026-06-13T17:54:19Z' assignee: null readiness: 'implementation_ready' risk_flags: ['e2e', 'tui', 'pty', 'quit-latency', 'mouse-input', 'rewind'] diff --git a/.yoi/tickets/00001KV10SN02/thread.md b/.yoi/tickets/00001KV10SN02/thread.md index a5bc36c3..3edff71d 100644 --- a/.yoi/tickets/00001KV10SN02/thread.md +++ b/.yoi/tickets/00001KV10SN02/thread.md @@ -25,4 +25,105 @@ LocalTicketBackend によって作成されました。 Ticket を `workspace-panel` が queued にしました。 +--- + + + +## Decision + +Routing decision: implementation_ready + +Reason: +- Ticket は `queued` で、既存 E2E harness first slice / latest-binary build / env isolation / tmp runtime isolation を前提にした残差 coverage が具体化されている。 +- `TicketRelationQuery` の relation は `related` のみで、blocking dependency はない。`TicketOrchestrationPlanQuery` に既存 blocker/conflict 記録はない。 +- `00001KV0YK5S0` の tmp runtime/data isolation は Orchestrator branch に merge/validated 済みで、この Ticket の前提は満たされている。 +- Risk flags は `e2e` / `tui` / `pty` / `quit-latency` / `mouse-input` / `rewind` だが、binding scope は opt-in E2E の missing critical-path coverage に限定され、real provider/network call 禁止、host credential isolation、fixture runtime isolation などの invariants が明記されている。 +- Orchestrator worktree は clean。visible implementation Pods はない。ほか queued Ticket はあるが、Panel/TUI 変更面や broad authority model 変更と同時に進めると review/validation capacity と merge conflict risk が上がるため、この Ticket を先に受理し、他 queued は現時点で開始しない。 + +Evidence checked: +- Ticket body / thread / artifacts。 +- relation records: related links only。 +- orchestration plan records: なし。 +- related completed work: `00001KSKBP9YG`, `00001KV0TJVN5`, `00001KV0YK5S0`, `00001KV04NJ8D`, `00001KV0723PC`, `00001KV072V89`。 +- workspace state: Orchestrator worktree clean、live spawned implementation Pods なし。 +- current queue: `00001KV0X254D`, `00001KV09X0XC`, `00001KV0SP0TY`, `00001KV10SN02`。 + +IntentPacket: + +Intent: +- Existing opt-in `yoi-e2e` Panel PTY harness を土台に、larger feature/MCP work 前の remaining critical-path gaps を最小限埋める。 +- 既存 Panel quit latency / mouse click selection / tmp isolation coverage は維持し、wheel/drag-capture regression と rewind UI real-process regression を追加する。 + +Binding decisions / invariants: +- E2E は opt-in のまま。`cargo test --workspace` default に混ぜない。 +- No real provider credentials / no network-backed LLM calls。 +- Tested processes は clean fixture tmp workspace/data/runtime/env isolation を使い、host runtime/credential を見ない。 +- Existing binary provider and `YOI_E2E_BIN` override, env isolation, mouse capture guard, quit pending barrier, cleanup artifacts を壊さない。 +- `cargo run` を measured process-under-test にしない。 +- Full drag-motion mouse capture (`?1002h` / `?1003h`) を reintroduce しないことを可能な範囲で PTY output から確認する。 +- Rewind E2E は success が `Esc` / restart / restore 後でないと見えない状態を fail させる。 + +Requirements / acceptance criteria: +- Existing Panel smoke/click/quit tests still pass on fixture-isolated harness。 +- Mouse E2E covers wheel input for viewport/list behavior and fails if wheel is ignored where observable。 +- Mouse E2E fails if click-to-select dispatches action or if full drag-motion capture is observed where forbidden。 +- Rewind UI E2E drives real single-Pod TUI or equivalent PTY surface with fixture/canned state: `Ctrl+R` opens picker; target `Enter` updates visible live view/composer/state without requiring `Esc`, restart, or restore; repeated `Enter` while pending does not send duplicate destructive rewind requests。 +- Failure artifacts include PTY output/events/status/timing enough for diagnosis。 +- Validation includes focused E2E commands, affected checks/tests, `cargo build -p yoi`, and `nix build .#yoi`。 + +Implementation latitude: +- Coder may extend the existing PanelHarness or add a SinglePodHarness if cleaner。 +- Canned session/test-only runtime controls may be used, but must remain opt-in/read-only or test-only and must not enable real provider calls。 +- Wheel coverage may use structured events and/or screen artifact assertions, but must exercise PTY input path。 +- Rewind scenario can be minimal and fixture-driven; do not broaden into a full provider/protocol E2E matrix。 + +Escalate if: +- Rewind E2E requires broad public protocol/API changes or real provider calls。 +- Single-Pod PTY fixture cannot be built without exposing test-only production surfaces beyond the existing `e2e-test` boundary。 +- `nix build .#yoi` requires package/source-filter changes outside delegated scope。 + +Validation: +- `cargo test -p yoi-e2e --features e2e ...` focused targets。 +- `cargo fmt --check`。 +- `git diff --check`。 +- Relevant package checks/tests, including `cargo build -p yoi`。 +- `nix build .#yoi` because package/source-filter correctness may be affected by E2E/test files or Cargo changes。 + +--- + + + +## State changed + +Ticket evidence、related records、orchestration plan、existing E2E branch state、workspace state、visible Pods を確認した。Blocking relation はなく、tmp runtime isolation 前提は merge 済み。Implementation side effect の前に inprogress acceptance を記録する。 + +--- + + + +## Implementation report + +Implementation handoff blocked after acceptance. + +- Routing / IntentPacket / accepted plan は記録済み。 +- State は `queued -> inprogress` acceptance 済み。 +- Implementation worktree は作成済み: `/home/hare/Projects/yoi/.worktree/e2e-critical-path` +- Branch: `ticket-00001KV10SN02-e2e-critical-path` + +Blocker: +- Coder Pod `coder-00001KV10SN02-e2e-critical` の spawn に失敗した。 +- Child launch validation が runtime workspace root `/home/hare/Projects/yoi` の readable grant を要求したが、現在の Orchestrator delegation scope では `/home/hare/Projects/yoi` non-recursive read を子に再委譲できない。 +- Error: `failed to create spawned pod: cwd is not readable under the configured scope: /home/hare/Projects/yoi` + +Impact: +- まだ Coder は起動しておらず、実装変更は行われていない。 +- Worktree creation だけが side effect として存在する。 + +Next recovery options: +1. Orchestrator Pod を、child launch validation に必要な root workspace non-recursive read delegation を持つ状態で再起動/復旧してから Coder を起動する。 +2. 明示許可があれば、この Orchestrator が sibling Coder なしで直接実装する。ただし本 Ticket は E2E/TUI/rewind の critical-path coverage で review 重要度が高いため、可能なら sibling Coder/Reviewer flow を復旧する方が望ましい。 +3. 継続できない場合は、作成済み worktree/branch を cleanup し、scope blocker を記録したまま人間判断待ちにする。 + +No further implementation side effects were performed after the failed spawn. + --- diff --git a/.yoi/tickets/00001KV12W2RT/artifacts/.gitkeep b/.yoi/tickets/00001KV12W2RT/artifacts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/.yoi/tickets/00001KV12W2RT/artifacts/relations.json b/.yoi/tickets/00001KV12W2RT/artifacts/relations.json new file mode 100644 index 00000000..ed0d1fa2 --- /dev/null +++ b/.yoi/tickets/00001KV12W2RT/artifacts/relations.json @@ -0,0 +1,21 @@ +{ + "version": 1, + "relations": [ + { + "ticket_id": "00001KV12W2RT", + "kind": "related", + "target": "00001KTR81P9X", + "note": "ready+waiting dependency display example", + "author": "yoi ticket", + "at": "2026-06-13T18:11:44Z" + }, + { + "ticket_id": "00001KV12W2RT", + "kind": "related", + "target": "00001KV10SN02", + "note": "Panel row layout changes may affect mouse/wheel E2E expectations", + "author": "yoi ticket", + "at": "2026-06-13T18:11:44Z" + } + ] +} diff --git a/.yoi/tickets/00001KV12W2RT/item.md b/.yoi/tickets/00001KV12W2RT/item.md new file mode 100644 index 00000000..c8978af3 --- /dev/null +++ b/.yoi/tickets/00001KV12W2RT/item.md @@ -0,0 +1,104 @@ +--- +title: 'Panel Ticket rows を2行表示にして gate 情報を分離する' +state: 'ready' +created_at: '2026-06-13T18:10:57Z' +updated_at: '2026-06-13T18:11:44Z' +assignee: null +readiness: 'implementation_ready' +risk_flags: ['tui', 'workspace-panel', 'ticket-relations', 'mouse-input', 'layout'] +--- + +## Background + +Workspace Panel の Ticket row は現在 1 行に `state / title / status / action` を圧縮している。これにより、canonical state と relation / queue blocker から導出される execution gate が混ざりやすい。 + +例: `state: ready` かつ unresolved `depends_on` がある Ticket は、正本としては ready のままでよい。しかし Panel では queue 可能ではなく、`Gate: waiting` として見せる必要がある。現在の実装では relation blocker が `PanelRowKind::Blocked` / red / `Edit` に寄り、正常な依存待ちが error や人間入力要求のように見える。 + +Panel の Ticket 表示を default 2 行にして、1 行目に正本 state/title、2 行目に Ticket id と derived gate/action/reason を表示する。選択状態は三角 `▶` ではなく、複数行をまとめる左罫線 `|` で示す。 + +## Target layout + +Default Ticket row is two visual lines: + +```text + ready Extend pod::feature API for external protocol-backed capability providers + 00001KTR81P9X | Gate: waiting · depends_on 00001KV0SP0TY +``` + +Selected Ticket row uses a left vertical marker instead of a triangle so the two lines read as one selected item: + +```text +| ready Extend pod::feature API for external protocol-backed capability providers +| 00001KTR81P9X | Gate: waiting · depends_on 00001KV0SP0TY +``` + +Queueable ready Ticket example: + +```text + ready Remove feature-layer HostAuthority model + 00001KV0SP0TY | Gate: queueable · Action: Queue +``` + +Planning Ticket example: + +```text + planning Some unclear work item + 00001XXXXXXX | Gate: needs planning · Action: Clarify +``` + +## Requirements + +- Ticket rows in Workspace Panel render as two visual lines by default. + - Line 1: canonical workflow state + title. + - Line 2: ticket id + derived gate/action/reason summary. +- Preserve the Ticket schema model. + - Do not add a persisted `waiting` state. + - Continue treating `state` as canonical lifecycle state. + - Derive gate/waiting/queueable from relations and current Panel context. +- Relation blockers should be displayed as execution gate information, not as canonical state replacement. + - `state: ready` with unresolved `depends_on` shows `ready` on line 1. + - Line 2 shows `Gate: waiting · depends_on `. + - Queue action is disabled/not offered while blockers are unresolved. + - Normal dependency wait should not use red/error styling or `Edit` as the primary action. +- Keep truly problematic states visually distinct. + - Invalid/corrupt/unusable Ticket data or user-decision-required blockers may still use stronger warning styling. + - Normal relation ordering waits should use a waiting/amber/dim style rather than red. +- Replace selected-row triangle marker with a multi-line grouping marker. + - Selected Ticket row uses `|` on each visual line. + - Non-selected Ticket row uses leading spaces aligned with the selected marker. + - Pod rows may keep existing one-line marker behavior unless changing them is necessary for layout consistency. +- Update list selection and scrolling to account for multi-line Ticket rows. + - Selection remains one logical Ticket per two visual lines. + - Keyboard up/down moves by logical row, not visual line. + - Mouse click on either visual line selects the same Ticket and does not dispatch the action. + - Mouse wheel behavior remains intact. +- Keep row layout robust for narrow terminals. + - Truncate title and gate summary with ellipsis. + - Avoid brittle fixed-width column alignment beyond small state/id labels. +- Update target/action status line wording as needed. + - Selected ready/waiting Ticket should communicate `queue disabled` or `waiting for ` instead of simply `ready · Enter Edit`. + +## Acceptance criteria + +- Workspace Panel unit tests cover two-line Ticket row rendering for at least queueable ready, ready+waiting, planning, queued/inprogress, and done/closed cases where practical. +- A ready Ticket with unresolved `depends_on` renders line 1 with `ready` and line 2 with `Gate: waiting` plus blocker identity. +- The same ready+waiting Ticket does not have `NextUserAction::Queue` and does not use `NextUserAction::Edit` merely because of a normal dependency wait. +- The ready+waiting Ticket is not rendered with the red/UserReply priority used for human-error/user-reply states. +- Selected Ticket rows use `|` on all visual lines belonging to that Ticket; no `▶` triangle is used for those multi-line rows. +- Mouse click on either visual line selects the same logical Ticket without dispatching its action. +- Existing mouse wheel behavior continues to scroll/select as before. +- Existing Panel E2E expectations are updated or extended if they depend on one-line rows or triangle markers. +- Validation: focused `tui` tests, affected E2E if practical, `cargo build -p yoi`, and `nix build .#yoi`. + +## Non-goals + +- Adding a persisted `waiting` Ticket state. +- Redesigning the whole Panel into full cards or a detail pane. +- Changing Ticket lifecycle transition rules. +- Reworking Pod row layout unless required for shared rendering primitives. + +## Related work + +- Ready Ticket blocked by dependency example: `00001KTR81P9X` depends on `00001KV0SP0TY`. +- Ticket relation blockers are already surfaced by `TicketShow` and `WorkspacePanelViewModel`. +- Mouse selection / wheel behavior follow-up E2E: `00001KV10SN02`. diff --git a/.yoi/tickets/00001KV12W2RT/thread.md b/.yoi/tickets/00001KV12W2RT/thread.md new file mode 100644 index 00000000..8e99166d --- /dev/null +++ b/.yoi/tickets/00001KV12W2RT/thread.md @@ -0,0 +1,7 @@ + + +## 作成 + +LocalTicketBackend によって作成されました。 + +--- diff --git a/crates/pod/src/pod.rs b/crates/pod/src/pod.rs index f8384e4e..0b029b28 100644 --- a/crates/pod/src/pod.rs +++ b/crates/pod/src/pod.rs @@ -4674,9 +4674,19 @@ pub enum PodError { #[error(transparent)] Scope(ScopeError), + #[error("workspace root is not readable under the configured scope: {}", .workspace_root.display())] + WorkspaceRootOutsideScope { workspace_root: PathBuf }, + #[error("cwd is not readable under the configured scope: {}", .cwd.display())] CwdOutsideScope { cwd: PathBuf }, + #[error("failed to resolve workspace root {}: {source}", .workspace_root.display())] + InvalidWorkspaceRoot { + workspace_root: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("failed to resolve cwd {}: {source}", .cwd.display())] InvalidCwd { cwd: PathBuf, @@ -4858,11 +4868,12 @@ fn prepare_pod_common_with_context( cwd: PathBuf, scope_config: ScopeConfig, ) -> Result { - let workspace_root = - std::fs::canonicalize(&workspace_root).map_err(|source| PodError::InvalidCwd { - cwd: workspace_root.clone(), + let workspace_root = std::fs::canonicalize(&workspace_root).map_err(|source| { + PodError::InvalidWorkspaceRoot { + workspace_root: workspace_root.clone(), source, - })?; + } + })?; let cwd = std::fs::canonicalize(&cwd).map_err(|source| PodError::InvalidCwd { cwd: cwd.clone(), source, @@ -4889,9 +4900,7 @@ fn prepare_pod_common_from_scope( scope: Scope, ) -> Result { if !scope.is_readable(&workspace_root) { - return Err(PodError::CwdOutsideScope { - cwd: workspace_root, - }); + return Err(PodError::WorkspaceRootOutsideScope { workspace_root }); } if !scope.is_readable(&cwd) { return Err(PodError::CwdOutsideScope { cwd }); @@ -5041,6 +5050,80 @@ mod spawned_context_tests { ); } + #[test] + fn prepare_context_reports_workspace_root_when_workspace_root_is_unreadable() { + let tmp = tempfile::tempdir().unwrap(); + let workspace_root = tmp.path().join("workspace-root"); + let cwd = tmp.path().join("child-worktree"); + std::fs::create_dir_all(&workspace_root).unwrap(); + std::fs::create_dir_all(&cwd).unwrap(); + + let manifest = minimal_manifest_for_context_test(&workspace_root, &cwd); + let err = match prepare_pod_common_with_context( + &manifest, + &PromptLoader::builtins_only(), + false, + workspace_root.clone(), + cwd.clone(), + ScopeConfig { + allow: vec![ScopeRule { + target: cwd.clone(), + permission: Permission::Read, + recursive: true, + }], + deny: Vec::new(), + }, + ) { + Ok(_) => panic!("expected workspace-root scope error"), + Err(err) => err, + }; + + match err { + PodError::WorkspaceRootOutsideScope { + workspace_root: got, + } => { + assert_eq!(got, workspace_root.canonicalize().unwrap()); + } + other => panic!("expected workspace-root scope error, got {other:?}"), + } + } + + #[test] + fn prepare_context_reports_cwd_when_only_cwd_is_unreadable() { + let tmp = tempfile::tempdir().unwrap(); + let workspace_root = tmp.path().join("workspace-root"); + let cwd = tmp.path().join("child-worktree"); + std::fs::create_dir_all(&workspace_root).unwrap(); + std::fs::create_dir_all(&cwd).unwrap(); + + let manifest = minimal_manifest_for_context_test(&workspace_root, &cwd); + let err = match prepare_pod_common_with_context( + &manifest, + &PromptLoader::builtins_only(), + false, + workspace_root.clone(), + cwd.clone(), + ScopeConfig { + allow: vec![ScopeRule { + target: workspace_root.clone(), + permission: Permission::Read, + recursive: true, + }], + deny: Vec::new(), + }, + ) { + Ok(_) => panic!("expected cwd scope error"), + Err(err) => err, + }; + + match err { + PodError::CwdOutsideScope { cwd: got } => { + assert_eq!(got, cwd.canonicalize().unwrap()); + } + other => panic!("expected cwd scope error, got {other:?}"), + } + } + fn minimal_manifest_for_context_test(workspace_root: &Path, cwd: &Path) -> PodManifest { let toml_str = format!( r#" diff --git a/crates/pod/src/spawn/tool.rs b/crates/pod/src/spawn/tool.rs index 019dd88f..d4d0464b 100644 --- a/crates/pod/src/spawn/tool.rs +++ b/crates/pod/src/spawn/tool.rs @@ -61,7 +61,7 @@ struct SpawnPodInput { task: String, /// Allow rules delegated to the spawned Pod. Must be a subset of the /// spawner's explicit delegation authority; direct tool scope alone is not - /// sufficient. + /// sufficient. Omit `recursive` for normal workspace/worktree delegation; it defaults to true. scope: Vec, }