Compare commits

..

36 Commits

Author SHA1 Message Date
1c5c77bd08
ticket: report companion lifecycle implementation 2026-06-07 19:40:36 +09:00
2177dbf019
ticket: delegate companion pod lifecycle 2026-06-07 19:18:01 +09:00
e418560a12
ticket: preflight companion pod lifecycle 2026-06-07 19:17:33 +09:00
b453125870
ticket: close orchestrator diagnostic persistence 2026-06-07 19:16:08 +09:00
c1be0a4ac1
merge: orchestrator diagnostic persistence 2026-06-07 19:15:40 +09:00
7511cd4ed4
ticket: approve orchestrator diagnostic persistence 2026-06-07 19:15:40 +09:00
817f53bf77
ticket: report orchestrator diagnostic implementation 2026-06-07 19:10:37 +09:00
e15e9b7a47
fix: persist orchestrator lifecycle diagnostics 2026-06-07 19:09:01 +09:00
a7683bda8e
ticket: delegate orchestrator diagnostic persistence 2026-06-07 19:00:33 +09:00
7d9312d8a5
ticket: add orchestrator diagnostic persistence 2026-06-07 18:59:51 +09:00
b5861e6bf2
config: add workspace role profiles 2026-06-07 17:44:58 +09:00
383b655072
ticket: close memory root marker 2026-06-07 16:59:18 +09:00
297b66838b
merge: memory root marker 2026-06-07 16:58:25 +09:00
1838865507
ticket: approve memory root marker 2026-06-07 16:58:25 +09:00
084f051a4a
ticket: review memory root marker 2026-06-07 16:53:50 +09:00
9ed6613a94
memory: resolve repo memory by memory marker 2026-06-07 16:52:19 +09:00
546c8c3ac1
ticket: delegate memory root marker 2026-06-07 16:46:05 +09:00
afd683ac06
ticket: preflight memory root marker 2026-06-07 16:45:08 +09:00
94e028e807
ticket: close yoi worktree sparse exclusions 2026-06-07 16:38:37 +09:00
d61fcc1249
merge: narrow yoi worktree sparse exclusions 2026-06-07 16:38:00 +09:00
4d5492c998
ticket: approve yoi worktree sparse exclusions 2026-06-07 16:38:00 +09:00
6f33275309
fixup! workflow: narrow yoi worktree sparse exclusions 2026-06-07 16:36:00 +09:00
bfa728f7e5
ticket: add pod notification guidance task 2026-06-07 16:33:49 +09:00
7bf8f99a10
ticket: review yoi worktree sparse exclusions 2026-06-07 16:28:58 +09:00
ee85b51e55
workflow: narrow yoi worktree sparse exclusions 2026-06-07 16:27:43 +09:00
f6f10c7f9d
ticket: delegate yoi worktree sparse exclusions 2026-06-07 16:15:42 +09:00
0cc81cfd4f
ticket: preflight yoi worktree sparse exclusions 2026-06-07 16:14:53 +09:00
157fd8ff92
ticket: close orchestrator merge completion 2026-06-07 15:47:14 +09:00
81248b1c7f
merge: orchestrator merge completion 2026-06-07 15:46:28 +09:00
6976243328
ticket: approve orchestrator merge completion 2026-06-07 15:46:16 +09:00
33abf3f960
fixup! orchestrator: add merge completion guidance 2026-06-07 15:43:44 +09:00
e41401f31f
ticket: review orchestrator merge completion 2026-06-07 15:38:25 +09:00
9b91e56fea
ticket: add yoi worktree memory boundary tasks 2026-06-07 15:29:28 +09:00
cb177288be
orchestrator: add merge completion guidance 2026-06-07 15:20:17 +09:00
3a0ec9feb9
ticket: delegate orchestrator merge completion 2026-06-07 15:16:24 +09:00
8db57dd59a
ticket: preflight orchestrator merge completion 2026-06-07 15:15:39 +09:00
42 changed files with 1993 additions and 85 deletions

21
.yoi/profiles.toml Normal file
View File

@ -0,0 +1,21 @@
default = "project:companion"
[profile.companion]
description = "Companion role profile: GPT-5.5 with bundled default behavior"
path = "profiles/companion.lua"
[profile.intake]
description = "Intake role profile: GPT-5.5 with bundled default behavior"
path = "profiles/intake.lua"
[profile.orchestrator]
description = "Orchestrator role profile: GPT-5.5 with bundled default behavior"
path = "profiles/orchestrator.lua"
[profile.coder]
description = "Coder role profile: GPT-5.5 with bundled default behavior"
path = "profiles/coder.lua"
[profile.reviewer]
description = "Reviewer role profile: GPT-5.5 with bundled default behavior"
path = "profiles/reviewer.lua"

44
.yoi/profiles/_base.lua Normal file
View File

@ -0,0 +1,44 @@
local profile = require("yoi.profile")
local scope = require("yoi.scope")
local compact = require("yoi.compact")
return function(opts)
return profile {
slug = opts.slug,
description = opts.description,
scope = scope.workspace_write(),
session = {
record_event_trace = true,
},
worker = {
reasoning = "high",
},
model = {
ref = opts.model_ref,
},
compaction = compact.tokens {
threshold = 240000,
request_threshold = 270000,
worker_context_max_tokens = 100000,
},
memory = {
extract_threshold = 50000,
consolidation_threshold_files = 5,
consolidation_threshold_bytes = 50000,
},
web = {
enabled = true,
search = {
provider = "brave",
api_key_secret = "web/brave/default",
},
},
}
end

7
.yoi/profiles/coder.lua Normal file
View File

@ -0,0 +1,7 @@
local base = require("_base")
return base {
slug = "coder",
description = "Coder role profile: GPT-5.5 with bundled default behavior",
model_ref = "codex-oauth/gpt-5.5",
}

View File

@ -0,0 +1,7 @@
local base = require("_base")
return base {
slug = "companion",
description = "Companion role profile: GPT-5.5 with bundled default behavior",
model_ref = "codex-oauth/gpt-5.5",
}

7
.yoi/profiles/intake.lua Normal file
View File

@ -0,0 +1,7 @@
local base = require("_base")
return base {
slug = "intake",
description = "Intake role profile: GPT-5.5 with bundled default behavior",
model_ref = "codex-oauth/gpt-5.5",
}

View File

@ -0,0 +1,7 @@
local base = require("_base")
return base {
slug = "orchestrator",
description = "Orchestrator role profile: GPT-5.5 with bundled default behavior",
model_ref = "codex-oauth/gpt-5.5",
}

View File

@ -0,0 +1,7 @@
local base = require("_base")
return base {
slug = "reviewer",
description = "Reviewer role profile: GPT-5.5 with bundled default behavior",
model_ref = "codex-oauth/gpt-5.5",
}

View File

@ -3,21 +3,17 @@ provider = "builtin:yoi_local"
root = ".yoi/tickets" root = ".yoi/tickets"
[roles.intake] [roles.intake]
profile = "builtin:default" profile = "project:intake"
workflow = "ticket-intake-workflow" workflow = "ticket-intake-workflow"
[roles.orchestrator] [roles.orchestrator]
profile = "builtin:default" profile = "project:orchestrator"
workflow = "ticket-orchestrator-routing" workflow = "ticket-orchestrator-routing"
[roles.coder] [roles.coder]
profile = "builtin:default" profile = "project:coder"
workflow = "multi-agent-workflow" workflow = "multi-agent-workflow"
[roles.reviewer] [roles.reviewer]
profile = "builtin:default" profile = "project:reviewer"
workflow = "multi-agent-workflow" workflow = "multi-agent-workflow"
[roles.investigator]
profile = "builtin:default"
workflow = "ticket-preflight-workflow"

View File

@ -2,13 +2,13 @@
id: 20260607-035231-orchestrator-merge-completion id: 20260607-035231-orchestrator-merge-completion
slug: orchestrator-merge-completion slug: orchestrator-merge-completion
title: Orchestrator merge completion title: Orchestrator merge completion
status: open status: closed
kind: task kind: task
priority: P1 priority: P1
labels: [orchestrator, merge, ticket, workflow, validation] labels: [orchestrator, merge, ticket, workflow, validation]
workflow_state: intake workflow_state: done
created_at: 2026-06-07T03:52:31Z created_at: 2026-06-07T03:52:31Z
updated_at: 2026-06-07T05:00:22Z updated_at: 2026-06-07T06:47:03Z
assignee: null assignee: null
legacy_ticket: null legacy_ticket: null
--- ---

View File

@ -0,0 +1,26 @@
Implemented, reviewed, merged, and validated.
Summary:
- Added Orchestrator merge-completion guidance for reviewed in-progress Tickets with merge-ready dossiers.
- Merge authority is explicit: dogfooding/workspace policy may authorize merge/cleanup/close; conservative/missing authorization stops at the dossier.
- Dossier requirements are explicit: Ticket id/slug, branch/worktree, commits, intent/invariant check, implementation summary, coder/reviewer Pods, blockers fixed or rejected findings with reasons, validation performed, residual risks, dirty state, and parent/human decision needs.
- Post-merge validation baseline is explicit: focused Ticket/dossier tests, `cargo fmt --check`, `git diff --check`, `target/debug/yoi ticket doctor` where applicable, and broader validation such as `cargo check --workspace --all-targets` / `nix build .#yoi` when risk/touched files warrant it.
- Branch-local reviewer verdicts remain dossier evidence before merge; final main Ticket review/approval/completion records are written during merge-completion after confirming the reviewed branch is the branch being merged.
- Guidance covers merge, post-merge validation, Ticket `done`/close handling, Pod scope reclaim, and worktree/branch cleanup.
- Queue routing and worktree/coder/reviewer routing were not reimplemented in this ticket.
Implementation:
- Child commits: `cb17728 orchestrator: add merge completion guidance`, `33abf3f fixup! orchestrator: add merge completion guidance`
- Merge commit: `merge: orchestrator merge completion`
Review:
- External reviewer `orchestrator-merge-reviewer-20260607` requested changes for explicit dossier/validation requirements.
- Fixup addressed blockers.
- Reviewer approved after fixup.
Validation after merge:
- `cargo test -p client ticket_role --lib`
- `cargo fmt --check`
- `git diff --check`
- `cargo build -p yoi`
- `target/debug/yoi ticket doctor`

View File

