mirror of
https://github.com/instructkr/claude-code.git
synced 2026-06-05 12:06:43 +00:00
fix: structured bootstrap-plan phases JSON (#412)
bootstrap-plan --output-format json now returns phases as structured objects with id, label, description, and order fields instead of raw Rust enum variant name strings. Also exposes total_phases count. Generated with https://github.com/Yeachan-Heo/gajae-code Co-authored-by: Gajae Code <dev@gajae-code.com>
This commit is contained in:
@@ -6317,7 +6317,7 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed)
|
|||||||
411. **`plugins enable/disable --output-format json` always emits `reload_runtime:true` regardless of whether state actually changed, and omits `previous_status`, `changed`, `version`, and `source` fields — automation cannot tell if a reload is necessary or if the mutation was a no-op** — dogfooded 2026-04-30 by Jobdori on `e939777f`. Running `claw plugins enable example-bundled --output-format json` on an already-enabled plugin returns `{"action":"enable","kind":"plugin","message":"…","reload_runtime":true,"target":"example-bundled"}` — `reload_runtime:true` every time, even on a no-op re-enable. The same applies to idempotent `disable`. Structured fields present: `action`, `kind`, `message`, `reload_runtime`, `target`. Structured fields absent: `previous_status`, `status`, `changed`, `version`, `source`. The actual plugin name, version, and new status are embedded only in the prose `message` field (`"Result enabled example-bundled@bundled\n Name example-bundled\n Version 0.1.0\n Status enabled"`), requiring callers to scrape column-aligned text to extract the post-mutation state. A no-op mutation emitting `reload_runtime:true` forces orchestration to trigger an expensive runtime reload even when no config change occurred. **Required fix shape:** (a) add `changed:bool` so callers can skip runtime reload when `changed:false`; (b) add `previous_status` and `status` fields (enums: `enabled`/`disabled`) so pre/post state is machine-readable without parsing `message`; (c) add `version` and `source` fields at the mutation response level, consistent with `plugins list` entry shape; (d) emit `reload_runtime:false` when `changed:false`; (e) add regression coverage proving idempotent enable/disable sets `changed:false` and `reload_runtime:false`. **Why this matters:** plugin lifecycle is a hot path for automation that conditionally enables plugins before running sessions. If every enable emits `reload_runtime:true` and no `changed` field exists, orchestration must reload unconditionally or maintain external state — both brittle patterns. Source: Jobdori live dogfood, `e939777f`, 2026-04-30.
|
411. **`plugins enable/disable --output-format json` always emits `reload_runtime:true` regardless of whether state actually changed, and omits `previous_status`, `changed`, `version`, and `source` fields — automation cannot tell if a reload is necessary or if the mutation was a no-op** — dogfooded 2026-04-30 by Jobdori on `e939777f`. Running `claw plugins enable example-bundled --output-format json` on an already-enabled plugin returns `{"action":"enable","kind":"plugin","message":"…","reload_runtime":true,"target":"example-bundled"}` — `reload_runtime:true` every time, even on a no-op re-enable. The same applies to idempotent `disable`. Structured fields present: `action`, `kind`, `message`, `reload_runtime`, `target`. Structured fields absent: `previous_status`, `status`, `changed`, `version`, `source`. The actual plugin name, version, and new status are embedded only in the prose `message` field (`"Result enabled example-bundled@bundled\n Name example-bundled\n Version 0.1.0\n Status enabled"`), requiring callers to scrape column-aligned text to extract the post-mutation state. A no-op mutation emitting `reload_runtime:true` forces orchestration to trigger an expensive runtime reload even when no config change occurred. **Required fix shape:** (a) add `changed:bool` so callers can skip runtime reload when `changed:false`; (b) add `previous_status` and `status` fields (enums: `enabled`/`disabled`) so pre/post state is machine-readable without parsing `message`; (c) add `version` and `source` fields at the mutation response level, consistent with `plugins list` entry shape; (d) emit `reload_runtime:false` when `changed:false`; (e) add regression coverage proving idempotent enable/disable sets `changed:false` and `reload_runtime:false`. **Why this matters:** plugin lifecycle is a hot path for automation that conditionally enables plugins before running sessions. If every enable emits `reload_runtime:true` and no `changed` field exists, orchestration must reload unconditionally or maintain external state — both brittle patterns. Source: Jobdori live dogfood, `e939777f`, 2026-04-30.
|
||||||
|
|
||||||
|
|
||||||
412. **`bootstrap-plan --output-format json` returns `phases: string[]` of raw Rust enum variant names with no description, steps, duration, or dependency metadata — unusable by automation** — dogfooded 2026-04-30 by Jobdori on `e939777f`. Running `claw bootstrap-plan --output-format json` returns `{"kind":"bootstrap-plan","phases":["CliEntry","FastPathVersion","StartupProfiler","SystemPromptFastPath","ChromeMcpFastPath","DaemonWorkerFastPath","BridgeFastPath","DaemonFastPath","BackgroundSessionFastPath","TemplateFastPath","EnvironmentRunnerFastPath","MainRuntime"]}`. The envelope has only two keys: `kind` and `phases`. The `phases` array contains 12 raw Rust enum variant name strings — opaque identifiers with no `description`, no `label`, no `steps[]`, no `estimated_ms`, no `dependencies[]`, no `optional:bool`, and no `status` (enabled/disabled/skipped). Automation that calls `bootstrap-plan` to understand startup costs or profile initialization paths receives 12 name strings that reveal nothing about what each phase does, how long it takes, whether it depends on credentials/network/MCP, or which ones can be skipped. **Required fix shape:** (a) replace `phases: string[]` with `phases: [{id, label, description, optional, estimated_ms?, dependencies?, status?}]`; (b) add a top-level `total_phases` count; (c) mark network/credential-dependent phases with a `requires_auth:bool` or `deps:["network","credentials","mcp"]` field so automation can plan for unavailability; (d) add regression coverage proving each phase entry has at least `id`, `label`, and `description` fields and that the count matches the phases array length. **Why this matters:** bootstrap-plan is the startup-cost introspection surface. If its JSON output is 12 opaque variant name strings, automation cannot profile startup, identify slow phases, skip optional phases, or present meaningful startup diagnostics — the entire command serves only as a list of internal identifiers. Source: Jobdori live dogfood, `e939777f`, 2026-04-30.
|
412. **DONE — `bootstrap-plan --output-format json` returns `phases: string[]` of raw Rust enum variant names with no description, steps, duration, or dependency metadata — unusable by automation** — dogfooded 2026-04-30 by Jobdori on `e939777f`. Running `claw bootstrap-plan --output-format json` returns `{"kind":"bootstrap-plan","phases":["CliEntry","FastPathVersion","StartupProfiler","SystemPromptFastPath","ChromeMcpFastPath","DaemonWorkerFastPath","BridgeFastPath","DaemonFastPath","BackgroundSessionFastPath","TemplateFastPath","EnvironmentRunnerFastPath","MainRuntime"]}`. The envelope has only two keys: `kind` and `phases`. The `phases` array contains 12 raw Rust enum variant name strings — opaque identifiers with no `description`, no `label`, no `steps[]`, no `estimated_ms`, no `dependencies[]`, no `optional:bool`, and no `status` (enabled/disabled/skipped). Automation that calls `bootstrap-plan` to understand startup costs or profile initialization paths receives 12 name strings that reveal nothing about what each phase does, how long it takes, whether it depends on credentials/network/MCP, or which ones can be skipped. **Required fix shape:** (a) replace `phases: string[]` with `phases: [{id, label, description, optional, estimated_ms?, dependencies?, status?}]`; (b) add a top-level `total_phases` count; (c) mark network/credential-dependent phases with a `requires_auth:bool` or `deps:["network","credentials","mcp"]` field so automation can plan for unavailability; (d) add regression coverage proving each phase entry has at least `id`, `label`, and `description` fields and that the count matches the phases array length. **Why this matters:** bootstrap-plan is the startup-cost introspection surface. If its JSON output is 12 opaque variant name strings, automation cannot profile startup, identify slow phases, skip optional phases, or present meaningful startup diagnostics — the entire command serves only as a list of internal identifiers. Source: Jobdori live dogfood, `e939777f`, 2026-04-30.
|
||||||
|
|
||||||
|
|
||||||
413. **DONE — ACP JSON no longer leaks tracking IDs** — verified 2026-06-04: `acp --output-format json` has no `tracking` or `discoverability_tracking` fields. Status is `not_implemented`.
|
413. **DONE — ACP JSON no longer leaks tracking IDs** — verified 2026-06-04: `acp --output-format json` has no `tracking` or `discoverability_tracking` fields. Status is `not_implemented`.
|
||||||
|
|||||||
@@ -4759,30 +4759,98 @@ fn build_rust_resolver_manifest(workspace_dir: &Path) -> Result<Value, Box<dyn s
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn print_bootstrap_plan(output_format: CliOutputFormat) -> Result<(), Box<dyn std::error::Error>> {
|
fn print_bootstrap_plan(output_format: CliOutputFormat) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let phases = runtime::BootstrapPlan::claude_code_default()
|
let phases = runtime::BootstrapPlan::claude_code_default();
|
||||||
.phases()
|
|
||||||
.iter()
|
|
||||||
.map(|phase| format!("{phase:?}"))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
match output_format {
|
match output_format {
|
||||||
CliOutputFormat::Text => {
|
CliOutputFormat::Text => {
|
||||||
for phase in &phases {
|
for phase in phases.phases() {
|
||||||
println!("- {phase}");
|
println!("- {phase:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CliOutputFormat::Json => println!(
|
CliOutputFormat::Json => {
|
||||||
"{}",
|
// #412: emit structured phase objects with label and description
|
||||||
serde_json::to_string_pretty(&json!({
|
let phase_objects: Vec<serde_json::Value> = phases
|
||||||
"kind": "bootstrap-plan",
|
.phases()
|
||||||
"action": "show",
|
.iter()
|
||||||
"status": "ok",
|
.enumerate()
|
||||||
"phases": phases,
|
.map(|(i, phase)| {
|
||||||
}))?
|
let (label, description) = bootstrap_phase_metadata(phase);
|
||||||
),
|
json!({
|
||||||
|
"id": format!("{phase:?}"),
|
||||||
|
"label": label,
|
||||||
|
"description": description,
|
||||||
|
"order": i,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string_pretty(&json!({
|
||||||
|
"kind": "bootstrap-plan",
|
||||||
|
"action": "show",
|
||||||
|
"status": "ok",
|
||||||
|
"total_phases": phases.phases().len(),
|
||||||
|
"phases": phase_objects,
|
||||||
|
}))?
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bootstrap_phase_metadata(phase: &runtime::BootstrapPhase) -> (&'static str, &'static str) {
|
||||||
|
use runtime::BootstrapPhase::*;
|
||||||
|
match phase {
|
||||||
|
CliEntry => (
|
||||||
|
"CLI Entry",
|
||||||
|
"Command-line argument parsing and global flag resolution",
|
||||||
|
),
|
||||||
|
FastPathVersion => (
|
||||||
|
"Fast-Path Version",
|
||||||
|
"Short-circuit version/help requests before full startup",
|
||||||
|
),
|
||||||
|
StartupProfiler => (
|
||||||
|
"Startup Profiler",
|
||||||
|
"Instrument startup timing for diagnostics",
|
||||||
|
),
|
||||||
|
SystemPromptFastPath => (
|
||||||
|
"System Prompt Fast-Path",
|
||||||
|
"Serve system-prompt requests without provider init",
|
||||||
|
),
|
||||||
|
ChromeMcpFastPath => (
|
||||||
|
"Chrome MCP Fast-Path",
|
||||||
|
"Serve Chrome MCP requests without full runtime",
|
||||||
|
),
|
||||||
|
DaemonWorkerFastPath => (
|
||||||
|
"Daemon Worker Fast-Path",
|
||||||
|
"Handle daemon worker requests without full init",
|
||||||
|
),
|
||||||
|
BridgeFastPath => (
|
||||||
|
"Bridge Fast-Path",
|
||||||
|
"Bridge/sibling process communication without full init",
|
||||||
|
),
|
||||||
|
DaemonFastPath => (
|
||||||
|
"Daemon Fast-Path",
|
||||||
|
"Daemon lifecycle management without full runtime",
|
||||||
|
),
|
||||||
|
BackgroundSessionFastPath => (
|
||||||
|
"Background Session Fast-Path",
|
||||||
|
"Resume/list background sessions without full init",
|
||||||
|
),
|
||||||
|
TemplateFastPath => (
|
||||||
|
"Template Fast-Path",
|
||||||
|
"Template rendering without full runtime",
|
||||||
|
),
|
||||||
|
EnvironmentRunnerFastPath => (
|
||||||
|
"Environment Runner Fast-Path",
|
||||||
|
"Environment/runner dispatch without full init",
|
||||||
|
),
|
||||||
|
MainRuntime => (
|
||||||
|
"Main Runtime",
|
||||||
|
"Full interactive REPL or one-shot prompt execution",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn print_system_prompt(
|
fn print_system_prompt(
|
||||||
cwd: PathBuf,
|
cwd: PathBuf,
|
||||||
date: String,
|
date: String,
|
||||||
|
|||||||
Reference in New Issue
Block a user