{"id":"orch-plan-20260615-140109-1","ticket_id":"00001KV5MRH6D","kind":"accepted_plan","accepted_plan":{"summary":"Accept Panel startup latency E2E measurement/improvement work. Measure first visible render via real yoi PTY E2E, separate background/full-ready waits, improve startup path where safe, and record before/after evidence.","branch":"impl/00001KV5MRH6D-panel-startup-latency","worktree":"/home/hare/Projects/yoi/.worktree/00001KV5MRH6D-panel-startup-latency","role_plan":"Orchestrator creates dedicated implementation worktree and spawns Coder with write scope limited to that worktree. Reviewer will run read-only after implementation report. Can run in parallel with Plugin resolver work because source surfaces are disjoint."},"author":"yoi-orchestrator","at":"2026-06-15T14:01:09Z"}
| Fixture first visible panel render (`panel_full_ready_has_separate_startup_budget`) | 20.342 ms | 20.384 ms | 1500 ms | Warm fixture did not reproduce 7 s latency; after path keeps first draw separate from reload work. |
| Fixture full-ready rows (`panel_full_ready_has_separate_startup_budget`) | 120.576 ms | 120.552 ms | 5000 ms | Separate metric; fixture full-ready remains well below budget after the first-frame deferral. |
| Held-reload ordering (`panel_first_visible_render_arrives_before_background_reload`) | `background_task_started` before `panel_ready` | `panel_ready` before held `background_task_started`; first visible 20.396 ms | 1500 ms | Guarantees initial visible frame is not blocked by reload/observation. |
| Pre-change ad-hoc Panel run from existing fixture artifact | `background_task_started@76 ms`, `panel_ready@80 ms`, full rows `@182 ms` | latest held/full tests above | n/a | Used to identify wait ordering; fixture still did not reproduce live 7 s. |
### Wait points identified
- Synchronous before first draw: CLI/process startup, `run_panel` workspace/cwd setup, `load_app` construction of an empty/loading `WorkspacePanelViewModel`, raw-mode/bracketed-paste/alternate-screen/mouse setup, and the first `terminal.draw`.
- Previously scheduled before first draw: initial Panel reload/observation task (`PendingReload::start(Ensure { ... })`), which can scan Tickets/Pods/orchestrator state and perform socket/status probing before fixture rows are fully ready.
- Background/full-ready after first draw: `load_multi_pod_snapshot`, Ticket list/detail loading, Pod metadata/status checks, orchestrator lifecycle observation, row selection/re-render, and background diagnostics.
### Changes
- Added PTY E2E startup coverage for `yoi panel`:
-`panel_first_visible_render_arrives_before_background_reload` asserts `panel_ready` arrives within 1500 ms and before the held reload task starts.
-`panel_full_ready_has_separate_startup_budget` asserts first visible render within 1500 ms and full fixture rows within 5 s as a separate metric.
- Deferred the initial Panel reload start until after the first loading frame is drawn, preserving later background reload correctness.
- Kept Panel/terminal mouse capture to SGR + normal tracking (`?1006h` + `?1000h`) and avoided drag-capture (`?1002h`/`?1003h`) so existing PTY tests can confirm no drag-capture regression.
### Guaranteed scope / residual gaps
- Guaranteed by E2E fixture: real binary, PTY, first visible frame budget, held-reload ordering, separate full-ready row budget, no provider/network/secret dependency.
- Residual live/manual gap: the reported ~7 s live Panel startup did not reproduce in this fixture. This change prevents initial reload/observation from blocking or contending with the first visible frame, but live-terminal confirmation is still needed if the remaining cause is workspace-specific (for example a large real Ticket/Pod set or slow live socket/status probe).
- Worktree was clean and at expected branch/commit.
Acceptance review:
- E2E uses real `yoi panel` process under PTY fixture, not unit-only coverage.
- First visible render is explicitly measured and asserted via `panel_first_visible_render_arrives_before_background_reload` with a 1500ms budget.
- Full-ready/background work is separately measured via `panel_full_ready_has_separate_startup_budget` with its own budget and ready-row assertion.
- The implementation defers the initial background reload until after the first loading frame is queued/drawn; it does not fake Ticket/Pod/Orchestrator state.
- The tests use a held reload barrier to prove first visible rendering happens before background reload unblocks.
- Background reload correctness is preserved by waiting for rows/actions after the barrier.
- The reported before/after evidence and thread update correctly explain that fixture E2E did not reproduce the live ~7s delay and that live/manual confirmation remains a residual gap.
- Existing no-provider/no-network style E2E isolation is preserved; no provider/secret/network dependency was introduced.
- The mouse capture adjustment avoids drag/full-motion capture modes while preserving SGR normal mouse tracking, matching recent Panel mouse invariants.
- No Ticket workflow, Pod restore/spawn authority, Orchestrator queue, scheduler, or lease semantics were changed.
Validation performed by reviewer:
- Passed: `git diff --check 4772c4d6..9bad2745`
- Passed: `cargo fmt --check`
- Worktree stayed clean.
Validation not run by reviewer:
- E2E/cargo test commands were not rerun because review scope was read-only and they write build artifacts. Coder-reported validation was inspected as evidence.
-`approve` recorded from `yoi-reviewer-00001KV5MRH6D`.
Validation in Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration`:
- Passed: `cargo fmt --check`
- Passed: `cargo test -p yoi-e2e --features e2e --no-run`
- Passed: `cargo test -p yoi-e2e --features e2e`
- yoi-e2e lib: 1 passed
- panel E2E: 5 passed
- rewind E2E: 1 passed
- Passed: `cargo check -p yoi-e2e -p yoi -p tui`
- Passed: `cargo test -p tui single_pod_mouse_capture_avoids_drag_and_all_motion_modes`
- 1 passed
- Passed: `git diff --check`
Evidence outcome:
- First visible render is asserted within 1500ms through real `yoi panel` PTY fixture.
- Full-ready/background row availability is asserted separately within 5s.
- Initial reload/observation is deferred until after first loading frame; state/diagnostics are not faked or skipped.
- Fixture did not reproduce the live ~7s startup delay, so live/manual confirmation remains a documented gap.
Cleanup planned:
- Stop related coder/reviewer Pods.
- Remove only child implementation worktree/branch for this Ticket.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-15T14:31:28Z from: inprogress to: done reason: merged_validated field: state -->
## State changed
Reviewer approved, implementation/evidence branch merged into the orchestration branch, and E2E-focused validation passed in the Orchestrator worktree. Marking Ticket done in the orchestration branch.
{"id":"orch-plan-20260615-140041-1","ticket_id":"00001KV5R5V2S","kind":"accepted_plan","accepted_plan":{"summary":"Accept Plugin package discovery and explicit enablement resolver implementation. Build typed discovery/manifest/resolver data without runtime execution or contribution registration, with fail-closed diagnostics and focused tests.","branch":"impl/00001KV5R5V2S-plugin-enable-resolver","worktree":"/home/hare/Projects/yoi/.worktree/00001KV5R5V2S-plugin-enable-resolver","role_plan":"Orchestrator creates dedicated implementation worktree and spawns Coder with write scope limited to that worktree. Reviewer will run read-only after implementation report. Can run in parallel with Panel startup E2E work because source surfaces are disjoint."},"author":"yoi-orchestrator","at":"2026-06-15T14:00:41Z"}
- Discovery is read-only and returns typed `DiscoveredPluginPackage` metadata only.
- Resolution only activates packages explicitly listed in config and returns typed `ResolvedPlugin` metadata.
- No runtime initialization, code execution, WASM runtime, hook/tool/service/ingress registration, network, package-manager, or MCP behavior was added.
- Source-qualified ids are required; unqualified ids fail closed as ambiguous.
- Unsupported plugin API versions fail closed.
- Digest pins are checked against deterministic package content digests.
- Traversal/root escape/symlink escape checks are enforced.
- Package count, package size, manifest size, entry count, per-file size, and expanded size are bounded.
- Non-empty authority-bearing grants fail closed, so plugins do not receive ambient workspace filesystem authority.
Diagnostics:
- Added typed diagnostic kinds for missing, duplicate, ambiguous, version, digest, API, surface, grant, malformed, traversal, bounds, and I/O.
- Diagnostics avoid full package path/content disclosure and use bounded/sanitized messages.
Validation reported by coder:
- Passed: `cargo fmt --check`
- Passed: `cargo test -p manifest plugin`
- Passed: `cargo test -p manifest`
- Passed: `cargo check`
- Passed: `cargo test -p pod spawn_pod`
- Passed: `git diff --check`
- Passed: `nix build .#yoi`
Residual risks / notes:
- Current archive reader intentionally supports a constrained stored-ZIP profile and fails closed on unsupported compression methods. Broader `.zip` compression support can be added later if package tooling requires it.
- Ticket requires enablement entries to express package version/version constraint and requires version mismatch to be a distinct diagnostic.
-`PluginEnablementConfig` currently has `id`, `digest`, `surfaces`, `grants`, `config`, but no version/version constraint field.
-`resolve_enabled_plugins` never compares enablement against `package.manifest.version`.
-`PluginPackageManifest` has `version`, but it is only validated non-empty.
-`PluginDiagnosticKind::Version` currently appears to be used for unsupported API version, so package-version mismatch and API incompatibility are not clearly separated.
Required fix:
- Add a typed version/version requirement field to enablement config, or explicitly documented minimal exact-version field if constraints are deferred.
- Compare it to `manifest.version` during resolution.
- Emit a distinct version-mismatch diagnostic separate from incompatible API version.
- Add tests for version mismatch fail-closed behavior.
2. Startup/restore determinism is not satisfied.
- Ticket requires deterministic startup/restore behavior for the resolved plugin set.
- Implementation preserves authoring config, but no resolved plugin metadata/digest recording or deterministic restore re-resolution path was found.
- Unpinned enablement can resolve to a different package if mutable user/workspace store changes before restore.
- The design doc also states restore should use a resolved plan, not fresh discovery choosing newer packages.
Required fix:
- Either persist resolved plugin identity/digest metadata into resolved manifest/session metadata used for restore, or define and implement deterministic re-resolution semantics that cannot silently change a restored plugin set.
- Add focused test or validation evidence for the chosen restore/reproducibility path.
- If intentionally deferred, Ticket acceptance/report must be updated before approval because current acceptance still requires it.
3. Bounded diagnostic truncation can panic on valid UTF-8.
-`bounded_message` slices a Rust `String` at byte offset 240.
- It is used for TOML parse errors from untrusted plugin manifests.
- If byte 240 falls inside a multibyte UTF-8 character, slicing panics instead of producing a bounded diagnostic.
Required fix:
- Truncate on character boundary using safe helper logic.
- Add malformed manifest test with long multibyte content proving diagnostics remain bounded and non-panicking.
- Consider reducing raw TOML-error content leakage because diagnostics should avoid secret-like path/content leakage.
Additional concern:
- Design doc examples use `schema_version`, but implemented parser requires `api_version`.
- Align schema naming before merge to avoid contradictory package-author guidance.
Conclusion:
- Changes requested. Do not integrate until these blockers are fixed and covered.
- Added runtime resolved-plan metadata to `PluginConfig`:
-`resolved: Vec<ResolvedPluginRecord>`
-`diagnostics: Vec<PluginDiagnostic>`
- Added `resolve_plugin_config_for_startup`.
- Fresh startup resolves enabled plugins and records identity/path/label/digest/version/manifest/enabled surfaces/grants/config into manifest plugin config.
- Restore/snapshot configs with an existing resolved plan are reused as-is and do not silently select newer mutable store contents.
- Wired startup resolution into pod manifest resolution in `crates/pod/src/entrypoint.rs`.
- Preserved resolved plugin plan through config merge.
- Plugin package manifest parser now uses `schema_version`, matching `docs/design/plugin-packages.md`.
- Updated tests/helpers accordingly.
- Updated design doc enablement example to include implemented exact `version` field and describe deferred richer constraints.
Validation reported by coder:
- Passed: `cargo fmt --check`
- Passed: `cargo test -p manifest plugin`
- Passed: `cargo test -p manifest`
- Passed: `cargo check`
- Passed: `cargo test -p pod spawn_pod`
- Passed: `git diff --check`
- Passed: `nix build .#yoi`
Repository status:
- Child implementation worktree clean after follow-up commit.
Residual note:
- Version constraints are intentionally minimal exact-version matching for this Ticket. Richer semantic/range constraints remain deferred and documented as follow-up scope.
- HEAD `ede7acfd fix: pin plugin resolution metadata`
- base `4772c4d6`
- Relevant files:
-`crates/manifest/src/plugin.rs`
-`crates/pod/src/entrypoint.rs`
-`crates/pod/src/pod.rs`
-`docs/design/plugin-packages.md`
Validation performed by reviewer:
- Passed: `git diff --check 4772c4d6..HEAD`
- Passed: `cargo fmt --check`
-`git status --short` clean
Validation not run:
-`cargo test`, `cargo check`, and `nix build` were not rerun because reviewer scope was read-only and those commands write build artifacts. Coder-reported results were treated as evidence only.
Remaining blockers:
1. Startup/restore determinism is incomplete for non-profile / spawn-config launches.
Positive pieces exist:
-`apply_plugin_resolution_plan()` resolves plugins during manifest resolution.
-`resolve_plugin_config_for_startup()` skips fresh discovery when `plugins.resolved` or `plugins.diagnostics` already exists.
- focused helper test confirms a restored resolved plan does not pick newer mutable store contents.
Blocker:
- resolved manifest snapshot is only persisted to Pod metadata when `self.manifest.profile.is_some()`.
- Restore uses saved snapshot if present, otherwise falls back to freshly resolved current manifest.
- A named Pod started from `--manifest` or spawn config can resolve plugin metadata at startup but fail to persist that resolved plan, then later restore can silently re-resolve against newer mutable store contents unless authoring config had digest pin.
Required fix:
- Persist the resolved manifest snapshot for plugin-resolved manifests regardless of `manifest.profile`, or otherwise make non-profile restore deterministically use the original resolved plan.
- Add focused test/validation for the runtime persistence path, not only the helper behavior.
2. Documentation examples still appear broader than the implemented strict parser.
Resolved part:
-`schema_version` naming is now consistent in implementation/tests/minimal docs.
Remaining mismatch:
- docs illustrative `plugin.toml` still includes fields/sections rejected by current `#[serde(deny_unknown_fields)]` parser, such as `[package]` and `[permissions]`.
- docs describe `runtime.kind = "declarative"` as an initial value while implementation rejects runtime kinds other than `"wasm"`.
Required fix:
- Either mark those fields/sections as future/aspirational and provide a minimal currently-valid manifest example, or extend the parser to accept the documented first-pass fields safely.
Prior blockers resolved:
- Version mismatch support is resolved with exact-version field and distinct `Version` vs `Api` diagnostics.
- Safe bounded diagnostics are mostly resolved with UTF-8-boundary truncation and redacted TOML parse diagnostics.
Second follow-up implementation report after reviewer request_changes:
New commit:
-`07978d2d fix: persist plugin snapshots for restore`
Resolved remaining blockers:
1. Non-profile / spawn-config restore determinism
- Changed Pod metadata snapshot persistence so resolved manifests are snapshotted when either:
-`manifest.profile.is_some()` (existing profile behavior), or
-`manifest.plugins.has_resolved_plan()` (new plugin-resolved manifest behavior).
- Added helper path:
-`pod_metadata_for_manifest`
-`should_persist_resolved_manifest_snapshot`
- A named Pod whose plugin enablement is resolved at startup now persists resolved plugin metadata/digest plan in `resolved_manifest_snapshot` even without a profile.
- Restore therefore uses the original resolved plan instead of silently rediscovering mutable stores.
- Clarified referenced files must exist in the package archive.
- Clarified optional `[runtime]` currently accepts only:
-`kind = "wasm"`
-`entry = "..."`
-`abi = "yoi-plugin-wasm-1"`
- Marked `[package]`, `[permissions]`, richer `contributions`, and `runtime.kind = "declarative"` as future/aspirational and intentionally rejected by strict parser for now.
- HEAD includes `07978d2d fix: persist plugin snapshots for restore`
- Non-profile / spawn-config restore determinism:
-`pod_metadata_for_manifest` persists `resolved_manifest_snapshot` when either profile is present or `manifest.plugins.has_resolved_plan()` is true.
- Pending and active metadata writes use the helper.
- Restore path prefers `metadata.resolved_manifest_snapshot` when present and falls back only when absent.
- Existing profile behavior is preserved.
- Focused test `plugin_resolved_manifest_snapshot_is_persisted_without_profile` exercises profile-none metadata construction with resolved plugin records and verifies identity/version/digest are retained.
- Docs/parser alignment:
-`docs/design/plugin-packages.md` now includes a minimal strict currently-valid `plugin.toml` example using `schema_version = 1`, `[runtime] kind = "wasm"`, and currently accepted hook metadata.
- Future/unsupported `[package]`, `[permissions]`, richer contributions, and `runtime.kind = "declarative"` are explicitly marked future/unsupported rather than current parser surface.
- Parser/tests align with current schema/runtime shape.
- Prior acceptance spot-check:
- Discovery remains inactive without explicit enablement.
- Version mismatch and API incompatibility diagnostics are distinct.
- Diagnostics are bounded/sanitized.
- Authority grants fail closed.
- No Tool/Hook/Service/Ingress registration plumbing was added.
Validation performed by reviewer:
- Passed: `cargo fmt --check`
- Passed: `git diff --check 4772c4d6..HEAD`
Validation not rerun by reviewer:
-`cargo test`, `cargo check`, and `nix build .#yoi` were not rerun due read-only review scope. Coder-reported successful heavier validation was used as supporting evidence.
Risk assessment:
- Residual risk is low. Snapshot persistence is in the central Pod metadata helper and restore already prefers persisted snapshots.
- Remaining coverage is unit/integration-level rather than full E2E process restore, matching current project E2E boundary and not a blocker for this Ticket.
- Initial review requested changes for version mismatch support, restore determinism, UTF-8-safe diagnostics, and docs/parser mismatch.
- Second review requested changes for non-profile/spawn restore determinism and docs/parser mismatch.
- Third-pass review `approve` recorded from `yoi-reviewer-00001KV5R5V2S-r3`.
Validation in Orchestrator worktree `/home/hare/Projects/yoi/.worktree/orchestration`:
- Passed: `cargo fmt --check`
- Passed: `cargo test -p manifest plugin`
- 19 passed
- Passed: `cargo test -p manifest`
- 54 passed
- Passed: `cargo test -p pod plugin_resolved_manifest_snapshot_is_persisted_without_profile`
- 1 passed
- Passed: `cargo test -p pod spawn_pod`
- 7 passed
- Passed: `cargo check`
- Passed: `git diff --check`
- Passed: `nix build .#yoi --no-link`
Outcome:
- Plugin package discovery and explicit enablement resolver are implemented as typed manifest-layer functionality.
- Discovery remains inactive/read-only without explicit enablement.
- Resolved plugin metadata/digests can be snapshotted for restore determinism, including non-profile/plugin-resolved manifests.
- No runtime Tool/Hook/Service/Ingress registration or plugin execution path was added.
- Package safety, source-qualified identity, exact version pins, digest pins, bounded diagnostics, and strict docs/parser alignment are covered by focused tests.
Cleanup planned:
- Stop related coder/reviewer Pods.
- Remove only child implementation worktree/branch for this Ticket.
---
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-15T15:30:00Z from: inprogress to: done reason: merged_validated field: state -->
## State changed
Reviewer approved after requested fixes, implementation branch merged into the orchestration branch, and focused plus packaging validation passed in the Orchestrator worktree. Marking Ticket done in the orchestration branch.
---
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.