@ -0,0 +1,278 @@
<!-- event: create author: LocalTicketBackend at: 2026-06-07T03:52:31Z -->
## Created
Created by LocalTicketBackend create.
---
<!-- event: decision author: hare at: 2026-06-07T05:00:22Z -->
## Decision
## Git/Ticket review record boundary
Decision:
- Reviewer verdicts on an unmerged implementation branch are branch-scoped evidence, not final main-branch Ticket approval.
- The Orchestrator should keep branch-local review results in the merge-ready dossier/review report during the worktree-agent phase.
- Main Ticket thread may still record durable progress such as worktree plan, coder/reviewer delegation, blockers, and fix-loop status.
- The final Ticket review/approval/completion record should be written during the merge-completion phase, after confirming the reviewed branch is the branch being merged, or as part of the same controlled completion sequence.
Rationale:
- Avoid main branch Ticket history claiming approval for code that is not yet in main.
- Keep git history aligned: implementation branch review evidence leads to merge; main Ticket lifecycle records the completed merge/validation outcome.
- Builtin Orchestrator workflow should make this explicit so automation does not reproduce the ad-hoc parent-side `TicketReview` timing used during manual dogfooding.
---
<!-- event: plan author: hare at: 2026-06-07T06:15:24Z -->
## Plan
## Preflight / implementation intent
Classification: implementation-ready as the third Orchestrator automation slice, after queued routing and worktree-agent routing landed.
Intent:
- Add explicit Orchestrator merge/completion guidance or focused wiring for reviewed in-progress Tickets that have a merge-ready dossier.
- Preserve the git/Ticket boundary: branch-local reviewer verdicts are evidence in the dossier before merge; final main Ticket approval/completion records are written only during the controlled merge-completion phase after confirming the reviewed branch is the branch being merged.
- Support dogfooding authorization for Orchestrator merge/cleanup/close, while conservative/missing authorization stops at merge-ready dossier.
Prerequisite state:
- `orchestrator-queued-ticket-routing` landed: Queue notification authorizes routing and requires `queued -> inprogress` before side effects.
- `orchestrator-worktree-agent-routing` landed: accepted Tickets can be routed through worktree + coder/reviewer sibling guidance and stop at merge-ready dossier.
- Ticket lifecycle tools and role launch config strictness/scaffold have landed.
Requirements for this slice:
- Add merge-completion guidance to the relevant Orchestrator role prompt/workflow surface.
- Merge conditions:
- Ticket is `inprogress` and has a merge-ready dossier;
- reviewed branch/worktree/commits in the dossier match the branch to be merged;
- independent reviewer approval exists in the dossier, or a human override decision is explicitly recorded;
- main workspace is safe and unrelated dirty changes are understood;
- merge authority is available in dogfooding/workspace policy, otherwise stop at dossier.
- Merge/completion sequence:
- stop/reclaim coder/reviewer Pods where appropriate;
- `git merge --no-ff <branch>` or project-agreed merge method;
- run post-merge validation appropriate to the change;
- record review/merge/validation outcome in the Ticket thread during merge-completion, not before;
- transition `inprogress -> done` or close the Ticket according to typed Ticket workflow rules;
- remove merged child worktree and delete merged branch unless explicitly kept.
- Add/update tests for merge authority boundary and required dossier/validation fields in generated guidance or helper text.
Likely implementation surfaces:
- `crates/client/src/ticket_role.rs`: existing Orchestrator role guidance already stops at merge-ready dossier; extend with merge-completion guidance for the authorized completion phase.
- `crates/tui/src/multi_pod.rs`: Queue notification should probably remain focused on routing/agent path and not over-expand into merge details unless existing tests require consistency.
- `.yoi/workflow/multi-agent-workflow.md` may need parent-side wording updates to align with branch-local verdict vs main Ticket approval boundary. Child worktree excludes `.yoi`, so any workflow edits must be reported for parent-side application.
Non-goals:
- Do not implement Queue notification/acceptance.
- Do not implement worktree/coder/reviewer routing.
- Do not remove human override path.
- Do not make merge authority implicit for all public/default configurations.
- Do not solve active workflow compaction persistence here.
Validation:
- Run focused tests for changed crates, especially `cargo test -p client ticket_role --lib` if role guidance changes.
- Run `cargo fmt --check`, `git diff --check`, and `target/debug/yoi ticket doctor` if Ticket/workflow records are touched.
---
<!-- event: intake_summary author: INSOMNIA at: 2026-06-07T06:15:32Z -->
## Intake summary
Implementation-ready as the third Orchestrator automation slice: add merge-completion guidance for reviewed in-progress Tickets with merge-ready dossiers, preserving branch-local review vs final main Ticket approval boundaries, dogfooding merge authorization, post-merge validation, Ticket completion, and worktree/branch/Pod cleanup.
---
<!-- event: state_changed author: INSOMNIA at: 2026-06-07T06:15:32Z from: intake to: ready reason: intake_ready field: workflow_state -->
## State changed
Ticket intake complete; workflow_state intake -> ready.
---
<!-- event: implementation_report author: hare at: 2026-06-07T06:16:17Z -->
## Implementation report
## Delegation
Implementation delegated to child Pod `orchestrator-merge-coder-20260607` in worktree `.worktree/orchestrator-merge-completion` on branch `work/orchestrator-merge-completion`.
Scope:
- Add Orchestrator merge/completion guidance for reviewed in-progress Tickets with merge-ready dossiers.
- Preserve branch-local reviewer verdict vs final main Ticket approval boundary.
- Define dogfooding merge authority vs conservative stop-at-dossier behavior.
- Include post-merge validation, Ticket completion, worktree/branch/Pod cleanup guidance.
- Do not implement queue routing or worktree/coder/reviewer routing.
Workflow note:
- Child worktree excludes `.yoi`; if workflow file updates are needed, the child should report an exact parent-side patch rather than editing `.yoi`.
The child should commit implementation work in the child worktree and report diff/tests. Merge, review, Ticket closure, workflow file updates, and cleanup remain with the parent/human workflow.
---
<!-- event: decision author: hare at: 2026-06-07T06:29:12Z -->
## Decision
## Related worktree/memory boundary follow-ups
Created two follow-up tickets from the `.yoi` sparse checkout / memory-root discussion:
- `narrow-yoi-worktree-sparse-exclusions`
- Stop excluding all `.yoi/**` from child worktrees.
- Include tracked Yoi project records such as tickets/workflows/config where appropriate.
- Continue excluding generated memory, logs, locks, local/runtime/secret-like files.
- Define child-vs-main Ticket edit policy for branch-local artifacts vs final orchestration records.
- `memory-root-uses-yoi-memory-marker`
- Keep `.yoi` as the Yoi project records marker.
- Stop treating `.yoi` alone as the memory root marker.
- Use explicit memory config or nearest ancestor `.yoi/memory` as repo-local memory root.
- Ensure child worktrees containing `.yoi/tickets` but not `.yoi/memory` do not become independent memory workspaces.
These are relevant to Orchestrator merge/worktree automation because branch-local dossiers and workflow edits should be possible without copying generated memory into child worktrees.
---
<!-- event: implementation_report author: hare at: 2026-06-07T06:38:16Z -->
## Implementation report
## Implementation report
Coder Pod `orchestrator-merge-coder-20260607` completed implementation in `.worktree/orchestrator-merge-completion`.
Commit:
- `cb17728 orchestrator: add merge completion guidance`
Observed diff:
- `crates/client/src/ticket_role.rs` only.
Implementation summary:
- Extended Orchestrator role guidance with merge-completion instructions for reviewed in-progress Tickets with merge-ready dossiers.
- Guidance requires the Ticket to be `inprogress` and the dossier branch/worktree/commits to match the branch being merged.
- Requires independent reviewer approval in the dossier or an explicit human override decision.
- Requires main workspace safety / unrelated dirty change awareness.
- Distinguishes dogfooding/workspace merge authority from conservative/missing authorization mode, which stops at the dossier.
- Preserves branch-local reviewer verdict vs final main Ticket approval boundary.
- Defines the authorized sequence: stop/reclaim coder/reviewer Pods where appropriate, merge, post-merge validation, record review/merge/validation outcome in Ticket thread, transition `inprogress -> done` or close, then cleanup worktree/branch unless explicitly kept.
- Added tests covering merge-completion guidance / dossier requirements.
Reported validation:
- `cargo test -p client ticket_role --lib`
- `cargo fmt --check`
- `git diff --check`
- `cargo build -p yoi`
- `nix build .#yoi`
Parent-side workflow patch needed: none observed from the implementation; child diff is code/prompt-generation only.
External review will be delegated before merge.
---
<!-- event: review author: orchestrator-merge-reviewer-20260607 at: 2026-06-07T06:41:49Z status: request_changes -->
## Review: request changes
Request changes.
The implementation gets the merge authority and branch-review boundary right, but it does not yet satisfy the ticket's required dossier/validation specificity, and the added test does not cover those missing fields.
Positive evidence:
- Merge authority boundary is good: conservative/missing authorization stops at the dossier and merge authority is not inferred from public/default config.
- Branch-local reviewer verdict vs final main Ticket approval boundary is preserved.
- Queue routing and worktree/coder/reviewer routing were not reimplemented beyond guidance; child commit changes only `crates/client/src/ticket_role.rs`.
Blockers:
1. Merge-ready dossier fields are not explicit enough. The current text lists branch, worktree path, commits, implementation summary, validation evidence, coder/reviewer evidence, blocker loop outcome, residual risk, and parent decision needs, but the ticket explicitly requires Ticket id/slug, intent/invariant check, coder/reviewer Pods, blockers fixed or rejected findings with reasons, validation performed, and dirty state. Several are missing or only implied.
2. Post-merge validation baseline is too generic. The ticket requires explicit minimum baseline guidance: focused tests from Ticket/dossier, `cargo fmt --check`, `git diff --check`, `target/debug/yoi ticket doctor` where applicable, plus broader validation such as `cargo check --workspace --all-targets` / `nix build .#yoi` when risk or touched files warrant it.
3. Test coverage mirrors the same gap. The new test covers authority, branch/worktree/commit matching, reviewer approval/override, main workspace safety, branch-local boundary, merge command, cleanup, and generic validation evidence, but does not assert the missing dossier fields or required validation baseline commands.
Reviewer validation:
- `cargo test -p client ticket_role --lib`
- `cargo fmt --check`
- `git diff --check develop..HEAD`
- `cargo build -p yoi`
- `target/debug/yoi ticket doctor` from main workspace
- `nix build .#yoi` (dirty-tree warning only; generated `result` symlink removed)
- merge-tree check against current `develop`
Merge readiness: not merge-ready until dossier/validation guidance and tests are explicit.
---
<!-- event: review author: orchestrator-merge-reviewer-20260607 at: 2026-06-07T06:46:06Z status: approve -->
## Review: approve
Approved after fixup commit `33abf3f`.
Evidence:
- Dossier fields are now explicit in `crates/client/src/ticket_role.rs`, including Ticket id/slug, branch/worktree, commits, intent/invariant check, implementation summary, coder/reviewer Pods, blockers fixed or rejected findings with reasons, validation performed, residual risks, dirty state, and parent/human decision needs.
- Post-merge validation baseline is explicit: focused Ticket/dossier tests, `cargo fmt --check`, `git diff --check`, `target/debug/yoi ticket doctor` where applicable, plus broader validation including `cargo check --workspace --all-targets` and `nix build .#yoi` when risk/touched files warrant it.
- Tests now assert the required dossier fields and validation baseline/broader-validation guidance.
- Merge authority remains explicit; conservative/missing authorization stops at the dossier and does not infer authority from public/default config.
- Branch-local reviewer verdict vs final main Ticket approval boundary remains preserved.
- Diff remains limited to `crates/client/src/ticket_role.rs`; no queue/worktree/coder/reviewer scheduler or TUI behavior was added.
Reviewer validation:
- `cargo test -p client ticket_role --lib`
- `cargo fmt --check`
- `git diff --check develop...HEAD`
- `git merge-tree --write-tree develop HEAD`
Operational note from reviewer: main workspace dirty-state should be checked before merge; observed dirtiness was Ticket files/backend lock from orchestration records, not a branch implementation issue.
---
<!-- event: state_changed author: hare at: 2026-06-07T06:47:03Z from: ready to: done reason: closed field: workflow_state -->
## State changed
Ticket closed; workflow_state set to done.
---
<!-- event: close author: hare at: 2026-06-07T06:47:03Z status: closed -->
## Closed
Implemented, reviewed, merged, and validated.
Summary:
- Added Orchestrator merge-completion guidance for reviewed in-progress Tickets with merge-ready dossiers.
- Merge authority is explicit: dogfooding/workspace policy may authorize merge/cleanup/close; conservative/missing authorization stops at the dossier.
- Dossier requirements are explicit: Ticket id/slug, branch/worktree, commits, intent/invariant check, implementation summary, coder/reviewer Pods, blockers fixed or rejected findings with reasons, validation performed, residual risks, dirty state, and parent/human decision needs.
- Post-merge validation baseline is explicit: focused Ticket/dossier tests, `cargo fmt --check`, `git diff --check`, `target/debug/yoi ticket doctor` where applicable, and broader validation such as `cargo check --workspace --all-targets` / `nix build .#yoi` when risk/touched files warrant it.
- Branch-local reviewer verdicts remain dossier evidence before merge; final main Ticket review/approval/completion records are written during merge-completion after confirming the reviewed branch is the branch being merged.
- Guidance covers merge, post-merge validation, Ticket `done`/close handling, Pod scope reclaim, and worktree/branch cleanup.
- Queue routing and worktree/coder/reviewer routing were not reimplemented in this ticket.
Implementation:
- Child commits: `cb17728 orchestrator: add merge completion guidance`, `33abf3f fixup! orchestrator: add merge completion guidance`
- Merge commit: `merge: orchestrator merge completion`
Review:
- External reviewer `orchestrator-merge-reviewer-20260607` requested changes for explicit dossier/validation requirements.
- Fixup addressed blockers.
- Reviewer approved after fixup.
Validation after merge:
- `cargo test -p client ticket_role --lib`
- `cargo fmt --check`
- `git diff --check`
- `cargo build -p yoi`
- `target/debug/yoi ticket doctor`
---

View File

@ -0,0 +1,61 @@
---
id: 20260607-062902-memory-root-uses-yoi-memory-marker
slug: memory-root-uses-yoi-memory-marker
title: Use .yoi/memory marker for repo-local memory root
status: closed
kind: task
priority: P1
labels: [memory, workspace, worktree, config]
workflow_state: done
created_at: 2026-06-07T06:29:02Z
updated_at: 2026-06-07T07:59:04Z
assignee: null
legacy_ticket: null
---
## Background
`.yoi/` currently acts as a broad Yoi workspace/project marker, but using `.yoi` existence as the memory workspace/root marker is too coarse. `.yoi` now contains project records such as Tickets, workflows, Knowledge, and config, while `.yoi/memory` is generated/personal memory state.
Child worktrees should be able to include `.yoi/tickets` and `.yoi/workflow` without becoming independent memory roots. Memory root detection should key off `.yoi/memory` or explicit memory configuration rather than `.yoi` alone.
## Goal
Separate Yoi project/workspace detection from repo-local memory root detection by treating `.yoi` as the project records marker and `.yoi/memory` as the repo-local memory root marker.
## Target model
- `.yoi/` means: this repository/workspace has Yoi project records/config.
- `.yoi/memory/` means: this workspace or ancestor is the repo-local memory root.
- Child worktrees may contain `.yoi/tickets` / `.yoi/workflow` without causing memory writes into the child worktree.
- Memory root lookup can walk ancestors until it finds `.yoi/memory` or an explicit configured root.
## Requirements
- Audit current workspace/memory root detection code that uses `.yoi` existence.
- Change memory root detection so `.yoi` alone is not enough to select a memory root.
- Use an explicit memory root if configured.
- If repo-local memory is enabled and no explicit root is configured, use nearest ancestor containing `.yoi/memory`.
- If memory is explicitly enabled for a workspace and no `.yoi/memory` exists yet, create the configured/default memory directory lazily rather than requiring both config and preexisting directory.
- If memory is explicitly disabled, do not use `.yoi/memory` even if present, unless a separate compatibility mode intentionally says otherwise.
- Decide and document fallback behavior when no explicit memory config and no ancestor `.yoi/memory` exists:
- disabled; or
- user-data workspace overlay;
- but do not silently treat `.yoi` alone as memory root.
- Preserve existing generated-memory safety rules: do not copy memory into child worktrees and keep memory logs/generated records out of git-tracked project artifacts.
- Update docs/tests for child worktree behavior and memory root lookup.
## Non-goals
- Removing `.yoi` as a Yoi project records marker.
- Removing repo-local `.yoi/memory` support entirely.
- Changing Ticket/workflow/project record roots.
- Implementing the child worktree sparse-checkout change; that belongs to `narrow-yoi-worktree-sparse-exclusions`.
## Acceptance criteria
- `.yoi` alone no longer selects a memory root.
- `.yoi/memory` can be found by ancestor walk and used as repo-local memory root when enabled/allowed.
- A child worktree containing `.yoi/tickets` but not `.yoi/memory` does not become an independent memory workspace.
- Explicit memory enable/disable behavior is tested.
- Documentation clearly distinguishes `.yoi` project records marker from `.yoi/memory` repo-local memory marker.

View File

@ -0,0 +1,25 @@
Implemented, reviewed, merged, and validated.
Summary:
- Kept `.yoi` as the Yoi project records marker while stopping `.yoi` alone from acting as the memory root marker.
- `WorkspaceLayout::resolve` still honors explicit `memory.workspace_root` exactly.
- Without explicit root, memory layout resolution now walks ancestors from the default root and selects the nearest ancestor containing `.yoi/memory`.
- Child worktrees containing `.yoi/tickets` / `.yoi/workflow` but no `.yoi/memory` do not become independent memory roots when an ancestor `.yoi/memory` exists.
- `.yoi` project records alone do not define a memory marker root.
- No-marker fallback remains `default_root` as documented compatibility behavior, without relying on `.yoi` presence.
- No broad memory enable/disable redesign or user-data overlay fallback was introduced.
- `MemoryConfig` docs were updated to describe marker-based implicit resolution.
Implementation:
- Child commit: `9ed6613 memory: resolve repo memory by memory marker`
- Merge commit: `merge: memory root marker`
Review:
- External reviewer `memory-root-marker-reviewer-20260607` approved with no blockers.
Validation after merge:
- `cargo test -p memory workspace --lib`
- `cargo test -p pod memory --lib`
- `cargo fmt --check`
- `git diff --check`
- `target/debug/yoi ticket doctor`

View File

