13 KiB
作成
LocalTicketBackend によって作成されました。
State changed
Ticket を workspace-panel が queued にしました。
Decision
Routing decision: implementation_ready
Reason:
- Panel Queue により routing が明示的に許可され、Ticket は
queued。 - Ticket body / thread / relations / OrchestrationPlan / Orchestrator workspace state を確認した。blocking relation はなく、planning に戻す concrete missing information はない。
- Prior Plugin package design
00001KT0Z4BK8は done 済みで、本 Ticket はその設計を踏まえた discovery + explicit enablement resolver の最初の実装として具体化されている。 - Risk flags は plugin / package-loading / discovery / enablement / capability-boundary / startup-restore だが、non-goals と fail-closed / read-only / no-registration invariants が明確で、残る不確実性は typed module/config/resolver design の実装戦術に閉じている。
Evidence checked:
- Ticket body/thread: scope、requirements、non-goals、acceptance criteria、implementation notes、related work を確認。
- Ticket relations: blocker なし。
- OrchestrationPlan: 既存 record なし。
- Orchestrator workspace:
/home/hare/Projects/yoi/.worktree/orchestrationは clean、425a6c66上。 - Visible Pods: implementation child Pod なし。
- Related design context:
00001KT0Z4BK8done(Plugin package/discovery design)。
IntentPacket:
Intent:
- Plugin package discovery と explicit enablement resolver を typed module として実装し、package presence / discovery / enablement / runtime initialization / contribution registration を明確に分離する。
Binding decisions / invariants:
- Discovery は read-only。package の存在だけで execution / Tool / Hook / Service / Ingress registration を行わない。
- Explicit enablement entry がなければ Plugin は active にならない。
- Source-qualified identity (
user:<id>,project:<id>,builtin:<id>) を扱い、ambiguous unqualified id は fail closed。 - Package safety checks は path traversal / root escape / bounded count/size / manifest size / deterministic digest を含む。
- unsupported/incompatible API version、digest mismatch、version mismatch、missing package、duplicate/ambiguous id、unsupported surface/grant は区別可能な diagnostic にする。
- Diagnostics を model-visible context に勝手に差し込まない。
- Plugin code execution / WASM runtime / actual Tool/Hook/Service/Ingress registration / MCP bridge は non-goal。
- No ambient workspace filesystem authority を plugin package discovery から発生させない。
Requirements / acceptance criteria:
- User store
${XDG_DATA_HOME:-~/.local/share}/yoi/plugins/*.yoi-pluginと workspace store<workspace>/.yoi/plugins/*.yoi-pluginから package を発見できる。 - Valid package root の
plugin.tomlを parse し typed manifest と deterministic digest を得る。 - Invalid package は startup 全体を不要に壊さず bounded diagnostic で fail closed。
- Package without enablement is not active。
- Explicit enablement resolves package to typed resolved Plugin metadata。
- Tests cover valid user/workspace discovery、discovery-only inactive、explicit enablement、duplicate/ambiguous fail-closed、digest mismatch、path traversal/root escape、unsupported api、malformed manifest、no contribution registration。
Implementation latitude:
- Small typed module/crate-local module を追加してよい。runtime launch code に resolver logic を埋め込まない。
.yoi-pluginarchive vs directory minimal implementation は prior design に合わせる。必要なら最小サポート範囲を明示し後続拡張可能にする。- Exact config/profile shape は既存 Profile / manifest design に合わせて最小 typed structure を追加してよい。
- Startup/restore reproducibility は deterministic re-resolution か resolved digest metadata 保持のどちらかを実装判断。ただし runtime-only mutable state 依存は不可。
Escalate if:
- Profile/manifest authority semantics、Pod restore semantics、secret handling、MCP enablement model を変える必要がある。
- Package archive implementation needs signature/trust/install/update/registry semantics。
- Arbitrary external filesystem/network authority が必要になる。
- Runtime registration/WASM execution なしでは acceptance を満たせないことが判明する。
Validation:
- focused tests for plugin discovery/resolver。
cargo fmt --check。- relevant
cargo check/cargo test。 git diff --check。nix build .#yoiif dependencies, runtime resources, packaging, or Cargo.lock changes matter。
Critical risks / reviewer focus:
- discovery vs enablement vs runtime/registration separation。
- fail-closed package safety / diagnostics。
- source-qualified identity and ambiguous refs。
- no contribution registration / no side effects from discovery。
- startup/restore determinism。
- secret-like diagnostic redaction。
- Plugin permission/grant requests not confused with actual grants。
State changed
Routing decision と accepted implementation plan を記録済み。blocking relation / unresolved OrchestrationPlan blocker はなく、Plugin resolver work は同時に開始する Panel startup latency work と主対象が異なるため、implementation side effects の前に queued -> inprogress acceptance を記録する。
Implementation report
Implementation report from Coder Pod yoi-coder-00001KV5R5V2S:
Commit:
a03a9da6 feat: add plugin package resolver
Changed files:
crates/manifest/src/plugin.rs- New typed plugin discovery/resolution module.
crates/manifest/src/lib.rs- Added
pluginsconfig toPodManifest. - Added parsing test for
[[plugins.enabled]].
- Added
crates/manifest/src/config.rs- Added
pluginstoPodManifestConfigand merge/materialization path.
- Added
crates/manifest/src/profile.rs- Added profile/Lua config support for plugin enablement config.
crates/pod/src/spawn/tool.rs- Preserves plugin config when deriving child manifest config.
crates/manifest/Cargo.toml- Added
sha2dependency for deterministic package digests.
- Added
Cargo.lock- Updated manifest crate dependency metadata.
package.nix- Updated
cargoHash.
- Updated
Resolver/config shape:
- Config supports explicit enablement through
[[plugins.enabled]]:id = "project:<id>" | "user:<id>" | "builtin:<id>"- optional
digest = "sha256:<hex>" - optional
surfaces = ["hook", ...] - optional
[plugins.enabled.config] - optional grants, currently fail closed when authority-bearing/non-empty.
- Discovery stores:
- User:
${XDG_DATA_HOME:-~/.local/share}/yoi/plugins/*.yoi-plugin - Workspace:
<workspace>/.yoi/plugins/*.yoi-plugin
- User:
Implemented behavior:
- Discovery is read-only and returns typed
DiscoveredPluginPackagemetadata only. - Resolution only activates packages explicitly listed in config and returns typed
ResolvedPluginmetadata. - 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
.zipcompression support can be added later if package tooling requires it.
Review: request changes
Review result: request_changes
Evidence checked:
- Child worktree only:
/home/hare/Projects/yoi/.worktree/00001KV5R5V2S-plugin-enable-resolver- branch/head
impl/00001KV5R5V2S-plugin-enable-resolverata03a9da6
- Diff files:
crates/manifest/src/plugin.rscrates/manifest/src/config.rscrates/manifest/src/lib.rscrates/manifest/src/profile.rscrates/pod/src/spawn/tool.rscrates/manifest/Cargo.tomlCargo.lockpackage.nixdocs/design/plugin-packages.md
- Ticket record:
.yoi/tickets/00001KV5R5V2S/item.md.yoi/tickets/00001KV5R5V2S/thread.md
Read-only validation performed:
- Passed:
git diff --check 4772c4d6..a03a9da6
Positive findings:
- Discovery/resolution code is isolated in
crates/manifest/src/plugin.rs. - No runtime registration, WASM execution, Tool/Hook/Service/Ingress contribution path was found.
- User/workspace stores are represented.
- Discovery checks include store containment, symlink escape rejection, path normalization, count/size/manifest bounds, duplicate normalized path rejection, and deterministic digest.
- Package presence alone does not activate anything; resolution requires
plugins.enabled. - Manifest/profile/child-spawn config plumbing preserves
pluginsconfig.
Required changes:
- Version mismatch support is missing.
- Ticket requires enablement entries to express package version/version constraint and requires version mismatch to be a distinct diagnostic.
PluginEnablementConfigcurrently hasid,digest,surfaces,grants,config, but no version/version constraint field.resolve_enabled_pluginsnever compares enablement againstpackage.manifest.version.PluginPackageManifesthasversion, but it is only validated non-empty.PluginDiagnosticKind::Versioncurrently 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.versionduring resolution. - Emit a distinct version-mismatch diagnostic separate from incompatible API version.
- Add tests for version mismatch fail-closed behavior.
- 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.
- Bounded diagnostic truncation can panic on valid UTF-8.
bounded_messageslices a RustStringat 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 requiresapi_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.