tui: introduce dashboard console boundaries
This commit is contained in:
parent
7e35721a81
commit
5415a9478d
|
|
@ -13,7 +13,7 @@ Main highlights:
|
|||
- Multi-agent orchestration with scoped coder/reviewer Pods.
|
||||
- Profile, Manifest, and prompt-based runtime configuration.
|
||||
- Local Tickets and workflow files for auditable project coordination.
|
||||
- TUI and CLI entry points, including a multi-Pod dashboard.
|
||||
- TUI and CLI entry points, including the `yoi panel` workspace Dashboard and single-Pod Console.
|
||||
|
||||
Yoi is actively dogfooded in this repository. Public APIs, configuration formats, and workflows may still change.
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ nix build .#yoi
|
|||
```sh
|
||||
yoi --help
|
||||
yoi
|
||||
yoi --multi
|
||||
yoi panel
|
||||
yoi --pod <name>
|
||||
yoi pod --help
|
||||
```
|
||||
|
|
@ -46,7 +46,7 @@ yoi pod --help
|
|||
Typical flow:
|
||||
|
||||
1. Configure providers, models, profiles, prompts, and scopes.
|
||||
2. Start or attach to a named Pod from the CLI/TUI.
|
||||
2. Start or attach to a named Pod in the Console, or inspect workspace activity in the Dashboard.
|
||||
3. Use explicit tools and scoped delegation for multi-agent work.
|
||||
4. Record project work through Tickets, workflow files, and git history.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Role
|
||||
|
||||
`tui` implements terminal UI clients for interacting with one or more Pods.
|
||||
`tui` implements terminal UI clients for the single-Pod Console and workspace Dashboard surfaces.
|
||||
|
||||
## Boundaries
|
||||
|
||||
|
|
@ -10,8 +10,8 @@ Owns:
|
|||
|
||||
- terminal rendering and input handling
|
||||
- local composer state and UI affordances
|
||||
- single-Pod attach/restore screens
|
||||
- multi-Pod dashboard presentation
|
||||
- single-Pod Console attach/restore/chat screens
|
||||
- workspace Dashboard presentation and role-action UI
|
||||
|
||||
Does not own:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
|
|
@ -30,9 +31,15 @@ use crate::app::{ActionbarNoticeLevel, ActionbarNoticeSource, App};
|
|||
use crate::composer_keys::{ComposerEditAction, composer_edit_action};
|
||||
use crate::picker::PickerOutcome;
|
||||
use crate::spawn::{SpawnOutcome, SpawnReady};
|
||||
use crate::{multi_pod, picker, spawn, ui};
|
||||
use crate::{picker, spawn, ui};
|
||||
|
||||
type FullscreenTerminal = Terminal<CrosstermBackend<io::Stdout>>;
|
||||
pub(crate) type ConsoleTerminal = Terminal<CrosstermBackend<io::Stdout>>;
|
||||
|
||||
/// Narrow request bridge used when the workspace Dashboard opens a Pod Console.
|
||||
pub(crate) struct DashboardConsoleOpenRequest {
|
||||
pub(crate) pod_name: String,
|
||||
pub(crate) socket_override: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Enable SGR coordinates plus normal mouse tracking. This captures clicks,
|
||||
/// releases, and wheel events without drag-capture modes (`?1002h`/`?1003h`)
|
||||
|
|
@ -58,13 +65,13 @@ impl Command for EnableSinglePodMouseCapture {
|
|||
}
|
||||
}
|
||||
|
||||
/// Enable Panel mouse input without drag tracking. The Panel only needs button
|
||||
/// presses/releases and wheel events; enabling `?1002h` can make terminal drag
|
||||
/// selection look captured and is intentionally avoided for Panel startup.
|
||||
/// Enable Dashboard mouse input without drag tracking. The Dashboard only needs
|
||||
/// button presses/releases and wheel events; enabling `?1002h` can make terminal
|
||||
/// drag selection look captured and is intentionally avoided before startup.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct EnablePanelMouseCapture;
|
||||
struct EnableDashboardMouseCapture;
|
||||
|
||||
impl Command for EnablePanelMouseCapture {
|
||||
impl Command for EnableDashboardMouseCapture {
|
||||
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
||||
// 1006: SGR extended coordinates used by crossterm's parser
|
||||
// 1000: normal mouse tracking (button presses/releases and wheel)
|
||||
|
|
@ -165,7 +172,7 @@ pub(crate) async fn run_pod_name(
|
|||
}
|
||||
|
||||
async fn run_connected_pod(
|
||||
terminal: &mut FullscreenTerminal,
|
||||
terminal: &mut ConsoleTerminal,
|
||||
pod_name: String,
|
||||
client: PodClient,
|
||||
runtime_command: PodRuntimeCommand,
|
||||
|
|
@ -176,12 +183,12 @@ async fn run_connected_pod(
|
|||
run_loop(terminal, &mut app, client, runtime_command).await
|
||||
}
|
||||
|
||||
async fn run_pod_name_nested(
|
||||
terminal: &mut FullscreenTerminal,
|
||||
request: multi_pod::OpenPodRequest,
|
||||
pub(crate) async fn open_from_dashboard(
|
||||
terminal: &mut ConsoleTerminal,
|
||||
request: DashboardConsoleOpenRequest,
|
||||
runtime_command: PodRuntimeCommand,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let multi_pod::OpenPodRequest {
|
||||
let DashboardConsoleOpenRequest {
|
||||
pod_name,
|
||||
socket_override,
|
||||
} = request;
|
||||
|
|
@ -196,7 +203,7 @@ async fn run_pod_name_nested(
|
|||
}
|
||||
|
||||
async fn spawn_pod_name_from_fullscreen(
|
||||
terminal: &mut FullscreenTerminal,
|
||||
terminal: &mut ConsoleTerminal,
|
||||
pod_name: &str,
|
||||
runtime_command: PodRuntimeCommand,
|
||||
) -> Result<SpawnReady, Box<dyn std::error::Error>> {
|
||||
|
|
@ -233,7 +240,7 @@ impl std::fmt::Display for NestedOpenCancelled {
|
|||
impl std::error::Error for NestedOpenCancelled {}
|
||||
|
||||
async fn run_ready_pod(
|
||||
terminal: &mut FullscreenTerminal,
|
||||
terminal: &mut ConsoleTerminal,
|
||||
ready: SpawnReady,
|
||||
runtime_command: PodRuntimeCommand,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
|
@ -281,36 +288,7 @@ pub(crate) async fn run_resume(
|
|||
run_pod_name(pod_name, socket_override, runtime_command).await
|
||||
}
|
||||
|
||||
pub(crate) async fn run_panel(
|
||||
runtime_command: PodRuntimeCommand,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut app = multi_pod::load_app(runtime_command.clone()).await?;
|
||||
let mut terminal = enter_panel_fullscreen()?;
|
||||
|
||||
loop {
|
||||
match multi_pod::run(&mut terminal, &mut app).await? {
|
||||
multi_pod::MultiPodOutcome::Quit => {
|
||||
let _ = leave_fullscreen(&mut terminal);
|
||||
return Ok(());
|
||||
}
|
||||
multi_pod::MultiPodOutcome::Open(request) => {
|
||||
let pod_name = request.pod_name.clone();
|
||||
match run_pod_name_nested(&mut terminal, request, runtime_command.clone()).await {
|
||||
Ok(()) => app.finish_open(&pod_name, Ok(())),
|
||||
Err(error) if is_recoverable_multi_open_error(error.as_ref()) => {
|
||||
app.finish_open(&pod_name, Err(error.as_ref()));
|
||||
}
|
||||
Err(error) => {
|
||||
let _ = leave_fullscreen(&mut terminal);
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_recoverable_multi_open_error(error: &(dyn std::error::Error + 'static)) -> bool {
|
||||
pub(crate) fn is_recoverable_dashboard_open_error(error: &(dyn Error + 'static)) -> bool {
|
||||
error.is::<spawn::SpawnError>() || error.is::<NestedOpenCancelled>()
|
||||
}
|
||||
|
||||
|
|
@ -353,7 +331,7 @@ pub(crate) async fn run_spawn(
|
|||
result
|
||||
}
|
||||
|
||||
fn enter_fullscreen() -> Result<FullscreenTerminal, Box<dyn std::error::Error>> {
|
||||
fn enter_fullscreen() -> Result<ConsoleTerminal, Box<dyn std::error::Error>> {
|
||||
let mut stdout = io::stdout();
|
||||
// Enable button-event tracking so the transcript can own drag selection;
|
||||
// avoid all-motion capture because hover-motion reports are unnecessary.
|
||||
|
|
@ -362,17 +340,17 @@ fn enter_fullscreen() -> Result<FullscreenTerminal, Box<dyn std::error::Error>>
|
|||
Ok(Terminal::new(backend)?)
|
||||
}
|
||||
|
||||
fn enter_panel_fullscreen() -> Result<FullscreenTerminal, Box<dyn std::error::Error>> {
|
||||
pub(crate) fn enter_dashboard_fullscreen() -> Result<ConsoleTerminal, Box<dyn std::error::Error>> {
|
||||
let mut stdout = io::stdout();
|
||||
// Panel needs clicks and wheel input only; do not capture drag motion before
|
||||
// Dashboard needs clicks and wheel input only; do not capture drag motion before
|
||||
// the first visible frame.
|
||||
execute!(stdout, EnterAlternateScreen, EnablePanelMouseCapture)?;
|
||||
execute!(stdout, EnterAlternateScreen, EnableDashboardMouseCapture)?;
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
Ok(Terminal::new(backend)?)
|
||||
}
|
||||
|
||||
fn enter_fullscreen_existing(
|
||||
terminal: &mut FullscreenTerminal,
|
||||
terminal: &mut ConsoleTerminal,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Re-enable the same least-intrusive wheel mouse mode after returning from
|
||||
// nested inline screens.
|
||||
|
|
@ -384,7 +362,7 @@ fn enter_fullscreen_existing(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn leave_fullscreen(terminal: &mut FullscreenTerminal) -> io::Result<()> {
|
||||
fn leave_fullscreen(terminal: &mut ConsoleTerminal) -> io::Result<()> {
|
||||
execute!(
|
||||
terminal.backend_mut(),
|
||||
DisableMouseCapture,
|
||||
|
|
@ -392,8 +370,12 @@ fn leave_fullscreen(terminal: &mut FullscreenTerminal) -> io::Result<()> {
|
|||
)
|
||||
}
|
||||
|
||||
pub(crate) fn leave_dashboard_fullscreen(terminal: &mut ConsoleTerminal) -> io::Result<()> {
|
||||
leave_fullscreen(terminal)
|
||||
}
|
||||
|
||||
async fn run(
|
||||
terminal: &mut FullscreenTerminal,
|
||||
terminal: &mut ConsoleTerminal,
|
||||
pod_name: String,
|
||||
socket_path: &std::path::Path,
|
||||
runtime_command: PodRuntimeCommand,
|
||||
|
|
@ -480,7 +462,7 @@ fn read_terminal_events(stop: Arc<AtomicBool>, tx: mpsc::UnboundedSender<Termina
|
|||
|
||||
#[cfg(feature = "e2e-test")]
|
||||
async fn run_e2e_rewind_fixture(
|
||||
terminal: &mut FullscreenTerminal,
|
||||
terminal: &mut ConsoleTerminal,
|
||||
pod_name: String,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let workspace_root = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
|
||||
File diff suppressed because it is too large
Load Diff
1025
crates/tui/src/dashboard/render.rs
Normal file
1025
crates/tui/src/dashboard/render.rs
Normal file
File diff suppressed because it is too large
Load Diff
3176
crates/tui/src/dashboard/tests.rs
Normal file
3176
crates/tui/src/dashboard/tests.rs
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -4,18 +4,18 @@ mod cache;
|
|||
mod command;
|
||||
mod composer_history;
|
||||
mod composer_keys;
|
||||
mod console;
|
||||
mod dashboard;
|
||||
#[cfg(feature = "e2e-test")]
|
||||
mod e2e_observer;
|
||||
mod input;
|
||||
pub mod keys;
|
||||
mod markdown;
|
||||
mod multi_pod;
|
||||
mod picker;
|
||||
mod pod_list;
|
||||
mod role_session_registry;
|
||||
mod scroll;
|
||||
pub mod setup_model;
|
||||
mod single_pod;
|
||||
mod spawn;
|
||||
mod task;
|
||||
mod text_selection;
|
||||
|
|
@ -64,7 +64,7 @@ pub enum LaunchMode {
|
|||
id: SegmentId,
|
||||
pod_name: Option<String>,
|
||||
},
|
||||
/// `yoi panel`: open the workspace panel from the current workspace.
|
||||
/// `yoi panel`: open the workspace Dashboard from the current workspace.
|
||||
Panel,
|
||||
}
|
||||
|
||||
|
|
@ -95,17 +95,17 @@ pub async fn launch(options: LaunchOptions) -> ExitCode {
|
|||
|
||||
let result = match mode {
|
||||
LaunchMode::Spawn { pod_name, profile } => {
|
||||
single_pod::run_spawn(None, pod_name, profile, runtime_command).await
|
||||
console::run_spawn(None, pod_name, profile, runtime_command).await
|
||||
}
|
||||
LaunchMode::PodName {
|
||||
pod_name,
|
||||
socket_override,
|
||||
} => single_pod::run_pod_name(pod_name, socket_override, runtime_command).await,
|
||||
LaunchMode::Resume => single_pod::run_resume(runtime_command).await,
|
||||
} => console::run_pod_name(pod_name, socket_override, runtime_command).await,
|
||||
LaunchMode::Resume => console::run_resume(runtime_command).await,
|
||||
LaunchMode::ResumeWithSession { id, pod_name } => {
|
||||
single_pod::run_spawn(Some(id), pod_name, None, runtime_command).await
|
||||
console::run_spawn(Some(id), pod_name, None, runtime_command).await
|
||||
}
|
||||
LaunchMode::Panel => single_pod::run_panel(runtime_command).await,
|
||||
LaunchMode::Panel => dashboard::launch(runtime_command).await,
|
||||
};
|
||||
|
||||
// Always restore the terminal first so any pending eprintln below
|
||||
|
|
|
|||
|
|
@ -671,7 +671,11 @@ coder = "profiles/coder.lua"
|
|||
.unwrap();
|
||||
|
||||
let (choices, default_index) = profile_choices_for_cwd(&project);
|
||||
assert_eq!(default_index, 1);
|
||||
let default_choice = choices
|
||||
.iter()
|
||||
.position(|choice| choice.selector.as_deref() == Some("project:coder"))
|
||||
.expect("project default choice is present");
|
||||
assert_eq!(default_index, default_choice);
|
||||
let selected = &choices[default_index];
|
||||
assert_eq!(selected.selector.as_deref(), Some("project:coder"));
|
||||
assert_eq!(selected.label, "project:coder (default)");
|
||||
|
|
@ -701,9 +705,19 @@ description = "Project coder"
|
|||
choices[0].label,
|
||||
"builtin:default — Bundled default Yoi coding profile"
|
||||
);
|
||||
assert_eq!(default_index, 1);
|
||||
assert_eq!(choices[1].selector.as_deref(), Some("project:coder"));
|
||||
assert_eq!(choices[1].label, "project:coder (default) — Project coder");
|
||||
let project_index = choices
|
||||
.iter()
|
||||
.position(|choice| choice.selector.as_deref() == Some("project:coder"))
|
||||
.expect("project default choice is present");
|
||||
assert_eq!(default_index, project_index);
|
||||
assert_eq!(
|
||||
choices[project_index].selector.as_deref(),
|
||||
Some("project:coder")
|
||||
);
|
||||
assert_eq!(
|
||||
choices[project_index].label,
|
||||
"project:coder (default) — Project coder"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -623,7 +623,7 @@ fn parse_session_id(value: &str) -> Result<SegmentId, ParseError> {
|
|||
|
||||
fn print_help() {
|
||||
println!(
|
||||
"yoi\n\nUsage:\n yoi [OPTIONS] [POD_NAME]\n yoi panel [--workspace <PATH>]\n yoi keys\n yoi setup-model\n yoi pod [POD_OPTIONS]\n yoi objective <COMMAND> [OPTIONS]\n yoi session analyze <SESSION_JSONL_PATH> --json\n yoi ticket <COMMAND> [OPTIONS]\n yoi plugin new rust-component-tool <PATH> [--json]\n yoi plugin check <PATH_OR_PACKAGE> [--json]\n yoi plugin pack <PATH> [--output <FILE>] [--json]\n yoi plugin list [--workspace <PATH>] [--profile <REF>] [--json]\n yoi plugin show <REF> [--workspace <PATH>] [--profile <REF>] [--json]\n yoi memory lint [OPTIONS]\n\nOptions:\n -r, --resume Open the Pod picker and resume/attach a Pod\n --workspace <PATH> Runtime workspace root (defaults to cwd)\n --pod <NAME> Attach/restore/create a Pod by name\n --socket <PATH> Attach to a specific Pod socket with --pod\n --session <UUID> Resume a specific session segment\n --profile <REF> Select a reusable Profile recipe\n -h, --help Print help\n"
|
||||
"yoi\n\nUsage:\n yoi [OPTIONS] [POD_NAME]\n yoi panel [--workspace <PATH>]\n yoi keys\n yoi setup-model\n yoi pod [POD_OPTIONS]\n yoi objective <COMMAND> [OPTIONS]\n yoi session analyze <SESSION_JSONL_PATH> --json\n yoi ticket <COMMAND> [OPTIONS]\n yoi plugin new rust-component-tool <PATH> [--json]\n yoi plugin check <PATH_OR_PACKAGE> [--json]\n yoi plugin pack <PATH> [--output <FILE>] [--json]\n yoi plugin list [--workspace <PATH>] [--profile <REF>] [--json]\n yoi plugin show <REF> [--workspace <PATH>] [--profile <REF>] [--json]\n yoi memory lint [OPTIONS]\n\nSurfaces:\n Console Single-Pod chat/client surface (default, --pod, --resume)\n Dashboard Workspace cockpit/action surface (yoi panel)\n TUI Terminal UI implementation umbrella for Console and Dashboard\n\nOptions:\n -r, --resume Open the Pod Console picker and resume/attach a Pod\n --workspace <PATH> Runtime workspace root (defaults to cwd)\n --pod <NAME> Open the Pod Console by name (attach/restore/create)\n --socket <PATH> Attach a Pod Console to a specific socket with --pod\n --session <UUID> Resume a specific session segment in the Pod Console\n --profile <REF> Select a reusable Profile recipe\n -h, --help Print help\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -973,6 +973,18 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_dashboard_word_remains_a_pod_console_name_not_an_alias() {
|
||||
let config = parse_args_from(["dashboard"]).unwrap();
|
||||
match config {
|
||||
Mode::Tui {
|
||||
mode: LaunchMode::PodName { pod_name, .. },
|
||||
..
|
||||
} => assert_eq!(pod_name, "dashboard"),
|
||||
other => panic!("expected PodName TUI mode, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multi_flag_is_not_a_launch_alias() {
|
||||
let err = parse_args_from(["--multi"]).unwrap_err();
|
||||
|
|
|
|||
|
|
@ -1125,7 +1125,8 @@ mod tests {
|
|||
assert!(config.contains("# [ticket]\n# language = \"Japanese\""));
|
||||
for role in TicketRole::ALL {
|
||||
assert!(config.contains(&format!(
|
||||
"[roles.{role}]\nprofile = \"builtin:default\"\nworkflow = \"{}\"",
|
||||
"[roles.{role}]\nprofile = \"{}\"\nworkflow = \"{}\"",
|
||||
role.default_profile(),
|
||||
role.default_workflow()
|
||||
)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ A Ticket may represent a feature, bug, cleanup, design decision, investigation,
|
|||
|
||||
Use the highest-level interface that matches the work:
|
||||
|
||||
- Use `yoi panel` for the Ticket/Intake/Orchestrator workspace UI and role-launch actions.
|
||||
- Use `yoi panel` for the Ticket/Intake/Orchestrator workspace Dashboard and role-launch actions.
|
||||
- Use `yoi objective ...` for lightweight medium-term Objective records and their non-blocking canonical Ticket links.
|
||||
- Inside Pods, use typed Ticket tools to create, inspect, comment, review, and close Tickets.
|
||||
- For multi-step work, follow the Ticket Intake, Orchestrator Routing, planning/requirements-sync, and Multi-agent workflows.
|
||||
|
|
@ -268,9 +268,9 @@ Before closing, verify concrete evidence:
|
|||
|
||||
Close with a resolution that summarizes what changed, key commits, validation, review state, and remaining follow-ups.
|
||||
|
||||
## Workspace panel Ticket role actions
|
||||
## Workspace Dashboard Ticket role actions
|
||||
|
||||
`yoi panel` is the active Ticket/Intake/Orchestrator UI. It owns fixed Ticket role-launch actions and uses the shared client Ticket role launcher. The single-Pod TUI no longer supports `:ticket ...` commands; typing them in command mode is treated like any other unknown command.
|
||||
`yoi panel` is the active Ticket/Intake/Orchestrator Dashboard. It owns fixed Ticket role-launch actions and uses the shared client Ticket role launcher. The single-Pod Console no longer supports `:ticket ...` commands; typing them in command mode is treated like any other unknown command.
|
||||
|
||||
Role actions map to the same fixed roles configured in `.yoi/ticket.config.toml`:
|
||||
|
||||
|
|
@ -279,24 +279,24 @@ Role actions map to the same fixed roles configured in `.yoi/ticket.config.toml`
|
|||
- implement launches the coder role for an implementation assignment.
|
||||
- review launches the reviewer role for review.
|
||||
|
||||
All actions are explicit and user-triggered. They are not a scheduler, queue, spawned-Pod panel, or automatic maintainer loop.
|
||||
All actions are explicit and user-triggered. They are not a scheduler, queue, spawned-Pod Dashboard, or automatic maintainer loop.
|
||||
|
||||
### Panel execution path
|
||||
### Dashboard execution path
|
||||
|
||||
The role-launch path is:
|
||||
|
||||
```text
|
||||
User triggers a Ticket action in yoi panel
|
||||
-> panel builds a TicketRoleLaunchContext
|
||||
-> Dashboard builds a TicketRoleLaunchContext
|
||||
-> client Ticket role launcher reads .yoi/ticket.config.toml
|
||||
-> launcher selects the role Profile and workflow
|
||||
-> launcher spawns the role Pod
|
||||
-> launcher sends Method::Run with WorkflowInvoke + Text segments
|
||||
-> launcher waits for run-acceptance evidence
|
||||
-> panel reports success/failure
|
||||
-> Dashboard reports success/failure
|
||||
```
|
||||
|
||||
The launched Pod receives dynamic Ticket/action context as its first committed run input. The panel does not inject hidden context, does not write Ticket files directly, and does not construct prompt/workflow segments by hand.
|
||||
The launched Pod receives dynamic Ticket/action context as its first committed run input. The Dashboard does not inject hidden context, does not write Ticket files directly, and does not construct prompt/workflow segments by hand.
|
||||
|
||||
The first run input contains:
|
||||
|
||||
|
|
@ -308,9 +308,9 @@ The first run input contains:
|
|||
|
||||
The selected Profile supplies durable system/role behavior. `ticket.config.toml` does not override system instruction.
|
||||
|
||||
### Panel setup
|
||||
### Dashboard setup
|
||||
|
||||
Because top-level role launches cannot inherit a parent Profile, configure concrete role profiles before using panel role actions:
|
||||
Because top-level role launches cannot inherit a parent Profile, configure concrete role profiles before using Dashboard role actions:
|
||||
|
||||
```toml
|
||||
# .yoi/ticket.config.toml
|
||||
|
|
@ -336,9 +336,9 @@ profile = "project:reviewer"
|
|||
workflow = "multi-agent-workflow"
|
||||
```
|
||||
|
||||
If a role still uses `profile = "inherit"`, the panel fails closed with a diagnostic explaining that a concrete profile is required.
|
||||
If a role still uses `profile = "inherit"`, the Dashboard fails closed with a diagnostic explaining that a concrete profile is required.
|
||||
|
||||
### Panel troubleshooting
|
||||
### Dashboard troubleshooting
|
||||
|
||||
- `profile = "inherit"`: configure a concrete role Profile in `.yoi/ticket.config.toml`.
|
||||
- malformed `.yoi/ticket.config.toml`: fix the config and retry.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<system-reminder>
|
||||
Workspace panel observed that this Orchestrator Pod is idle while queued Ticket work is present.
|
||||
Workspace Dashboard observed that this Orchestrator Pod is idle while queued Ticket work is present.
|
||||
|
||||
This is bounded attention only, not scheduler authority. Do not drain the queue automatically. Before implementation side effects, verify the Ticket state and record the normal `queued -> inprogress` acceptance through Ticket tools.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user