@ -0,0 +1,196 @@
<!-- event: create author: LocalTicketBackend at: 2026-06-07T06:29:02Z -->
## Created
Created by LocalTicketBackend create.
---
<!-- event: plan author: hare at: 2026-06-07T07:44:53Z -->
## Plan
## Preflight / implementation intent
Classification: implementation-ready after `narrow-yoi-worktree-sparse-exclusions` landed.
Intent:
- Keep `.yoi` as the Yoi project records/workspace marker.
- Stop using `.yoi` alone as the memory root marker.
- Resolve repo-local memory through an explicit `memory.workspace_root` or an ancestor `.yoi/memory` marker, so child worktrees can contain `.yoi/tickets`/`.yoi/workflow` without becoming independent memory roots.
Current code map:
- `crates/memory/src/workspace.rs`
- `WorkspaceLayout::resolve(cfg, default_root)` currently uses `cfg.workspace_root.unwrap_or(default_root)`.
- `memory_dir()` is `<root>/.yoi/memory`; `knowledge_dir()` and `workflow_dir()` are also rooted under `<root>/.yoi`.
- Tests currently assert fallback to default root.
- `crates/pod/src/pod.rs`
- Memory extract/consolidation/tools/resident setup call `WorkspaceLayout::resolve(&memory_cfg, &self.pwd)`.
- `crates/yoi/src/memory_lint.rs`
- CLI lint works with explicit workspace path and may need to keep explicit workspace behavior.
- `manifest::MemoryConfig` currently uses `Option<MemoryConfig>` presence as enablement and `workspace_root: Option<PathBuf>` as the explicit root. There is not yet a separate `enabled = false` field.
Implementation direction:
- Add a small resolver in `memory::WorkspaceLayout` or nearby that:
- honors explicit `memory.workspace_root` exactly as today;
- when no explicit root exists, walks ancestors from the default root and selects the nearest ancestor that contains `.yoi/memory`;
- does not select a directory merely because it contains `.yoi`;
- if no ancestor `.yoi/memory` exists, uses an explicit, documented fallback that does not silently interpret `.yoi` alone as the memory root. Prefer fail/no-marker behavior for automatic memory operations over creating child-worktree memory; if a lazy-create path is needed, keep it tied to explicit configuration rather than implicit `.yoi` presence.
- Preserve `.yoi/knowledge`/`.yoi/workflow` project-record lookup semantics as much as possible; be explicit if moving memory root also changes knowledge/workflow layout root in a way that needs follow-up.
- Do not implement user-data overlay fallback unless it is already supported and clearly smaller.
- If adding a manifest `enabled` field would be broad, do not do it in this slice; document that current explicit disable is represented by absence of `[memory]` and keep the ticket focused on `.yoi/memory` marker resolution.
Tests to add/update:
- Explicit `workspace_root` still wins.
- No explicit root + ancestor `.yoi/memory` selects that ancestor.
- Child worktree containing `.yoi/tickets`/`.yoi/workflow` but no `.yoi/memory` does not select the child as memory root.
- `.yoi` alone does not select a memory root.
- Existing explicit workspace/lint behavior remains sensible.
Validation:
- Run `cargo test -p memory workspace --lib` or equivalent focused memory tests.
- Run relevant `cargo test -p pod memory --lib` if Pod memory setup tests are affected.
- Run `cargo test -p yoi memory --lib` if memory lint behavior changes.
- Run `cargo fmt --check`, `git diff --check`, and `target/debug/yoi ticket doctor`.
---
<!-- event: intake_summary author: INSOMNIA at: 2026-06-07T07:45:01Z -->
## Intake summary
Implementation-ready after narrowed worktree sparse exclusions: keep `.yoi` as project records marker, resolve repo-local memory from explicit `memory.workspace_root` or nearest ancestor `.yoi/memory`, and ensure `.yoi` alone does not make child worktrees independent memory roots.
---
<!-- event: state_changed author: INSOMNIA at: 2026-06-07T07:45:01Z from: intake to: ready reason: intake_ready field: workflow_state -->
## State changed
Ticket intake complete; workflow_state intake -> ready.
---
<!-- event: implementation_report author: hare at: 2026-06-07T07:45:55Z -->
## Implementation report
## Delegation
Implementation delegated to child Pod `memory-root-marker-coder-20260607` in worktree `.worktree/memory-root-uses-yoi-memory-marker` on branch `work/memory-root-uses-yoi-memory-marker`.
The worktree uses the new narrowed sparse rules:
- `.yoi` project records are visible;
- `.yoi/memory` is absent.
Scope:
- Keep `.yoi` as project records marker.
- Resolve memory from explicit `memory.workspace_root` or nearest ancestor `.yoi/memory`.
- Do not treat `.yoi` alone as memory root.
- Do not add broad memory enable/disable redesign.
- Do not change child worktree sparse rules; that has already landed.
The child should commit implementation work in the child worktree and report diff/tests. Merge, review, Ticket closure, and cleanup remain with the parent/human workflow.
---
<!-- event: implementation_report author: hare at: 2026-06-07T07:53:06Z -->
## Implementation report
## Implementation report
Coder Pod `memory-root-marker-coder-20260607` completed implementation in `.worktree/memory-root-uses-yoi-memory-marker`.
Commit:
- `9ed6613 memory: resolve repo memory by memory marker`
Reported summary:
- Updated `WorkspaceLayout::resolve` to honor explicit `memory.workspace_root` exactly.
- Without explicit root, it searches `default_root` ancestors for the nearest `.yoi/memory` marker.
- `.yoi` alone is never treated as a memory root marker.
- If no marker exists, it falls back to `default_root` as documented no-marker compatibility behavior.
- Added focused tests for explicit root, ancestor marker selection, child worktree project records without memory, and `.yoi`-alone non-marker behavior.
- Updated `MemoryConfig` docs for marker-based implicit resolution.
Reported validation:
- `cargo test -p memory workspace --lib`
- `cargo test -p pod memory --lib`
- `cargo test -p yoi memory` (reported no lib target)
- `cargo fmt --check`
- `git diff --check`
- `target/debug/yoi ticket doctor`
- `nix build .#yoi`
External review will be delegated before merge.
---
<!-- event: review author: memory-root-marker-reviewer-20260607 at: 2026-06-07T07:58:15Z status: approve -->
## Review: approve
Approved.
Evidence:
- Explicit `memory.workspace_root` behavior is preserved: `WorkspaceLayout::resolve` returns the explicit root before marker search.
- Implicit resolution now searches ancestors for `.yoi/memory` only, using nearest-ancestor order.
- `.yoi` alone is not treated as a memory marker; the resolver never checks `.yoi` by itself, and test coverage verifies project records alone do not define the memory marker root.
- Child worktree behavior is covered: a child `.yoi/tickets` / `.yoi/workflow` without child `.yoi/memory` resolves to the ancestor workspace that has `.yoi/memory`.
- No-marker fallback to `default_root` is acceptable for this slice as documented compatibility behavior and is not based on `.yoi` presence. A stricter no-marker-disabled behavior can be a future enable/disable redesign if desired.
- No broad memory enable/disable redesign or user-data overlay fallback was introduced.
Reviewer validation:
- `cargo test -p memory workspace --lib`
- `cargo test -p pod memory --lib`
- `cargo fmt --check`
- `git diff --check develop...HEAD`
- `target/debug/yoi ticket doctor`
- `nix build .#yoi`
- `git merge-tree --write-tree develop HEAD`
Merge readiness: approved; implementation worktree clean and merge-tree against current `develop` succeeded.
---
<!-- event: state_changed author: hare at: 2026-06-07T07:59:04Z from: ready to: done reason: closed field: workflow_state -->
## State changed
Ticket closed; workflow_state set to done.
---
<!-- event: close author: hare at: 2026-06-07T07:59:04Z status: closed -->
## Closed
Implemented, reviewed, merged, and validated.
Summary:
- Kept `.yoi` as the Yoi project records marker while stopping `.yoi` alone from acting as the memory root marker.
- `WorkspaceLayout::resolve` still honors explicit `memory.workspace_root` exactly.
- Without explicit root, memory layout resolution now walks ancestors from the default root and selects the nearest ancestor containing `.yoi/memory`.
- Child worktrees containing `.yoi/tickets` / `.yoi/workflow` but no `.yoi/memory` do not become independent memory roots when an ancestor `.yoi/memory` exists.
- `.yoi` project records alone do not define a memory marker root.
- No-marker fallback remains `default_root` as documented compatibility behavior, without relying on `.yoi` presence.
- No broad memory enable/disable redesign or user-data overlay fallback was introduced.
- `MemoryConfig` docs were updated to describe marker-based implicit resolution.
Implementation:
- Child commit: `9ed6613 memory: resolve repo memory by memory marker`
- Merge commit: `merge: memory root marker`
Review:
- External reviewer `memory-root-marker-reviewer-20260607` approved with no blockers.
Validation after merge:
- `cargo test -p memory workspace --lib`
- `cargo test -p pod memory --lib`
- `cargo fmt --check`
- `git diff --check`
- `target/debug/yoi ticket doctor`
---

View File

@ -0,0 +1,59 @@
---
id: 20260607-062902-narrow-yoi-worktree-sparse-exclusions
slug: narrow-yoi-worktree-sparse-exclusions
title: Narrow child worktree .yoi sparse exclusions
status: closed
kind: task
priority: P1
labels: [worktree, workflow, memory, ticket, orchestration]
workflow_state: done
created_at: 2026-06-07T06:29:02Z
updated_at: 2026-06-07T07:38:29Z
assignee: null
legacy_ticket: null
---
## Background
Current worktree workflow excludes `.yoi/**` from child implementation worktrees. This was originally a conservative way to keep generated/personal memory and runtime state out of child worktrees. As Ticket/Workflow orchestration has become project-record based, excluding all of `.yoi` is too broad.
Project records such as `.yoi/tickets`, `.yoi/workflow`, `.yoi/knowledge`, and `.yoi/ticket.config.toml` may need to be visible in child worktrees for branch-local artifacts, workflow edits, review dossiers, and Ticket-aware implementation. Generated memory/runtime/local files should still be excluded.
## Goal
Narrow child worktree sparse-checkout exclusions so git-tracked Yoi project records can appear in child worktrees while memory/local/runtime state remains excluded.
## Requirements
- Update `worktree-workflow` and any worktree creation helpers/scripts to stop excluding all of `.yoi/**`.
- Include git-tracked project record paths where appropriate:
- `.yoi/tickets/**`;
- `.yoi/workflow/**`;
- `.yoi/knowledge/**` when curated/tracked;
- `.yoi/ticket.config.toml`;
- other tracked Yoi project config/resources as needed.
- Continue excluding generated/personal/local/runtime paths, including at least:
- `.yoi/memory/**`;
- `.yoi/**/_logs/**`;
- `.yoi/tickets/.ticket-backend.lock`;
- local/override/secret-like files according to existing ignore/config policy.
- Define the edit policy for Ticket records in child worktrees:
- branch-local artifacts / merge-ready dossier may be written in child worktree when part of the implementation branch;
- active orchestration progress and final Ticket review/approval/close remain main-workspace responsibilities unless explicitly designed otherwise;
- avoid concurrent edits to the same Ticket thread from main and child worktree.
- Ensure generated/personal memory is not copied into child worktrees.
- Update tests/docs/workflows that currently assume `.yoi` is absent from child worktrees.
## Non-goals
- Moving memory storage out of `.yoi/memory`; that belongs to the memory root marker ticket.
- Making child implementation Pods responsible for final Ticket closure.
- Allowing secret/local files into child worktrees.
- Replacing the main workspace as the orchestration authority.
## Acceptance criteria
- New child worktrees include relevant tracked `.yoi` project records but exclude `.yoi/memory` and local/runtime files.
- Worktree workflow documentation states the new sparse-checkout rules and Ticket edit policy.
- Child worktree creation validation checks that memory/local paths are absent while project record paths can be present.
- Orchestrator workflows can use child worktrees for branch-local artifacts/dossiers without copying generated memory.

View File

@ -0,0 +1,25 @@
Implemented, reviewed, merged, and validated.
Summary:
- Updated worktree workflow rules so child worktrees may include tracked `.yoi` project records instead of excluding all `.yoi/**`.
- Sparse-checkout guidance now excludes generated/personal/local/runtime state such as `.yoi/memory`, root and recursive `_logs`, logs, locks, local/runtime/pods/sessions/sockets/tmp/cache/secrets, local overrides, and secret-like files.
- Updated validation guidance to check excluded generated/local paths rather than requiring `.yoi` absence.
- Updated child-worktree Ticket edit policy: branch-local artifacts/dossiers may live in child worktrees; active orchestration progress and final review/approval/close remain main-workspace responsibilities.
- Updated multi-agent workflow and generated role/queue guidance to match the new boundary.
- Did not change memory root detection; `memory-root-uses-yoi-memory-marker` remains the follow-up for that.
Implementation:
- Child commits: `ee85b51 workflow: narrow yoi worktree sparse exclusions`, `6f33275 fixup! workflow: narrow yoi worktree sparse exclusions`
- Merge commit: `merge: narrow yoi worktree sparse exclusions`
Review:
- External reviewer initially requested recursive `.yoi/**/_logs/**` sparse exclusions.
- Fixup added recursive `_logs` exclusions.
- Reviewer approved after fixup.
Validation after merge:
- `cargo test -p client ticket_role --lib`
- `cargo test -p tui ticket_queue_notification --lib`
- `target/debug/yoi ticket doctor`
- `cargo fmt --check`
- `git diff --check`

View File

