chore: capture current pod and panel followups

This commit is contained in:
Keisuke Hirata 2026-06-14 03:41:40 +09:00
parent 6cae63fc53
commit 1abce888ae
No known key found for this signature in database
9 changed files with 328 additions and 10 deletions

View File

@ -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"}

View File

@ -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']

View File

@ -25,4 +25,105 @@ LocalTicketBackend によって作成されました。
Ticket を `workspace-panel` が queued にしました。
---
<!-- event: decision author: orchestrator at: 2026-06-13T17:51:56Z -->
## 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。
---
<!-- event: state_changed author: orchestrator at: 2026-06-13T17:52:12Z from: queued to: inprogress reason: orchestrator_acceptance_unblocked_e2e_critical_path field: state -->
## 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 を記録する。
---
<!-- event: implementation_report author: orchestrator at: 2026-06-13T17:54:09Z -->
## 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.
---

View File

@ -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"
}
]
}

View File

@ -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 <id>`.
- 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 <blocker>` 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`.

View File

@ -0,0 +1,7 @@
<!-- event: create author: "yoi ticket" at: 2026-06-13T18:10:57Z -->
## 作成
LocalTicketBackend によって作成されました。
---

View File

@ -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<PodCommon, PodError> {
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<PodCommon, PodError> {
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#"

View File

@ -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<ScopeRuleInput>,
}