@ -0,0 +1,226 @@
<!-- event: create author: LocalTicketBackend at: 2026-06-07T06:29:02Z -->
## Created
Created by LocalTicketBackend create.
---
<!-- event: plan author: hare at: 2026-06-07T07:14:24Z -->
## Plan
## Preflight / implementation intent
Classification: implementation-ready and should run before `memory-root-uses-yoi-memory-marker`.
Intent:
- Stop treating child worktrees as code-only surfaces that exclude all `.yoi/**`.
- Keep Yoi project records visible in child worktrees where useful: Tickets, workflows, curated Knowledge, and project config.
- Continue excluding generated/personal/local runtime state, especially `.yoi/memory`.
Updated decision from discussion:
- `.yoi` remains the project records/workspace marker.
- `.yoi/memory` is the repo-local memory marker and should not be copied into child worktrees.
- The worktree workflow should stop saying child worktrees must not contain `.yoi`; it should instead define which `.yoi` subpaths are included/excluded and how Ticket edits are split between branch-local artifacts and main-workspace final records.
Implementation direction:
- Update `.yoi/workflow/worktree-workflow.md`:
- replace `!/.yoi/**` sparse rules with narrower exclusions for `.yoi/memory`, logs, locks, local/runtime/secret-like paths;
- update validation from `test ! -e .../.yoi` to checks that memory/local paths are absent and project records are allowed;
- update child-worktree prohibitions from “no `.yoi`” to “no generated memory/local/runtime files”;
- define Ticket edit policy: branch-local artifacts/dossiers may live in child worktree; active orchestration progress and final review/close remain main-workspace responsibilities unless explicitly designed otherwise.
- Update `.yoi/workflow/multi-agent-workflow.md` where it still says `.yoi` is excluded or that Ticket/workflow/docs records are always main-workspace-only.
- Update generated role guidance/tests if code still says child worktrees exclude `.yoi` broadly.
- Keep the change focused on worktree sparse/workflow policy; memory root detection changes belong to `memory-root-uses-yoi-memory-marker`.
Validation:
- Run focused tests for any crate touched.
- Run `target/debug/yoi ticket doctor`, `cargo fmt --check`, and `git diff --check`.
- If only workflow/docs change, no broad cargo tests are required beyond relevant prompt/guidance tests if touched.
---
<!-- event: intake_summary author: INSOMNIA at: 2026-06-07T07:14:31Z -->
## Intake summary
Implementation-ready: keep `.yoi` as the project records marker, stop excluding all `.yoi/**` from child worktrees, include tracked project records, exclude `.yoi/memory`/logs/local/runtime files, and update workflow/role guidance plus Ticket edit policy. Memory root detection remains a separate follow-up ticket.
---
<!-- event: state_changed author: INSOMNIA at: 2026-06-07T07:14:31Z from: intake to: ready reason: intake_ready field: workflow_state -->
## State changed
Ticket intake complete; workflow_state intake -> ready.
---
<!-- event: implementation_report author: hare at: 2026-06-07T07:15:31Z -->
## Implementation report
## Delegation
Implementation delegated to child Pod `narrow-yoi-worktree-coder-20260607` in worktree `.worktree/narrow-yoi-worktree-sparse-exclusions` on branch `work/narrow-yoi-worktree-sparse-exclusions`.
This worktree intentionally uses the proposed narrower sparse rules:
- includes `.yoi` project records;
- excludes `.yoi/memory`, memory logs, ticket backend lock, and local/runtime paths.
Scope:
- Update `worktree-workflow` and `multi-agent-workflow` away from `.yoi` whole-directory exclusion.
- Define project-record vs generated-memory/local-runtime boundaries.
- Update role guidance/tests if they still say child worktrees exclude all `.yoi`.
- Do not implement memory-root detection; `memory-root-uses-yoi-memory-marker` follows after this.
The child should commit implementation work in the child worktree and report diff/tests. Merge, review, Ticket closure, and cleanup remain with the parent/human workflow.
---
<!-- event: implementation_report author: hare at: 2026-06-07T07:28:48Z -->
## Implementation report
## Implementation report
Coder Pod `narrow-yoi-worktree-coder-20260607` completed implementation in `.worktree/narrow-yoi-worktree-sparse-exclusions`.
Commit:
- `ee85b51 workflow: narrow yoi worktree sparse exclusions`
Reported summary:
- Updated `.yoi/workflow/worktree-workflow.md` so child worktrees may include tracked `.yoi` project records.
- Replaced broad `.yoi/**` sparse exclusion guidance with narrower exclusions for `.yoi/memory`, logs, locks, local/runtime/cache/tmp/session/socket/pod state, and secret-like paths.
- Updated validation guidance to allow `.yoi` while checking `.yoi/memory` and local/runtime/log/lock/secret-like paths are absent.
- Added explicit Ticket edit policy: branch-local artifacts/dossiers may live in child worktrees; active orchestration progress and final review/approval/close stay main-workspace responsibilities.
- Updated `.yoi/workflow/multi-agent-workflow.md` to stop describing `.yoi` as broadly excluded and to clarify child-worktree project-record boundaries.
- Updated generated role guidance/tests in `crates/client/src/ticket_role.rs` and `crates/tui/src/multi_pod.rs`.
- Did not implement memory root detection and did not recreate `.yoi/memory`.
Reported validation:
- `cargo test -p client ticket_role --lib`
- `cargo test -p tui ticket_queue_notification --lib`
- `target/debug/yoi ticket doctor`
- `cargo fmt --check`
- `git diff --check`
- `nix build .#yoi`
External review will be delegated before merge.
---
<!-- event: review author: narrow-yoi-worktree-reviewer-20260607 at: 2026-06-07T07:34:37Z status: request_changes -->
## Review: request changes
Request changes.
Blocker:
- `worktree-workflow` sparse-checkout rules do not exclude recursive `.yoi/**/_logs/**`, even though the Ticket explicitly requires generated log paths under `.yoi/**/_logs/**` to remain out of child worktrees.
Evidence:
- `.yoi/workflow/worktree-workflow.md` only excludes root log dirs:
- `!/.yoi/logs/`
- `!/.yoi/logs/**`
- `!/.yoi/_logs/`
- `!/.yoi/_logs/**`
- There is no committed recursive `_logs` sparse rule.
- The validation block checks for any `*/_logs` directory, so a tracked nested `.yoi/<project-record>/_logs/...` path would be allowed by sparse-checkout and then fail validation rather than being excluded by the rules.
Suggested minimal fix:
- Add recursive sparse exclusions such as:
- `!/.yoi/**/_logs/`
- `!/.yoi/**/_logs/**`
- Consider whether the same recursive treatment is intended for `logs`, `locks`, `local`, `runtime`, `pods`, `sessions`, `sockets`, `tmp`, `cache`, and `secrets`; the required blocker is strongest for `_logs` because the Ticket names `.yoi/**/_logs/**` explicitly.
Other checks passed:
- Workflow docs no longer require whole-directory `.yoi` exclusion.
- Project records are allowed in child worktrees.
- Ticket edit policy is clear enough and keeps final review/approval/close as main-workspace responsibilities.
- Generated role guidance/tests align with the new boundary.
- Memory root detection was not changed.
Reviewer validation:
- `cargo test -p client ticket_role --lib`
- `cargo test -p tui ticket_queue_notification --lib`
- `target/debug/yoi ticket doctor`
- `cargo fmt --check`
- `git diff --check develop...HEAD`
- `nix build .#yoi`
- `git merge-tree --write-tree develop HEAD`
Merge readiness: not ready until recursive `_logs` sparse exclusion is fixed.
---
<!-- event: review author: narrow-yoi-worktree-reviewer-20260607 at: 2026-06-07T07:37:53Z status: approve -->
## Review: approve
Approved after fixup commit `6f33275`.
Evidence:
- Previous blocker is fixed in `.yoi/workflow/worktree-workflow.md`: sparse rules now include both `!/.yoi/**/_logs/` and `!/.yoi/**/_logs/**`.
- Nearby guidance explicitly says generated log trees exclude root `_logs` and recursive `.yoi/**/_logs/**`.
- No broad `.yoi/**` exclusion was reintroduced.
- `.yoi` project records remain allowed in child worktrees.
- Generated/local state remains excluded: `.yoi/memory`, logs, locks, local/runtime/pods/sessions/sockets/tmp/cache/secrets, local overrides, and secret-like files remain listed in sparse rules.
- Ticket edit policy remains intact: active orchestration progress and final review/approval/close remain main-workspace responsibilities.
- Memory root detection was not changed; diff remains limited to workflow/guidance files.
Reviewer validation:
- `cargo test -p client ticket_role --lib`
- `cargo test -p tui ticket_queue_notification --lib`
- `target/debug/yoi ticket doctor`
- `cargo fmt --check`
- `git diff --check develop...HEAD`
- `nix build .#yoi`
- `git merge-tree --write-tree develop HEAD`
Merge readiness: approved; worktree clean at `6f33275`.
---
<!-- event: state_changed author: hare at: 2026-06-07T07:38:29Z from: ready to: done reason: closed field: workflow_state -->
## State changed
Ticket closed; workflow_state set to done.
---
<!-- event: close author: hare at: 2026-06-07T07:38:29Z status: closed -->
## Closed
Implemented, reviewed, merged, and validated.
Summary:
- Updated worktree workflow rules so child worktrees may include tracked `.yoi` project records instead of excluding all `.yoi/**`.
- Sparse-checkout guidance now excludes generated/personal/local/runtime state such as `.yoi/memory`, root and recursive `_logs`, logs, locks, local/runtime/pods/sessions/sockets/tmp/cache/secrets, local overrides, and secret-like files.
- Updated validation guidance to check excluded generated/local paths rather than requiring `.yoi` absence.
- Updated child-worktree Ticket edit policy: branch-local artifacts/dossiers may live in child worktrees; active orchestration progress and final review/approval/close remain main-workspace responsibilities.
- Updated multi-agent workflow and generated role/queue guidance to match the new boundary.
- Did not change memory root detection; `memory-root-uses-yoi-memory-marker` remains the follow-up for that.
Implementation:
- Child commits: `ee85b51 workflow: narrow yoi worktree sparse exclusions`, `6f33275 fixup! workflow: narrow yoi worktree sparse exclusions`
- Merge commit: `merge: narrow yoi worktree sparse exclusions`
Review:
- External reviewer initially requested recursive `.yoi/**/_logs/**` sparse exclusions.
- Fixup added recursive `_logs` exclusions.
- Reviewer approved after fixup.
Validation after merge:
- `cargo test -p client ticket_role --lib`
- `cargo test -p tui ticket_queue_notification --lib`
- `target/debug/yoi ticket doctor`
- `cargo fmt --check`
- `git diff --check`
---

View File

@ -0,0 +1,22 @@
---
id: 20260607-095142-workspace-orchestrator-spawn-diagnostic-persistence
slug: workspace-orchestrator-spawn-diagnostic-persistence
title: Keep workspace Orchestrator spawn diagnostics visible
status: closed
kind: task
priority: P2
labels: [tui, panel, orchestrator, diagnostic]
workflow_state: done
created_at: 2026-06-07T09:51:42Z
updated_at: 2026-06-07T10:16:08Z
assignee: null
legacy_ticket: null
---
## Background
Created by LocalTicketBackend.
## Acceptance criteria
- TBD

View File

@ -0,0 +1,24 @@
Implemented, reviewed, merged, and validated.
Summary:
- Added panel/app state to retain the latest actionable workspace Orchestrator lifecycle failure.
- Subsequent `Observe` refreshes that only report plain `missing` / `stopped` keep the prior actionable diagnostic visible.
- Stale diagnostics are cleared when Orchestrator becomes `live`, `spawned`, or `restored`.
- New `Unavailable` lifecycle diagnostics supersede older failures.
- Orchestrator scope policy was unchanged.
- `Observe` refresh still does not retry spawn.
- Ticket actionbar gate messages remain separate from Orchestrator lifecycle diagnostics.
Implementation:
- Child commit: `e15e9b7 fix: persist orchestrator lifecycle diagnostics`
- Merge commit: `merge: orchestrator diagnostic persistence`
Review:
- External reviewer `orchestrator-diagnostic-reviewer-20260607` approved with no blockers.
Validation after merge:
- `cargo test -p tui multi_orchestrator --lib`
- `cargo test -p tui --lib`
- `cargo fmt --check`
- `git diff --check`
- `target/debug/yoi ticket doctor`

View File

@ -0,0 +1,194 @@
<!-- event: create author: "yoi ticket" at: 2026-06-07T09:51:42Z -->
## Created
Created by LocalTicketBackend create.
---
<!-- event: plan author: hare at: 2026-06-07T09:51:42Z -->
## Plan
## Background
`yoi panel` currently attempts to ensure the workspace Orchestrator on initial panel load. If Orchestrator spawn fails, the initial `Ensure` path can surface an `unavailable` diagnostic such as a scope conflict. A later panel reload runs in `Observe` mode and may replace the header/status with plain `orchestrator missing`, making the actual failure reason disappear within a few seconds.
This makes spawn failures look like a missing-state problem and prevents the user from reading the actionable error.
Observed example while dogfooding:
```text
error: failed to create pod: requested scope `/home/hare/Projects/yoi` conflicts with pod `yoi` rule `/home/hare/Projects/yoi`
```
The scope conflict itself is expected in this situation: the current Pod is being used as the Companion and already owns workspace write scope. The UI issue is that the panel loses the prior spawn failure diagnostic on refresh.
## Goal
Keep Orchestrator lifecycle diagnostics visible across panel refreshes until superseded by a successful Orchestrator state or a newer lifecycle diagnostic.
## Requirements
- Do not change Orchestrator scope policy in this ticket.
- Preserve the distinction between:
- `Ensure` lifecycle attempts that can spawn/restore and produce actionable failure diagnostics;
- `Observe` refreshes that should not retry spawn.
- When an `Ensure` spawn/restore fails, keep the failure reason visible after subsequent `Observe` reloads that only report `missing`.
- Do not let stale diagnostics remain after the Orchestrator becomes live/restored/spawned successfully.
- Prefer a bounded recent lifecycle diagnostic stored in the panel/app state over recomputing or retrying spawn on every refresh.
- Make the diagnostic readable enough that short-lived errors are not lost to periodic refresh.
- Ensure actionbar Ticket gate messages, such as `attention_required is set`, do not obscure the Orchestrator lifecycle failure reason.
## Acceptance criteria
- Reproduce by causing Orchestrator spawn failure, then waiting through at least one panel refresh: the actionable failure remains visible.
- When Orchestrator later becomes live/restorable/spawned, the stale failure diagnostic is cleared or superseded.
- Plain missing state still displays when no spawn has been attempted and no prior failure exists.
- No refresh path starts retrying Orchestrator spawn implicitly.
- Focused TUI model/lifecycle tests cover diagnostic persistence and clearing.
- `cargo test -p tui ... --lib`, `cargo fmt --check`, `git diff --check`, and `target/debug/yoi ticket doctor` pass.
---
<!-- event: plan author: hare at: 2026-06-07T10:00:33Z -->
## Plan
## Delegation intent
Implement `workspace-orchestrator-spawn-diagnostic-persistence` in a dedicated worktree.
Worktree:
- `.worktree/workspace-orchestrator-spawn-diagnostic-persistence`
- branch `work/workspace-orchestrator-spawn-diagnostic-persistence`
Coder Pod:
- `orchestrator-diagnostic-coder-20260607`
Scope and boundaries:
- Keep Orchestrator scope policy unchanged. The observed scope conflict with the current `yoi` Pod is expected for dogfooding and should remain a visible diagnostic, not be solved by loosening scope.
- Preserve the current `Ensure` vs `Observe` lifecycle distinction. `Observe` refresh must not retry spawn.
- Persist or carry forward the most recent actionable Orchestrator lifecycle failure from an `Ensure` attempt so a later `Observe` result of plain `missing` does not erase it.
- Clear/supersede stale lifecycle failures when Orchestrator becomes live/restored/spawned or when a newer lifecycle diagnostic is produced.
- Keep Ticket actionbar gate messages separate from Orchestrator lifecycle diagnostics.
- Prefer a small panel/app-state model change plus focused tests over broad UI restructuring.
Relevant code map:
- `crates/tui/src/multi_pod.rs`: panel load/reload, `OrchestratorLifecycleMode::{Ensure, Observe}`, `orchestrator_lifecycle`, `spawn_orchestrator_pod`, snapshot assembly.
- `crates/tui/src/workspace_panel.rs`: panel model/header diagnostics rendering and actionbar behavior.
- `crates/client/src/ticket_role.rs`: Ticket role Pod launch path; errors from spawn/connect/initial Run should remain available to the TUI lifecycle layer.
Requested validation:
- Focused TUI tests covering diagnostic persistence and clearing.
- `cargo test -p tui ... --lib` for affected tests.
- `cargo fmt --check`.
- `git diff --check`.
- `target/debug/yoi ticket doctor`.
---
<!-- event: implementation_report author: hare at: 2026-06-07T10:10:37Z -->
## Implementation report
## Implementation report
Coder Pod `orchestrator-diagnostic-coder-20260607` completed implementation in `.worktree/workspace-orchestrator-spawn-diagnostic-persistence`.
Commit:
- `e15e9b7 fix: persist orchestrator lifecycle diagnostics`
Reported summary:
- Added `MultiPodApp` state to remember the latest actionable workspace Orchestrator lifecycle failure.
- Later `Observe` refreshes that only report plain `missing` / `stopped` keep the prior actionable diagnostic visible.
- Stored diagnostic is cleared when the Orchestrator becomes `live`, `spawned`, or `restored`.
- A newer `Unavailable` lifecycle diagnostic supersedes the older one.
- Actionbar behavior and Ticket gate messages were not changed.
Reported validation:
- `cargo test -p tui multi_orchestrator --lib`
- `cargo test -p tui --lib`
- `cargo fmt --check`
- `git diff --check`
- `nix build .#yoi`
- `./result/bin/yoi ticket doctor`
Caveat:
- `target/debug/yoi ticket doctor` was not available inside the child worktree; coder used the Nix-built binary after `nix build .#yoi`.
External review will be delegated before merge.
---
<!-- event: review author: hare at: 2026-06-07T10:15:40Z status: approve -->
## Review: approve
Approved by `orchestrator-diagnostic-reviewer-20260607`.
No blocking findings. The implementation matches the ticket intent and is merge-ready.
Evidence:
- `Ensure` vs `Observe` is preserved. Initial load uses `Ensure`; refresh paths use `Observe`; observe does not restore/spawn.
- Prior actionable failure is retained across plain observe `missing` / `stopped` for the same Pod name.
- Plain missing without prior failure remains plain missing.
- Stale failures are cleared when Orchestrator becomes `live`, `spawned`, or `restored`.
- New `Unavailable` diagnostics supersede older ones.
- Ticket actionbar gating remains separate; lifecycle diagnostics remain in `panel.header.diagnostics`.
- Worktree is clean and merge-tree against current `develop` reports no conflict.
Reviewer validation:
- `CARGO_TARGET_DIR=/tmp/yoi-review-target cargo test -p tui multi_orchestrator --lib`
- `CARGO_TARGET_DIR=/tmp/yoi-review-target cargo test -p tui --lib`
- `cargo fmt --check`
- `git diff --check develop...HEAD`
- `nix build .#yoi --no-link`
- `CARGO_TARGET_DIR=/tmp/yoi-review-target cargo run -p yoi -- ticket doctor`
---
<!-- event: state_changed author: hare at: 2026-06-07T10:16:08Z from: intake to: done reason: closed field: workflow_state -->
## State changed
Ticket closed; workflow_state set to done.
---
<!-- event: close author: hare at: 2026-06-07T10:16:08Z status: closed -->
## Closed
Implemented, reviewed, merged, and validated.
Summary:
- Added panel/app state to retain the latest actionable workspace Orchestrator lifecycle failure.
- Subsequent `Observe` refreshes that only report plain `missing` / `stopped` keep the prior actionable diagnostic visible.
- Stale diagnostics are cleared when Orchestrator becomes `live`, `spawned`, or `restored`.
- New `Unavailable` lifecycle diagnostics supersede older failures.
- Orchestrator scope policy was unchanged.
- `Observe` refresh still does not retry spawn.
- Ticket actionbar gate messages remain separate from Orchestrator lifecycle diagnostics.
Implementation:
- Child commit: `e15e9b7 fix: persist orchestrator lifecycle diagnostics`
- Merge commit: `merge: orchestrator diagnostic persistence`
Review:
- External reviewer `orchestrator-diagnostic-reviewer-20260607` approved with no blockers.
Validation after merge:
- `cargo test -p tui multi_orchestrator --lib`
- `cargo test -p tui --lib`
- `cargo fmt --check`
- `git diff --check`
- `target/debug/yoi ticket doctor`
---

View File

@ -6,9 +6,9 @@ status: open
kind: task kind: task
priority: P2 priority: P2
labels: [tui, panel, companion, pod] labels: [tui, panel, companion, pod]
workflow_state: intake workflow_state: ready
created_at: 2026-06-07T00:16:51Z created_at: 2026-06-07T00:16:51Z
updated_at: 2026-06-07T01:21:43Z updated_at: 2026-06-07T10:40:36Z
assignee: null assignee: null
legacy_ticket: null legacy_ticket: null
--- ---
@ -26,8 +26,11 @@ Restore or spawn a workspace-named Companion Pod when `yoi panel` opens, and rou
## Requirements ## Requirements
- Define the Companion Pod naming rule. - Define the Companion Pod naming rule.
- It should be based on the workspace name/path, consistent with existing default Pod naming where practical. - Use the workspace directory basename as the Companion Pod id/name.
- Avoid colliding with the workspace Orchestrator name. - For this repository, the Companion Pod id/name is `yoi`.
- Do not add a `-companion` suffix or introduce a separate companion-specific id for the normal workspace Companion.
- This intentionally lets `yoi panel` treat an existing default workspace Pod named `yoi` as the Companion when it is live/restorable.
- Avoid colliding with the workspace Orchestrator name, which remains distinct (for example `yoi-orchestrator`).
- On `yoi panel` open: - On `yoi panel` open:
- if Companion is live, use it; - if Companion is live, use it;
- if restorable, restore it; - if restorable, restore it;

View File

@ -16,4 +16,150 @@ Companion lifecycle should remain separate from Ticket/role Pod claim authority.
The Companion may eventually read/display this derived status, but it should not own the registry or gain mutation authority by default. The Companion may eventually read/display this derived status, but it should not own the registry or gain mutation authority by default.
---
<!-- event: plan author: hare at: 2026-06-07T10:17:33Z -->
## Plan
## Preflight / implementation intent
Decision:
- The workspace Companion Pod id/name is the workspace directory basename.
- For this repository, that id/name is `yoi`.
- Do not introduce a separate `yoi-companion` identity for the normal workspace Companion.
- `yoi panel` should treat an existing live/restorable default workspace Pod named `yoi` as the Companion.
- The workspace Orchestrator remains a distinct identity such as `yoi-orchestrator`.
Readiness:
- This ticket is implementation-ready after `workspace-orchestrator-spawn-diagnostic-persistence` landed.
- Orchestrator scope conflict diagnostics are now persistent enough to distinguish expected scope conflicts from plain missing state during panel dogfooding.
Current code map:
- `crates/tui/src/multi_pod.rs`
- owns panel load/reload, `MultiPodApp`, composer target dispatch, pending reloads, and current Orchestrator lifecycle glue.
- current Companion composer target is not backed by a real Companion lifecycle and should be connected here or through a small helper module.
- `crates/tui/src/workspace_panel.rs`
- owns panel model/header rows/composer target model and diagnostics.
- `crates/tui/src/pod_list.rs`
- Pod list/read/restore/open helpers may be useful for determining live/restorable Companion presence.
- `crates/client/src/ticket_role.rs`
- Ticket role launcher is separate; Companion is not a Ticket role and should not be implemented as one.
- `crates/client/src/runtime_command.rs` and existing Pod spawn/restore client helpers
- likely source for typed runtime command and one-shot socket interaction.
Implementation intent:
- Add a workspace Companion lifecycle alongside, but not inside, the Ticket Orchestrator lifecycle.
- On `yoi panel` open, resolve the Companion Pod name from the workspace directory basename (`yoi` here).
- If that Pod is live, use it; if restorable, restore it; if missing, spawn it with the configured/default Companion profile path as appropriate.
- Panel close must not stop it.
- Route the panel `Companion` composer target to this Companion Pod's user history.
- Preserve Ticket Intake as a separate target; Intake text must not be appended to Companion history.
- Keep no-Ticket workspaces functional: Companion management chat plus Pod inspect/open should work without `.yoi/ticket.config.toml`.
- Do not reintroduce selected-Pod direct send, `--multi`, or `:ticket`.
Risks / boundaries:
- Companion should not be treated as a Ticket role and should not claim Ticket role session authority.
- Existing live `yoi` Pod may already own workspace write scope; the panel should reuse/attach/route to it rather than trying to spawn another `yoi` and failing with a scope conflict.
- If `yoi` is live but busy, message send behavior should follow existing Pod client semantics and report bounded diagnostics rather than inventing queueing in this ticket.
- Avoid broad UI restructuring; keep lifecycle state and diagnostics explicit and testable.
- If nonblocking spawn/restore requires more work, keep the implementation compatible with `workspace-panel-nonblocking-transitions` rather than solving that entire ticket here.
Validation expected:
- Focused TUI tests for Companion naming (`yoi` from repo dir), live/restorable/missing lifecycle decisions, no-Ticket availability, and composer routing separation from Ticket Intake.
- Existing panel/Orchestrator tests still pass.
- `cargo test -p tui ... --lib` for affected tests.
- `cargo fmt --check`.
- `git diff --check`.
- `target/debug/yoi ticket doctor`.
---
<!-- event: plan author: hare at: 2026-06-07T10:18:01Z -->
## Plan
## Delegation intent
Implement `workspace-panel-companion-pod-lifecycle` in a dedicated worktree.
Worktree:
- `.worktree/workspace-panel-companion-pod-lifecycle`
- branch `work/workspace-panel-companion-pod-lifecycle`
Coder Pod:
- `companion-lifecycle-coder-20260607`
Key decision to preserve:
- Companion Pod id/name is the workspace directory basename.
- In this repository, the Companion Pod id/name is `yoi`.
- Do not use `yoi-companion` for the normal workspace Companion.
- `yoi panel` should treat an existing live/restorable default workspace Pod named `yoi` as the Companion.
- Workspace Orchestrator remains distinct, e.g. `yoi-orchestrator`.
Scope and boundaries:
- Companion is not a Ticket role; do not route this through `TicketRole` or `.yoi/ticket.config.toml` role slots.
- Preserve Ticket Intake as a separate composer target and separate history path.
- Do not reintroduce selected-Pod direct send, `--multi`, or `:ticket`.
- Panel close must not stop Companion.
- Existing live `yoi` Pod may own workspace write scope; lifecycle should reuse/route to it instead of spawning a duplicate with a conflicting scope.
- Keep diagnostics bounded and visible.
- Avoid broad UI restructuring.
Requested validation:
- Focused TUI tests for Companion naming/lifecycle decisions and composer routing separation.
- Existing panel/orchestrator tests affected by lifecycle changes.
- `cargo test -p tui ... --lib` for affected tests.
- `cargo fmt --check`.
- `git diff --check`.
- `target/debug/yoi ticket doctor`.
---
<!-- event: implementation_report author: hare at: 2026-06-07T10:40:36Z -->
## Implementation report
## Implementation report
Coder Pod `companion-lifecycle-coder-20260607` completed implementation in `.worktree/workspace-panel-companion-pod-lifecycle`.
Commit:
- `ba0c9d5 feat: wire panel companion lifecycle`
Reported summary:
- Added real workspace Companion Pod lifecycle for `yoi panel`.
- Companion Pod name is resolved from workspace basename, with no `-companion` suffix.
- Live Pods are reused, restorable Pods are restored, missing Pods are spawned through normal default/Companion profile startup.
- Panel close does not stop Companion.
- Wired `Companion` composer submit to the workspace Companion Pod via `Method::Run`.
- It no longer routes to arbitrary selected Pods.
- Empty composer behavior still preserves selected-Pod open/attach behavior.
- Busy/unavailable Companion states keep the draft and surface bounded diagnostics.
- Kept Ticket Intake separate.
- Ticket Intake target still launches Intake handoff and does not append to Companion history.
- Added focused tests for Companion naming, lifecycle decisions, no-Ticket Companion availability, composer routing, and Intake separation.
Changed files:
- `crates/tui/src/multi_pod.rs`
- `crates/tui/src/workspace_panel.rs`
Reported validation:
- `cargo test -p tui companion --lib`
- `cargo test -p tui multi_pod --lib`
- `cargo test -p tui workspace_panel --lib`
- `cargo test -p tui --lib`
- `cargo fmt --check`
- `git diff --check`
- `nix build .#yoi`
- `result/bin/yoi ticket doctor`
Caveat:
- `target/debug/yoi` was not used for `ticket doctor`; coder used the Nix-built `result/bin/yoi` after `nix build .#yoi`.
External review will be delegated before merge.
--- ---

View File

@ -1,26 +0,0 @@
<!-- event: create author: LocalTicketBackend at: 2026-06-07T03:52:31Z -->
## Created
Created by LocalTicketBackend create.
---
<!-- event: decision author: hare at: 2026-06-07T05:00:22Z -->
## Decision
## Git/Ticket review record boundary
Decision:
- Reviewer verdicts on an unmerged implementation branch are branch-scoped evidence, not final main-branch Ticket approval.
- The Orchestrator should keep branch-local review results in the merge-ready dossier/review report during the worktree-agent phase.
- Main Ticket thread may still record durable progress such as worktree plan, coder/reviewer delegation, blockers, and fix-loop status.
- The final Ticket review/approval/completion record should be written during the merge-completion phase, after confirming the reviewed branch is the branch being merged, or as part of the same controlled completion sequence.
Rationale:
- Avoid main branch Ticket history claiming approval for code that is not yet in main.
- Keep git history aligned: implementation branch review evidence leads to merge; main Ticket lifecycle records the completed merge/validation outcome.
- Builtin Orchestrator workflow should make this explicit so automation does not reproduce the ad-hoc parent-side `TicketReview` timing used during manual dogfooding.
---

View File

@ -0,0 +1,49 @@
---
id: 20260607-072708-builtin-workflow-knowledge-resources
slug: builtin-workflow-knowledge-resources
title: Builtin Workflow and Knowledge resources
status: open
kind: task
priority: P2
labels: [workflow, knowledge, resources, builtin]
workflow_state: intake
created_at: 2026-06-07T07:27:08Z
updated_at: 2026-06-07T07:27:08Z
assignee: null
legacy_ticket: null
---
## Background
Recent Orchestrator automation work embedded key workflow contracts as role-specific builtin guidance in generated prompts. This is sufficient for now, but it is not a full builtin Workflow/Knowledge resource system.
Longer term, reusable Yoi workflows and curated guidance should be available as bundled builtin Workflow/Knowledge resources that can be resolved by slug and optionally overridden/extended by project/user records.
## Goal
Add builtin Workflow/Knowledge resources as first-class bundled resources while preserving project/user override capability.
## Requirements
- Define a bundled resource location for builtin Workflows and builtin Knowledge, e.g. under `resources/`.
- Add resolver support for builtin Workflow/Knowledge slugs such as `builtin:multi-agent-workflow` or equivalent source-qualified identifiers.
- Preserve project-authored records under `.yoi/workflow` and `.yoi/knowledge` as project overrides/extensions.
- Define precedence between builtin, user, and project resources.
- Make builtin resource resolution available to role/prompt generation and workflow invocation paths where appropriate.
- Keep existing role-specific prompt guidance working; do not require a broad migration in the first pass.
- Ensure builtin resources can be referenced without copying them into every workspace.
- Document how users/projects override or extend builtin Workflow/Knowledge.
- Add tests for resource lookup, override precedence, and missing-resource diagnostics.
## Non-goals
- Rewriting all current workflow prompts immediately.
- Solving active workflow compaction persistence; that remains `preserve-active-workflows-across-compaction`.
- Removing project `.yoi/workflow` / `.yoi/knowledge` support.
## Acceptance criteria
- Builtin Workflow/Knowledge resources can be resolved by explicit source-qualified slug.
- Project/user resources can override or extend builtin resources according to documented precedence.
- Existing project workflow files continue to work.
- Tests cover builtin lookup and override behavior.

View File

@ -0,0 +1,7 @@
<!-- event: create author: LocalTicketBackend at: 2026-06-07T07:27:08Z -->
## Created
Created by LocalTicketBackend create.
---

View File

@ -0,0 +1,52 @@
---
id: 20260607-073313-pod-notification-injection-guidance
slug: pod-notification-injection-guidance
title: Improve Pod notification injection guidance
status: open
kind: task
priority: P2
labels: [pod, notification, prompt, orchestration, ux]
workflow_state: intake
created_at: 2026-06-07T07:33:13Z
updated_at: 2026-06-07T07:33:13Z
assignee: null
legacy_ticket: null
---
## Background
During long orchestration sessions, spawned Pod completion notifications can arrive as system-injected messages. Although the notification says it is not a blocking request, the assistant may still respond with conversational phrasing that appears to answer the notification or stale user context, rather than clearly reporting the notification-handling result.
This is especially noticeable in long contexts where multiple similar ticket/worktree/Pod reports are present. The issue appears more like notification framing/turn-target ambiguity than a direct task failure.
## Goal
Improve the system-injected Pod notification wording so the assistant is guided to handle notifications as background events and report concrete handling results, not respond as if the notification were a user message.
## Requirements
- Review current Pod notification injection text for spawned Pod finished/shutdown events.
- Adjust wording to more strongly distinguish notifications from user requests.
- Add guidance for the assistant's next response after handling a notification, e.g.:
- do not answer the notification as if it were a user message;
- if you choose to handle it now, read the Pod output and report the action taken;
- summarize which Pod was handled, what changed, and what remains pending;
- otherwise continue the current user task and handle the notification later.
- Keep notification text concise enough not to bloat context excessively.
- Preserve the existing policy that notifications are not blocking and should not force polling/wait loops.
- Ensure wording does not authorize extra workflow actions, merges, ticket closes, or cleanup solely because a notification arrived.
- Add/update tests or prompt snapshot tests for notification text if such tests exist.
## Non-goals
- Redesigning the Pod event protocol.
- Implementing typed hidden event channels.
- Solving all long-context degradation issues.
- Changing tool behavior or automatic notification handling policy.
## Acceptance criteria
- Pod notification injection text clearly frames notifications as background events, not user turns.
- The text gives concrete response-shaping guidance for notification-handling reports.
- Existing orchestration policy remains: verify output/evidence before treating child work complete, and do not start/merge/close solely from a notification.
- Tests/snapshots are updated where applicable.

View File

@ -0,0 +1,7 @@
<!-- event: create author: LocalTicketBackend at: 2026-06-07T07:33:13Z -->
## Created
Created by LocalTicketBackend create.
---

View File

@ -0,0 +1,48 @@
---
id: 20260607-084344-remove-fixed-investigator-ticket-role
slug: remove-fixed-investigator-ticket-role
title: Remove fixed investigator Ticket role
status: open
kind: task
priority: P2
labels: [ticket, orchestration, role, cleanup]
workflow_state: intake
created_at: 2026-06-07T08:43:44Z
updated_at: 2026-06-07T08:43:44Z
assignee: null
legacy_ticket: null
---
## Background
`investigator` was introduced by prior AI-driven Ticket orchestration design as a fixed Ticket role alongside `intake`, `orchestrator`, `coder`, and `reviewer`. It was not part of the current desired role profile set, and the former TUI `:ticket investigate` surface has already been removed.
Investigation/read-only research remains useful, but it should be modeled as a task-specific helper Pod spawned by Intake/Orchestrator/preflight when needed, not as a permanent fixed Ticket role with workspace config/profile slots.
## Goal
Remove `investigator` as a fixed Ticket role and clean up user/project-facing references while preserving the ability for Orchestrator/preflight flows to spawn read-only investigation helper Pods as ordinary task-specific Pods.
## Requirements
- Remove `TicketRole::Investigator` from the fixed role model.
- Update Ticket config parsing/scaffold/defaults so `.yoi/ticket.config.toml` only requires/advertises:
- `intake`
- `orchestrator`
- `coder`
- `reviewer`
- Treat `[roles.investigator]` in current config according to the projects desired compatibility policy; prefer a clear config error or documented migration over silently using an unsupported role.
- Update launcher/client/TUI tests and prompt generation that currently mention or branch on `Investigator`.
- Remove docs/workflow wording that presents `investigator` as a fixed role or configured role slot.
- Preserve wording/behavior that allows Orchestrator/preflight to create read-only helper Pods for investigation when explicitly useful.
- Do not reintroduce the removed TUI `:ticket investigate` command surface.
- Do not add a generic arbitrary role registry as part of this cleanup.
## Acceptance criteria
- No runtime fixed-role enum/config/scaffold path exposes `investigator` as a Ticket role.
- `yoi ticket init` no longer emits `[roles.investigator]`.
- Project docs describe investigation as an optional helper-Pod activity under Intake/Orchestrator/preflight, not a configured Ticket role.
- Existing Ticket role launch flows for intake/orchestrator/coder/reviewer still work.
- Focused tests for `ticket::config`, `client::ticket_role`, and any affected TUI role-launch/panel paths pass.
- `target/debug/yoi ticket doctor`, `cargo fmt --check`, and `git diff --check` pass.

View File

@ -0,0 +1,7 @@
<!-- event: create author: LocalTicketBackend at: 2026-06-07T08:43:44Z -->
## Created
Created by LocalTicketBackend create.
---

View File

@ -106,7 +106,7 @@ reviewer には coder の実装方針ではなく、この intent packet と dif
2. worktree 作成 2. worktree 作成
- `$user/worktree-workflow` に従い `./.worktree/<task-name>` を作る。 - `$user/worktree-workflow` に従い `./.worktree/<task-name>` を作る。
- `.yoi` を sparse checkout で除外する。 - `.yoi` 自体は除外しない。tracked project records は child worktree に存在してよく、`.yoi/memory` と local/runtime/log/lock/secret-like paths だけを sparse checkout で除外する。
3. coder Pod spawn 3. coder Pod spawn
- read scope: main workspace 全体。 - read scope: main workspace 全体。
@ -117,6 +117,8 @@ reviewer には coder の実装方針ではなく、この intent packet と dif
- intent packet - intent packet
- Bash は必ず child worktree に `cd` すること - Bash は必ず child worktree に `cd` すること
- main workspace の `TODO.md` / `tickets/` / `docs/report/` / `.yoi` は編集しないこと - main workspace の `TODO.md` / `tickets/` / `docs/report/` / `.yoi` は編集しないこと
- child worktree 内の tracked `.yoi` project records は実装対象に必要な branch-local artifacts/dossiers として編集してよいが、`.yoi/memory` や local/runtime/secret-like files は作らないこと
- active orchestration progress と最終 review/approval/close は main workspace の責任として残すこと
- 範囲外事項 - 範囲外事項
- 実行すべき build / test / format - 実行すべき build / test / format
- 完了報告項目 - 完了報告項目
@ -155,6 +157,8 @@ reviewer には coder の実装方針ではなく、この intent packet と dif
- child worktree 内でのみ実装する。 - child worktree 内でのみ実装する。
- main workspace の管理ファイルを書かない。 - main workspace の管理ファイルを書かない。
- child worktree 内の tracked `.yoi` project records は ticket 要件に必要な branch-local artifact/dossier として扱ってよい。
- `.yoi/memory`、local/runtime state、logs、locks、secret-like files を child worktree に作らない。
- intent / requirements / invariants / non-goals を読んでから実装する。 - intent / requirements / invariants / non-goals を読んでから実装する。
- 指定された build / test / format を実行する。 - 指定された build / test / format を実行する。
- ticket 要件外の設計変更、依存関係追加、scope / permission / history persistence / prompt context 加工原則に触れる変更が必要なら止めて orchestrator に報告する。 - ticket 要件外の設計変更、依存関係追加、scope / permission / history persistence / prompt context 加工原則に触れる変更が必要なら止めて orchestrator に報告する。
@ -191,7 +195,7 @@ coder Pod には child worktree 内での commit を許可してよい。
- commit は ticket 内で意味のある粒度にする。 - commit は ticket 内で意味のある粒度にする。
- 例: `feat: ...`、`fix: ...`、`test: ...`、`docs: ...` - 例: `feat: ...`、`fix: ...`、`test: ...`、`docs: ...`
- coder Pod は merge / push / branch deletion / worktree remove をしない。 - coder Pod は merge / push / branch deletion / worktree remove をしない。
- coder Pod は `TODO.md` / ticket の完了処理 commit をしない。 - coder Pod は main workspace の Ticket 完了処理 commit、最終 review/approval/close をしない。child worktree 側には branch-local dossier や実装証跡を残してよい。
- orchestrator は review 時に commit 粒度も確認する。 - orchestrator は review 時に commit 粒度も確認する。
- 必要な修正は、原則追加 commit として積む。履歴改変や squash は人間の明示指示がある時だけ行う。 - 必要な修正は、原則追加 commit として積む。履歴改変や squash は人間の明示指示がある時だけ行う。

View File

@ -8,7 +8,7 @@ requires: []
yoi プロジェクトで実装差分を main workspace から分離するため、`./.worktree/<task-name>` に child git worktree を作る。これは **worktree の扱い方だけ** を定める Workflow であり、ticket 選定、coder / reviewer sibling の起動、外部レビュー、merge の運用は `$user/multi-agent-workflow` 側で扱う。 yoi プロジェクトで実装差分を main workspace から分離するため、`./.worktree/<task-name>` に child git worktree を作る。これは **worktree の扱い方だけ** を定める Workflow であり、ticket 選定、coder / reviewer sibling の起動、外部レビュー、merge の運用は `$user/multi-agent-workflow` 側で扱う。
yoi では Pod の write scope が排他的に委譲されるため、child worktree に `.yoi` を置かない。main workspace は orchestration / ticket / docs / memory / workflow 管理の場所として残し、child worktree はコード差分専用の作業面として扱う yoi では Pod の write scope が排他的に委譲されるため、child Pod の write scope は child worktree に限定する。child worktree は Yoi project records marker として tracked `.yoi` records を含んでよいが、generated/personal memory root `.yoi/memory`、local override、runtime state、logs、locks、secret-like files は出さない。main workspace は active orchestration progress と最終 review/approval/close の authority として残す
## 適用範囲 ## 適用範囲
@ -17,7 +17,8 @@ yoi では Pod の write scope が排他的に委譲されるため、child work
- coder Pod にこの Workflow を渡して worktree を作らせない。 - coder Pod にこの Workflow を渡して worktree を作らせない。
- coder Pod は、orchestrator が作成済みの child worktree を受け取り、その中で実装・build・test・報告を行う。 - coder Pod は、orchestrator が作成済みの child worktree を受け取り、その中で実装・build・test・報告を行う。
- reviewer Pod は、coder Pod の子ではなく orchestrator 配下の sibling として、原則 read-only で main workspace と child worktree を読む。 - reviewer Pod は、coder Pod の子ではなく orchestrator 配下の sibling として、原則 read-only で main workspace と child worktree を読む。
- ticket 作成、TODO 更新、review artifact、docs/report は main workspace 側で扱う。 - ticket 作成、active orchestration progress、最終 review/approval/close は main workspace 側で扱う。
- branch-local artifacts / dossiers / docs/report / tracked `.yoi` project records は、実装対象に必要なら child worktree 内で扱ってよい。
## 原則 ## 原則
@ -25,8 +26,9 @@ yoi では Pod の write scope が排他的に委譲されるため、child work
- 複数 ticket を下位 orchestrator に任せる場合も、実装差分は ticket / bounded task ごとに worktree を分ける。 - 複数 ticket を下位 orchestrator に任せる場合も、実装差分は ticket / bounded task ごとに worktree を分ける。
- worktree path は `./.worktree/<task-name>` - worktree path は `./.worktree/<task-name>`
- branch 名は原則 `<task-name>` と同じ kebab-case。 - branch 名は原則 `<task-name>` と同じ kebab-case。
- child worktree には `.yoi` を出さない。 - child worktree には `.yoi` project records を出してよい。
- child worktree は実装差分用。`TODO.md` / `tickets/` / `docs/report/` / workflow / memory は原則 main workspace 側で扱う。 - child worktree では `.yoi/memory`、local/runtime/log/lock/secret-like paths を sparse checkout で除外する。
- active orchestration progress と最終 review/approval/close は main workspace 側で扱う。branch-local artifacts/dossiers は child worktree 内に置いてよい。
- push はしない。 - push はしない。
## 事前確認 ## 事前確認
@ -52,17 +54,64 @@ git worktree add .worktree/<task-name> -b <task-name>
git -C .worktree/<task-name> sparse-checkout init --no-cone git -C .worktree/<task-name> sparse-checkout init --no-cone
git -C .worktree/<task-name> sparse-checkout set --no-cone \ git -C .worktree/<task-name> sparse-checkout set --no-cone \
'/*' \ '/*' \
'!/.yoi/' \ '!/.yoi/memory/' \
'!/.yoi/**' '!/.yoi/memory/**' \
'!/.yoi/logs/' \
'!/.yoi/logs/**' \
'!/.yoi/_logs/' \
'!/.yoi/_logs/**' \
'!/.yoi/**/_logs/' \
'!/.yoi/**/_logs/**' \
'!/.yoi/locks/' \
'!/.yoi/locks/**' \
'!/.yoi/**/*.log' \
'!/.yoi/**/*.lock' \
'!/.yoi/**/.lock' \
'!/.yoi/override.local.toml' \
'!/.yoi/**/*.local' \
'!/.yoi/**/*.local.*' \
'!/.yoi/local/' \
'!/.yoi/local/**' \
'!/.yoi/runtime/' \
'!/.yoi/runtime/**' \
'!/.yoi/pods/' \
'!/.yoi/pods/**' \
'!/.yoi/sessions/' \
'!/.yoi/sessions/**' \
'!/.yoi/sockets/' \
'!/.yoi/sockets/**' \
'!/.yoi/tmp/' \
'!/.yoi/tmp/**' \
'!/.yoi/cache/' \
'!/.yoi/cache/**' \
'!/.yoi/secrets/' \
'!/.yoi/secrets/**' \
'!/.yoi/**/*.secret' \
'!/.yoi/**/*.secret.*'
``` ```
この sparse-checkout は `.yoi` 自体を除外しない。`.yoi/memory` は generated/personal memory marker として child worktree から外し、generated log trees は root `_logs` だけでなく recursive `.yoi/**/_logs/**` も外す。memory root detection の実装変更はこの Workflow では扱わない。
確認する。 確認する。
```bash ```bash
git -C .worktree/<task-name> status --short --branch git -C .worktree/<task-name> status --short --branch
test ! -e .worktree/<task-name>/.yoi test ! -e .worktree/<task-name>/.yoi/memory
if test -d .worktree/<task-name>/.yoi; then
test ! -e .worktree/<task-name>/.yoi/override.local.toml
test -z "$(find .worktree/<task-name>/.yoi \
\( -path '*/_logs' -o -path '*/logs' -o -path '*/locks' \
-o -path '*/local' -o -path '*/runtime' -o -path '*/pods' \
-o -path '*/sessions' -o -path '*/sockets' -o -path '*/tmp' \
-o -path '*/cache' -o -path '*/secrets' -o -name '*.log' \
-o -name '*.lock' -o -name '.lock' -o -name '*.local' \
-o -name '*.local.*' -o -name '*.secret' -o -name '*.secret.*' \) \
-print -quit)"
fi
``` ```
この確認は `.yoi` project records の存在を失敗扱いしない。`.yoi/memory` と local/runtime/log/lock/secret-like paths が出ていないことを確認する。
失敗した場合は、worktree / branch / lock の状態を確認し、勝手に cleanup せず人間へ報告する。 失敗した場合は、worktree / branch / lock の状態を確認し、勝手に cleanup せず人間へ報告する。
## Pod へ渡す scope ## Pod へ渡す scope
@ -89,8 +138,11 @@ reviewer は原則 write scope を持たない。review artifact を書かせる
## child worktree 内の禁止事項 ## child worktree 内の禁止事項
- `.yoi` を作らない / コピーしない。 - `.yoi/memory` を作らない / コピーしない / 復元しない。
- main workspace の `TODO.md` / `tickets/` / `docs/report/` を編集しない。 - local overrides、runtime sockets/state、Pod session mirrors、cache/tmp、logs、locks、secret-like files を作らない / コピーしない / commit しない。
- main workspace の `TODO.md` / `tickets/` / `docs/report/` / `.yoi` を編集しない。
- active orchestration progress と最終 review/approval/close を child worktree 内だけで完結させない。
- 実装対象に必要な tracked `.yoi` project records、branch-local artifacts / dossiers / docs/report は child worktree 内で扱ってよい。
- merge / push / branch deletion / worktree remove をしない。 - merge / push / branch deletion / worktree remove をしない。
- scope / permission / history persistence / prompt context 加工原則に関わる設計変更を無断で行わない。 - scope / permission / history persistence / prompt context 加工原則に関わる設計変更を無断で行わない。

View File

@ -548,17 +548,27 @@ fn append_orchestrator_agent_routing_guidance(out: &mut String) {
out.push_str("\nOrchestrator worktree + agent routing guidance:\n"); out.push_str("\nOrchestrator worktree + agent routing guidance:\n");
out.push_str("- Treat `ticket-orchestrator-routing` as the routing gate. Read the Ticket and workspace state first; `ready -> queued` authorizes routing, not implementation side effects.\n"); out.push_str("- Treat `ticket-orchestrator-routing` as the routing gate. Read the Ticket and workspace state first; `ready -> queued` authorizes routing, not implementation side effects.\n");
out.push_str("- Create worktrees or spawn coder/reviewer Pods only after `workflow_state = inprogress` is already recorded and accepted. If the Ticket is still queued and unblocked, record `queued -> inprogress` before any worktree/SpawnPod side effect.\n"); out.push_str("- Create worktrees or spawn coder/reviewer Pods only after `workflow_state = inprogress` is already recorded and accepted. If the Ticket is still queued and unblocked, record `queued -> inprogress` before any worktree/SpawnPod side effect.\n");
out.push_str("- Use `worktree-workflow` for the mechanical worktree plan: create `.worktree/<task-name>`, exclude `.yoi` from the child worktree, and keep the main workspace as the authority for Ticket, workflow, docs, and memory records.\n"); out.push_str("- Use `worktree-workflow` for the mechanical worktree plan: create `.worktree/<task-name>`, keep tracked `.yoi` project records visible in the child worktree, exclude `.yoi/memory` plus local/runtime/log/lock/secret-like `.yoi` paths, and keep active orchestration progress plus final review/approval/close in the main workspace unless explicitly designed otherwise.\n");
out.push_str("- Use `multi-agent-workflow` for the sibling loop: coder and reviewer are siblings under this Orchestrator; coder gets narrow write scope to the child worktree; reviewer is read-only by default.\n"); out.push_str("- Use `multi-agent-workflow` for the sibling loop: coder and reviewer are siblings under this Orchestrator; coder gets narrow write scope to the child worktree; reviewer is read-only by default.\n");
out.push_str("- Give the coder an intent packet, child worktree/branch, validation commands, and report expectations; require Bash commands to `cd` into the child worktree and prohibit editing main-workspace `.yoi`/Ticket/workflow/docs records.\n"); out.push_str("- Give the coder an intent packet, child worktree/branch, validation commands, and report expectations; require Bash commands to `cd` into the child worktree, prohibit editing main-workspace `.yoi`/Ticket/workflow/docs records, and prohibit creating generated memory/local/runtime/secret-like files in the child worktree.\n");
out.push_str("- Give the reviewer the Ticket intent, diff/commits, validation evidence, and blocker/non-blocker criteria; keep branch-local reviewer verdicts in the review report or merge-ready dossier rather than recording them as final main-branch Ticket approval.\n"); out.push_str("- Give the reviewer the Ticket intent, diff/commits, validation evidence, and blocker/non-blocker criteria; keep branch-local reviewer verdicts in the review report or merge-ready dossier rather than recording them as final main-branch Ticket approval.\n");
out.push_str("- Ticket thread progress may record worktree plan, coder delegated/completed/blocked, reviewer delegated, blocker/fix-loop summaries, and merge-ready dossier pointer; do not merge, close, or record final main approval in this phase.\n"); out.push_str("- Ticket thread progress may record worktree plan, coder delegated/completed/blocked, reviewer delegated, blocker/fix-loop summaries, and merge-ready dossier pointer; do not merge, close, or record final main approval in this routing/branch-review phase.\n");
out.push_str("- Stop at a merge-ready dossier for `orchestrator-merge-completion` containing branch, commits, conceptual implementation summary, validation evidence, coder/reviewer evidence, blocker loop outcome, residual risk, and parent decision needs.\n"); out.push_str("- Stop at a merge-ready dossier for `orchestrator-merge-completion` containing Ticket id/slug, branch/worktree, commits, intent/invariant check, implementation summary, coder/reviewer Pods, blockers fixed or rejected findings with reasons, validation performed, residual risks, dirty state, and parent/human decision needs if any.\n");
out.push_str("\nOrchestrator merge-completion guidance:\n");
out.push_str("- Enter merge-completion only for an `inprogress` Ticket with a merge-ready dossier. Conservative or missing authorization mode stops at the dossier; do not infer merge authority from public/default configuration.\n");
out.push_str("- Required dossier fields before merge: Ticket id/slug; branch/worktree; commits; intent/invariant check; implementation summary; coder/reviewer Pods; blockers fixed or rejected findings with reasons; validation performed; residual risks; dirty state; parent/human decision needs if any.\n");
out.push_str("- Before merging, verify the dossier branch/worktree/commits match the branch to merge, independent reviewer approval exists in the dossier or an explicit human override decision is recorded, the main workspace is safe, and unrelated dirty changes are understood.\n");
out.push_str("- Merge only when dogfooding/workspace policy grants merge authority. If authority is unavailable, record/return the dossier and stop without merge, close, final main Ticket approval, or cleanup.\n");
out.push_str("- Preserve the boundary: branch-local reviewer verdicts are dossier evidence; final main-branch Ticket approval or close happens only during authorized merge-completion after merge and validation evidence.\n");
out.push_str("- Authorized sequence: stop/reclaim coder and reviewer Pods where appropriate; merge with `git merge --no-ff <branch>` or the project-agreed method; run post-merge validation appropriate to the change; record review, merge, and validation outcomes in the Ticket thread during merge-completion; transition `inprogress -> done` or close according to typed Ticket workflow rules; then remove the merged child worktree and delete the merged branch unless explicitly kept.\n");
out.push_str("- Post-merge validation baseline: run focused tests from the Ticket/dossier, `cargo fmt --check`, `git diff --check`, and `target/debug/yoi ticket doctor` where applicable; add broader validation such as `cargo check --workspace --all-targets`, `nix build .#yoi`, or equivalent when risk, API surface, packaging, runtime resources, prompts, or touched files warrant it.\n");
} }
fn append_coder_agent_routing_guidance(out: &mut String) { fn append_coder_agent_routing_guidance(out: &mut String) {
out.push_str("\nCoder worktree routing guidance:\n"); out.push_str("\nCoder worktree routing guidance:\n");
out.push_str("- Implement only in the provided child worktree/branch. Use `cd <worktree>` before Bash commands and do not edit main-workspace `.yoi`, Ticket, workflow, docs, or memory records.\n"); out.push_str("- Implement only in the provided child worktree/branch. Use `cd <worktree>` before Bash commands and do not edit main-workspace `.yoi`, Ticket, workflow, docs, or memory records; child-worktree `.yoi` project records may be visible when they are part of the branch.\n");
out.push_str("- Do not create `.yoi/memory`, local/runtime state, logs, locks, caches, sockets, or secret-like files in the child worktree.\n");
out.push_str("- Treat the intent packet, invariants, non-goals, validation expectations, and report expectations as the contract. Escalate to Orchestrator rather than expanding scope when design, permission, history, prompt-context, dependency, or Ticket-boundary questions appear.\n"); out.push_str("- Treat the intent packet, invariants, non-goals, validation expectations, and report expectations as the contract. Escalate to Orchestrator rather than expanding scope when design, permission, history, prompt-context, dependency, or Ticket-boundary questions appear.\n");
out.push_str("- Report worktree path, branch, commits/status, changed files, implementation summary, validation run, unresolved notes, and whether the branch is ready for external review. Do not merge, push, close Tickets, or delete worktrees.\n"); out.push_str("- Report worktree path, branch, commits/status, changed files, implementation summary, validation run, unresolved notes, and whether the branch is ready for external review. Do not merge, push, close Tickets, or delete worktrees.\n");
} }
@ -1068,6 +1078,12 @@ workflow = "ticket-review-workflow"
assert!(orchestrator_text.contains("cargo check --workspace --all-targets")); assert!(orchestrator_text.contains("cargo check --workspace --all-targets"));
assert!(orchestrator_text.contains("workflow_state = inprogress")); assert!(orchestrator_text.contains("workflow_state = inprogress"));
assert!(orchestrator_text.contains("worktree-workflow")); assert!(orchestrator_text.contains("worktree-workflow"));
assert!(orchestrator_text.contains("keep tracked `.yoi` project records visible"));
assert!(orchestrator_text.contains("exclude `.yoi/memory`"));
assert!(
orchestrator_text
.contains("prohibit creating generated memory/local/runtime/secret-like files")
);
assert!(orchestrator_text.contains("multi-agent-workflow")); assert!(orchestrator_text.contains("multi-agent-workflow"));
assert!(orchestrator_text.contains("coder and reviewer are siblings")); assert!(orchestrator_text.contains("coder and reviewer are siblings"));
assert!(orchestrator_text.contains("branch-local reviewer verdicts")); assert!(orchestrator_text.contains("branch-local reviewer verdicts"));
@ -1088,6 +1104,8 @@ workflow = "ticket-review-workflow"
assert!(coder_text.contains("cargo test -p client ticket_role")); assert!(coder_text.contains("cargo test -p client ticket_role"));
assert!(coder_text.contains("provided child worktree/branch")); assert!(coder_text.contains("provided child worktree/branch"));
assert!(coder_text.contains("do not edit main-workspace `.yoi`")); assert!(coder_text.contains("do not edit main-workspace `.yoi`"));
assert!(coder_text.contains("child-worktree `.yoi` project records may be visible"));
assert!(coder_text.contains("Do not create `.yoi/memory`"));
assert!(coder_text.contains("Do not merge, push, close Tickets, or delete worktrees")); assert!(coder_text.contains("Do not merge, push, close Tickets, or delete worktrees"));
let mut reviewer = TicketRoleLaunchContext::new(temp.path(), TicketRole::Reviewer); let mut reviewer = TicketRoleLaunchContext::new(temp.path(), TicketRole::Reviewer);
@ -1106,6 +1124,66 @@ workflow = "ticket-review-workflow"
assert!(reviewer_text.contains("Do not record final main-branch Ticket approval")); assert!(reviewer_text.contains("Do not record final main-branch Ticket approval"));
} }
#[test]
fn orchestrator_prompt_covers_merge_completion_authority_and_dossier_boundaries() {
let temp = TempDir::new().unwrap();
write_builtin_role_config(temp.path(), &[TicketRole::Orchestrator]);
let mut orchestrator = TicketRoleLaunchContext::new(temp.path(), TicketRole::Orchestrator);
orchestrator.ticket = Some(TicketRef::slug("orchestrator-merge-completion"));
orchestrator.intent_packet =
Some("Complete an already-reviewed merge-ready Ticket.".into());
orchestrator.validation = vec!["cargo test -p client ticket_role --lib".into()];
let plan = plan_ticket_role_launch(orchestrator).unwrap();
let text = text_segment(&plan);
assert!(text.contains("Orchestrator merge-completion guidance"));
assert!(text.contains("`inprogress` Ticket with a merge-ready dossier"));
assert!(text.contains("Conservative or missing authorization mode stops at the dossier"));
assert!(text.contains("do not infer merge authority"));
assert!(text.contains("dossier branch/worktree/commits match the branch to merge"));
assert!(text.contains("independent reviewer approval exists in the dossier"));
assert!(text.contains("explicit human override decision is recorded"));
assert!(text.contains("the main workspace is safe"));
assert!(text.contains("unrelated dirty changes are understood"));
assert!(text.contains("dogfooding/workspace policy grants merge authority"));
assert!(text.contains("branch-local reviewer verdicts are dossier evidence"));
assert!(text.contains("final main-branch Ticket approval or close happens only during authorized merge-completion"));
assert!(text.contains("stop/reclaim coder and reviewer Pods"));
assert!(text.contains("git merge --no-ff <branch>"));
assert!(text.contains("run post-merge validation appropriate to the change"));
assert!(
text.contains("record review, merge, and validation outcomes in the Ticket thread")
);
assert!(text.contains(
"transition `inprogress -> done` or close according to typed Ticket workflow rules"
));
assert!(text.contains(
"remove the merged child worktree and delete the merged branch unless explicitly kept"
));
assert!(text.contains("Required dossier fields before merge"));
assert!(text.contains("Ticket id/slug"));
assert!(text.contains("branch/worktree"));
assert!(text.contains("commits"));
assert!(text.contains("intent/invariant check"));
assert!(text.contains("implementation summary"));
assert!(text.contains("coder/reviewer Pods"));
assert!(text.contains("blockers fixed or rejected findings with reasons"));
assert!(text.contains("validation performed"));
assert!(text.contains("residual risks"));
assert!(text.contains("dirty state"));
assert!(text.contains("parent/human decision needs if any"));
assert!(text.contains("Post-merge validation baseline"));
assert!(text.contains("focused tests from the Ticket/dossier"));
assert!(text.contains("cargo fmt --check"));
assert!(text.contains("git diff --check"));
assert!(text.contains("target/debug/yoi ticket doctor"));
assert!(text.contains("where applicable"));
assert!(text.contains("cargo check --workspace --all-targets"));
assert!(text.contains("nix build .#yoi"));
assert!(text.contains("when risk, API surface, packaging, runtime resources, prompts, or touched files warrant it"));
}
#[test] #[test]
fn caller_provided_pod_name_is_used_exactly() { fn caller_provided_pod_name_is_used_exactly() {
let temp = TempDir::new().unwrap(); let temp = TempDir::new().unwrap();

View File

@ -165,17 +165,19 @@ pub struct WebFetchConfig {
} }
/// Memory subsystem configuration. Presence in the manifest enables /// Memory subsystem configuration. Presence in the manifest enables
/// memory; the workspace root defaults to the Pod's pwd unless an /// memory; `workspace_root` pins the memory workspace explicitly. When it
/// explicit override is given. /// is absent, memory resolution searches upward from the Pod's pwd for a
/// `.yoi/memory` marker rather than treating `.yoi` project records alone
/// as a memory root.
/// ///
/// All fields are `Option`; defaults are applied at the consumer /// All fields are `Option`; defaults are applied at the consumer
/// (`.unwrap_or(defaults::...)`). This keeps cascade `merge` simple /// (`.unwrap_or(defaults::...)`). This keeps cascade `merge` simple
/// (`upper.x.or(self.x)`) without a separate partial/resolved split. /// (`upper.x.or(self.x)`) without a separate partial/resolved split.
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct MemoryConfig { pub struct MemoryConfig {
/// Override for the workspace root. When `None`, the Pod's pwd /// Override for the memory workspace root. When `None`, consumers resolve
/// (resolved at construction time) is used. When set, must be an /// the root from their default path and ancestor `.yoi/memory` markers.
/// absolute path. /// When set, must be an absolute path.
#[serde(default)] #[serde(default)]
pub workspace_root: Option<PathBuf>, pub workspace_root: Option<PathBuf>,
/// Maximum number of records returned by `MemoryQuery` / /// Maximum number of records returned by `MemoryQuery` /

View File

@ -1,10 +1,9 @@
//! Workspace-level path layout for the memory subsystem. //! Workspace-level path layout for the memory subsystem.
//! //!
//! `WorkspaceLayout` carries the workspace root (typically the Pod's //! `WorkspaceLayout` carries the root used by the memory subsystem.
//! pwd). All yoi-managed content lives under the conventional //! All yoi-managed memory content lives under the conventional
//! `<root>/.yoi/` subdirectory — the same place that holds //! `<root>/.yoi/` subdirectory — alongside workspace project records
//! `profiles.toml`, `prompts/`, workflow, knowledge, and generated //! such as workflow and generated durable memory. The trees inside it:
//! memory. The trees inside it:
//! //!
//! - `<root>/.yoi/workflow/<slug>.md` //! - `<root>/.yoi/workflow/<slug>.md`
//! - `<root>/.yoi/knowledge/<slug>.md` //! - `<root>/.yoi/knowledge/<slug>.md`
@ -18,9 +17,9 @@
//! Workflows are human-managed and live one level up under //! Workflows are human-managed and live one level up under
//! `.yoi/workflow/`. //! `.yoi/workflow/`.
//! //!
//! Configuring `[memory]` with an empty body is therefore sufficient //! `memory.workspace_root` pins this root explicitly. Without an explicit
//! for any workspace that already uses the `.yoi/` convention; no //! root, resolution searches upward from the Pod pwd for a `.yoi/memory`
//! `workspace_root` override is needed. //! marker; `.yoi` project records alone are not a memory marker.
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -82,17 +81,26 @@ impl WorkspaceLayout {
Self { root: root.into() } Self { root: root.into() }
} }
/// Resolve a layout from a `MemoryConfig`, falling back to /// Resolve a layout from a `MemoryConfig`.
/// `default_root` (typically the Pod's pwd) when the manifest does ///
/// not pin `workspace_root` explicitly. Single source of truth for /// An explicit `memory.workspace_root` is honored exactly. Without an
/// the `workspace_root.unwrap_or(pwd)` convention used across the /// explicit root, resolution searches `default_root` and its ancestors for
/// codebase (controller wiring, scope-deny build, system-prompt /// the nearest `.yoi/memory` directory. This keeps child worktrees that
/// resident-injection). /// contain `.yoi` project records such as tickets or workflows from
/// becoming independent memory roots merely because they contain `.yoi`.
///
/// If no memory marker exists, this falls back to `default_root` because
/// existing call sites require a concrete layout. That fallback is a
/// no-marker compatibility path, not a `.yoi` marker interpretation; it
/// must not be used as evidence that `.yoi` alone enables repo-local
/// memory.
pub fn resolve(cfg: &manifest::MemoryConfig, default_root: &Path) -> Self { pub fn resolve(cfg: &manifest::MemoryConfig, default_root: &Path) -> Self {
let root = cfg if let Some(root) = &cfg.workspace_root {
.workspace_root return Self::new(root.clone());
.clone() }
.unwrap_or_else(|| default_root.to_path_buf());
let root =
find_memory_marker_root(default_root).unwrap_or_else(|| default_root.to_path_buf());
Self::new(root) Self::new(root)
} }
@ -225,6 +233,13 @@ impl WorkspaceLayout {
} }
} }
fn find_memory_marker_root(default_root: &Path) -> Option<PathBuf> {
default_root
.ancestors()
.find(|ancestor| ancestor.join(YOI_DIR).join(MEMORY_DIR).is_dir())
.map(Path::to_path_buf)
}
fn classify_kinded_md( fn classify_kinded_md(
rel: &Path, rel: &Path,
kind: RecordKind, kind: RecordKind,
@ -257,6 +272,7 @@ fn classify_kinded_md(
mod tests { mod tests {
use super::*; use super::*;
use std::path::PathBuf; use std::path::PathBuf;
use tempfile::TempDir;
fn layout() -> WorkspaceLayout { fn layout() -> WorkspaceLayout {
WorkspaceLayout::new(PathBuf::from("/ws")) WorkspaceLayout::new(PathBuf::from("/ws"))
@ -379,9 +395,45 @@ mod tests {
} }
#[test] #[test]
fn resolve_falls_back_to_default_when_workspace_root_missing() { fn resolve_selects_nearest_ancestor_memory_marker_when_workspace_root_missing() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().join("workspace");
let child = workspace.join(".worktree/child");
std::fs::create_dir_all(workspace.join(".yoi/memory")).unwrap();
std::fs::create_dir_all(&child).unwrap();
let cfg = manifest::MemoryConfig::default(); let cfg = manifest::MemoryConfig::default();
let layout = WorkspaceLayout::resolve(&cfg, Path::new("/fallback")); let layout = WorkspaceLayout::resolve(&cfg, &child);
assert_eq!(layout.root(), Path::new("/fallback")); assert_eq!(layout.root(), workspace.as_path());
}
#[test]
fn resolve_ignores_child_project_records_without_memory_marker() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().join("workspace");
let child = workspace.join(".worktree/child");
std::fs::create_dir_all(workspace.join(".yoi/memory")).unwrap();
std::fs::create_dir_all(child.join(".yoi/tickets")).unwrap();
std::fs::create_dir_all(child.join(".yoi/workflow")).unwrap();
let cfg = manifest::MemoryConfig::default();
let layout = WorkspaceLayout::resolve(&cfg, &child);
assert_eq!(layout.root(), workspace.as_path());
}
#[test]
fn yoi_project_records_alone_do_not_define_memory_marker_root() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().join("workspace");
let child = workspace.join("child");
std::fs::create_dir_all(workspace.join(".yoi/tickets")).unwrap();
std::fs::create_dir_all(workspace.join(".yoi/workflow")).unwrap();
std::fs::create_dir_all(&child).unwrap();
assert_eq!(find_memory_marker_root(&child), None);
let cfg = manifest::MemoryConfig::default();
let layout = WorkspaceLayout::resolve(&cfg, &child);
assert_eq!(layout.root(), child.as_path());
} }
} }

View File

@ -383,6 +383,7 @@ pub(crate) struct MultiPodApp {
notice: Option<String>, notice: Option<String>,
sending: bool, sending: bool,
runtime_command: PodRuntimeCommand, runtime_command: PodRuntimeCommand,
last_orchestrator_lifecycle_failure: Option<OrchestratorPanelState>,
} }
impl MultiPodApp { impl MultiPodApp {
@ -397,6 +398,8 @@ impl MultiPodApp {
}, },
) )
.await?; .await?;
let last_orchestrator_lifecycle_failure =
orchestrator_lifecycle_failure_from_panel(&snapshot.panel);
let mut app = Self { let mut app = Self {
list: snapshot.list, list: snapshot.list,
panel: snapshot.panel, panel: snapshot.panel,
@ -406,6 +409,7 @@ impl MultiPodApp {
notice: None, notice: None,
sending: false, sending: false,
runtime_command, runtime_command,
last_orchestrator_lifecycle_failure,
}; };
app.ensure_selection_visible(); app.ensure_selection_visible();
app.ensure_composer_target_available(); app.ensure_composer_target_available();
@ -439,6 +443,7 @@ impl MultiPodApp {
} }
fn apply_reloaded_snapshot(&mut self, mut snapshot: MultiPodSnapshot) { fn apply_reloaded_snapshot(&mut self, mut snapshot: MultiPodSnapshot) {
self.apply_orchestrator_lifecycle_memory(&mut snapshot.panel);
let previous_selected_pod = self.list.selected_name.clone(); let previous_selected_pod = self.list.selected_name.clone();
snapshot.list.selected_name = previous_selected_pod snapshot.list.selected_name = previous_selected_pod
.filter(|name| { .filter(|name| {
@ -463,6 +468,35 @@ impl MultiPodApp {
self.ensure_composer_target_available(); self.ensure_composer_target_available();
} }
fn apply_orchestrator_lifecycle_memory(&mut self, panel: &mut WorkspacePanelViewModel) {
let Some(state) = panel.header.orchestrator.as_ref() else {
self.last_orchestrator_lifecycle_failure = None;
return;
};
match state.status {
OrchestratorPanelStatus::Unavailable => {
self.last_orchestrator_lifecycle_failure =
orchestrator_lifecycle_failure_from_panel(panel);
}
OrchestratorPanelStatus::Live
| OrchestratorPanelStatus::Spawned
| OrchestratorPanelStatus::Restored => {
self.last_orchestrator_lifecycle_failure = None;
}
OrchestratorPanelStatus::Missing | OrchestratorPanelStatus::Stopped => {
if let Some(previous) = self.last_orchestrator_lifecycle_failure.clone() {
if previous.pod_name == state.pod_name {
panel.header.orchestrator = Some(previous.clone());
append_unique_diagnostic(panel, previous.detail.as_deref());
} else {
self.last_orchestrator_lifecycle_failure = None;
}
}
}
}
}
fn selected_panel_row(&self) -> Option<&PanelRow> { fn selected_panel_row(&self) -> Option<&PanelRow> {
self.selected_row self.selected_row
.as_ref() .as_ref()
@ -1040,6 +1074,31 @@ struct MultiPodSnapshot {
panel: WorkspacePanelViewModel, panel: WorkspacePanelViewModel,
} }
fn orchestrator_lifecycle_failure_from_panel(
panel: &WorkspacePanelViewModel,
) -> Option<OrchestratorPanelState> {
let state = panel.header.orchestrator.as_ref()?;
if state.status == OrchestratorPanelStatus::Unavailable && state.detail.is_some() {
Some(state.clone())
} else {
None
}
}
fn append_unique_diagnostic(panel: &mut WorkspacePanelViewModel, diagnostic: Option<&str>) {
let Some(diagnostic) = diagnostic else {
return;
};
if !panel
.header
.diagnostics
.iter()
.any(|existing| existing == diagnostic)
{
panel.header.diagnostics.push(diagnostic.to_string());
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum OrchestratorLifecycleMode { enum OrchestratorLifecycleMode {
Ensure { runtime_command: PodRuntimeCommand }, Ensure { runtime_command: PodRuntimeCommand },
@ -1539,7 +1598,7 @@ fn orchestrator_queue_notification_message(
) -> String { ) -> String {
let title = ticket.title.replace(['\r', '\n'], " "); let title = ticket.title.replace(['\r', '\n'], " ");
format!( format!(
"Workspace panel Queue for Ticket `{}` (`{}`), title `{}`: human authorized Orchestrator routing; this is not an unattended scheduler. Read the Ticket and inspect current workspace state. If unblocked, record routing and transition workflow_state queued -> inprogress before any worktree/SpawnPod implementation side effects. After inprogress acceptance, use worktree-workflow for `.worktree/<task-name>` creation with `.yoi` excluded, then use multi-agent-workflow to run sibling coder/reviewer Pods (coder narrow child-worktree write scope, reviewer read-only by default) and stop at a merge-ready dossier without merge/close/final approval. If blocked, record a concise reason and leave the Ticket queued or explicitly defer it.", "Workspace panel Queue for Ticket `{}` (`{}`), title `{}`: human authorized Orchestrator routing; this is not an unattended scheduler. Read the Ticket and inspect current workspace state. If unblocked, record routing and transition workflow_state queued -> inprogress before any worktree/SpawnPod implementation side effects. After inprogress acceptance, use worktree-workflow for `.worktree/<task-name>` creation with tracked `.yoi` project records visible and `.yoi/memory` plus local/runtime/log/lock/secret-like `.yoi` paths excluded, then use multi-agent-workflow to run sibling coder/reviewer Pods (coder narrow child-worktree write scope, reviewer read-only by default) and stop at a merge-ready dossier without merge/close/final approval. If blocked, record a concise reason and leave the Ticket queued or explicitly defer it.",
ticket.slug, ticket.slug,
ticket.id, ticket.id,
title.trim() title.trim()
@ -2598,7 +2657,10 @@ mod tests {
assert!(message.contains("After inprogress acceptance")); assert!(message.contains("After inprogress acceptance"));
assert!(message.contains("worktree-workflow")); assert!(message.contains("worktree-workflow"));
assert!(message.contains("`.worktree/<task-name>`")); assert!(message.contains("`.worktree/<task-name>`"));
assert!(message.contains("`.yoi` excluded")); assert!(message.contains("tracked `.yoi` project records visible"));
assert!(message.contains(
"`.yoi/memory` plus local/runtime/log/lock/secret-like `.yoi` paths excluded"
));
assert!(message.contains("multi-agent-workflow")); assert!(message.contains("multi-agent-workflow"));
assert!(message.contains("sibling coder/reviewer Pods")); assert!(message.contains("sibling coder/reviewer Pods"));
assert!(message.contains("coder narrow child-worktree write scope")); assert!(message.contains("coder narrow child-worktree write scope"));
@ -2811,6 +2873,117 @@ mod tests {
assert!(notice.contains("boom")); assert!(notice.contains("boom"));
} }
#[test]
fn multi_orchestrator_failure_persists_over_plain_observe_missing() {
let detail =
"could not spawn workspace Orchestrator: delegated scope conflicts with writer";
let mut app = app_with_panel(
empty_test_list(),
panel_with_orchestrator(OrchestratorPanelStatus::Unavailable, Some(detail)),
);
app.apply_reloaded_snapshot(MultiPodSnapshot {
list: empty_test_list(),
panel: panel_with_orchestrator(OrchestratorPanelStatus::Missing, None),
});
let orchestrator = app.panel.header.orchestrator.as_ref().unwrap();
assert_eq!(orchestrator.status, OrchestratorPanelStatus::Unavailable);
assert_eq!(orchestrator.detail.as_deref(), Some(detail));
assert_eq!(
app.panel
.header
.diagnostics
.iter()
.filter(|diagnostic| diagnostic.as_str() == detail)
.count(),
1
);
}
#[test]
fn multi_orchestrator_plain_missing_remains_when_no_prior_failure_exists() {
let mut app = app_with_panel(
empty_test_list(),
panel_with_orchestrator(OrchestratorPanelStatus::Missing, None),
);
app.apply_reloaded_snapshot(MultiPodSnapshot {
list: empty_test_list(),
panel: panel_with_orchestrator(OrchestratorPanelStatus::Missing, None),
});
let orchestrator = app.panel.header.orchestrator.as_ref().unwrap();
assert_eq!(orchestrator.status, OrchestratorPanelStatus::Missing);
assert!(orchestrator.detail.is_none());
assert!(app.panel.header.diagnostics.is_empty());
}
#[test]
fn multi_orchestrator_failure_clears_after_live_lifecycle() {
let detail =
"could not spawn workspace Orchestrator: delegated scope conflicts with writer";
let mut app = app_with_panel(
empty_test_list(),
panel_with_orchestrator(OrchestratorPanelStatus::Unavailable, Some(detail)),
);
app.apply_reloaded_snapshot(MultiPodSnapshot {
list: empty_test_list(),
panel: panel_with_orchestrator(OrchestratorPanelStatus::Live, None),
});
assert_eq!(
app.panel.header.orchestrator.as_ref().unwrap().status,
OrchestratorPanelStatus::Live
);
app.apply_reloaded_snapshot(MultiPodSnapshot {
list: empty_test_list(),
panel: panel_with_orchestrator(OrchestratorPanelStatus::Missing, None),
});
let orchestrator = app.panel.header.orchestrator.as_ref().unwrap();
assert_eq!(orchestrator.status, OrchestratorPanelStatus::Missing);
assert!(orchestrator.detail.is_none());
}
#[test]
fn multi_orchestrator_failure_supersedes_prior_failure() {
let old_detail = "could not spawn workspace Orchestrator: old scope conflict";
let new_detail = "could not restore workspace Orchestrator: socket refused";
let mut app = app_with_panel(
empty_test_list(),
panel_with_orchestrator(OrchestratorPanelStatus::Unavailable, Some(old_detail)),
);
app.apply_reloaded_snapshot(MultiPodSnapshot {
list: empty_test_list(),
panel: panel_with_orchestrator(OrchestratorPanelStatus::Unavailable, Some(new_detail)),
});
app.apply_reloaded_snapshot(MultiPodSnapshot {
list: empty_test_list(),
panel: panel_with_orchestrator(OrchestratorPanelStatus::Missing, None),
});
let orchestrator = app.panel.header.orchestrator.as_ref().unwrap();
assert_eq!(orchestrator.status, OrchestratorPanelStatus::Unavailable);
assert_eq!(orchestrator.detail.as_deref(), Some(new_detail));
assert!(
!app.panel
.header
.diagnostics
.iter()
.any(|diagnostic| diagnostic == old_detail)
);
assert!(
app.panel
.header
.diagnostics
.iter()
.any(|diagnostic| diagnostic == new_detail)
);
}
#[tokio::test] #[tokio::test]
async fn multi_poll_reload_does_not_overlap_in_flight_reload() { async fn multi_poll_reload_does_not_overlap_in_flight_reload() {
let mut app = test_app(vec![live_info("alpha", PodStatus::Idle)]); let mut app = test_app(vec![live_info("alpha", PodStatus::Idle)]);
@ -3564,7 +3737,28 @@ mod tests {
app_with_panel(list, WorkspacePanelViewModel::empty(Path::new("test"))) app_with_panel(list, WorkspacePanelViewModel::empty(Path::new("test")))
} }
fn empty_test_list() -> PodList {
PodList::from_sources(PodVisibilitySource::ResumePicker, vec![], vec![], None, 10)
}
fn panel_with_orchestrator(
status: OrchestratorPanelStatus,
detail: Option<&str>,
) -> WorkspacePanelViewModel {
let mut panel = WorkspacePanelViewModel::empty(Path::new("test"));
panel.header.orchestrator = Some(OrchestratorPanelState::new(
"test-orchestrator",
status,
detail.map(str::to_string),
));
if let Some(detail) = detail {
panel.header.diagnostics.push(detail.to_string());
}
panel
}
fn app_with_panel(list: PodList, panel: WorkspacePanelViewModel) -> MultiPodApp { fn app_with_panel(list: PodList, panel: WorkspacePanelViewModel) -> MultiPodApp {
let last_orchestrator_lifecycle_failure = orchestrator_lifecycle_failure_from_panel(&panel);
let mut app = MultiPodApp { let mut app = MultiPodApp {
list, list,
panel, panel,
@ -3574,6 +3768,7 @@ mod tests {
notice: None, notice: None,
sending: false, sending: false,
runtime_command: PodRuntimeCommand::for_executable("/tmp/yoi"), runtime_command: PodRuntimeCommand::for_executable("/tmp/yoi"),
last_orchestrator_lifecycle_failure,
}; };
app.ensure_selection_visible(); app.ensure_selection_visible();
app.ensure_composer_target_available(); app.ensure_composer_target_available();