mirror of
https://github.com/instructkr/claw-code.git
synced 2026-06-05 14:07:11 +08:00
fix: filter boundary sentinel from system-prompt sections JSON
__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__ is now filtered from the sections array in JSON output. boundary_index field exposed for callers that need the static/dynamic split point. Generated with https://github.com/Yeachan-Heo/gajae-code Co-authored-by: Gajae Code <dev@gajae-code.com>
This commit is contained in:
@@ -6326,10 +6326,10 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed)
|
|||||||
415. **`config <section> --output-format json` returns `merged_keys:int` (a count) with no actual merged key-value pairs — automation cannot read the resolved configuration values from JSON** — dogfooded 2026-04-30 by Jobdori on `e939777f`. Running `claw config env --output-format json`, `claw config model --output-format json`, or `claw config hooks --output-format json` all return an identical five-key envelope: `{"cwd":"...","files":[...],"kind":"config","loaded_files":2,"merged_keys":1}`. The `merged_keys` field is an integer count of how many keys were merged across the loaded files, not an object or array of the actual key names and resolved values. The `files` array shows which config files were loaded/missing but contains no per-file key-value content. The merged section content — the actual resolved `env`, `model`, or `hooks` configuration — is entirely absent from the JSON output. It only appears in the prose output as a "Merged section: env / <value>" block. **Required fix shape:** (a) add a `merged` or `resolved` object/array field to the JSON envelope containing the actual key-value pairs that resulted from merging the loaded config files for the requested section; (b) rename `merged_keys` from an integer count to either remove it (derivable from `len(merged)`) or keep it as a companion count field; (c) for each entry in `merged`, include `key`, `value`, and optionally `source_file` so automation can attribute which file contributed the value; (d) add regression coverage proving `config env --output-format json` with a non-empty env section populates `merged` (or equivalent) with the actual resolved key-value pairs. **Why this matters:** the entire purpose of `config env/model/hooks --output-format json` is to allow automation to read the resolved runtime configuration without screen-scraping prose. Returning only a count defeats the purpose and forces callers to either re-parse the prose output or re-read and merge the source config files themselves. Source: Jobdori live dogfood, `e939777f`, 2026-04-30.
|
415. **`config <section> --output-format json` returns `merged_keys:int` (a count) with no actual merged key-value pairs — automation cannot read the resolved configuration values from JSON** — dogfooded 2026-04-30 by Jobdori on `e939777f`. Running `claw config env --output-format json`, `claw config model --output-format json`, or `claw config hooks --output-format json` all return an identical five-key envelope: `{"cwd":"...","files":[...],"kind":"config","loaded_files":2,"merged_keys":1}`. The `merged_keys` field is an integer count of how many keys were merged across the loaded files, not an object or array of the actual key names and resolved values. The `files` array shows which config files were loaded/missing but contains no per-file key-value content. The merged section content — the actual resolved `env`, `model`, or `hooks` configuration — is entirely absent from the JSON output. It only appears in the prose output as a "Merged section: env / <value>" block. **Required fix shape:** (a) add a `merged` or `resolved` object/array field to the JSON envelope containing the actual key-value pairs that resulted from merging the loaded config files for the requested section; (b) rename `merged_keys` from an integer count to either remove it (derivable from `len(merged)`) or keep it as a companion count field; (c) for each entry in `merged`, include `key`, `value`, and optionally `source_file` so automation can attribute which file contributed the value; (d) add regression coverage proving `config env --output-format json` with a non-empty env section populates `merged` (or equivalent) with the actual resolved key-value pairs. **Why this matters:** the entire purpose of `config env/model/hooks --output-format json` is to allow automation to read the resolved runtime configuration without screen-scraping prose. Returning only a count defeats the purpose and forces callers to either re-parse the prose output or re-read and merge the source config files themselves. Source: Jobdori live dogfood, `e939777f`, 2026-04-30.
|
||||||
|
|
||||||
|
|
||||||
416. **`plugins list --output-format json` returns the mutation response shape with a prose `message` table instead of a structured `plugins:[]` array — `name`, `version`, `status`, `source` are embedded in `message` prose only** — dogfooded 2026-04-30 by Jobdori on `e939777f`. Running `claw plugins list --output-format json` returns `{"action":"list","kind":"plugin","message":"Plugins\n example-bundled v0.1.0 disabled\n sample-hooks v0.1.0 disabled","reload_runtime":false,"target":null}`. This is the same four-key response envelope used by `plugins enable` and `plugins disable` mutation commands, not a list envelope. The `message` field contains the full rendered prose table (plugin name, version, and status as whitespace-aligned columns), but no `plugins` array with structured per-entry objects. `target` is `null` because no specific plugin was targeted. The `reload_runtime:false` field is meaningless for a read-only list operation. **This is distinct from ROADMAP #411** which covers the mutation commands' own missing `changed`/`previous_status`/`version`/`source` fields — #416 targets the list command's structural mismatch: it uses the mutation envelope entirely instead of emitting a dedicated list schema. **Required fix shape:** (a) emit a distinct `{kind:"plugin_list", plugins:[{name, version, status, source, path?, description?}], count}` envelope for the `list` action; (b) omit `action`, `reload_runtime`, and `target` from list responses (mutation-only fields); (c) the `message` field should be absent or optional and must not be the sole machine-readable inventory surface; (d) add regression coverage proving `plugins list --output-format json` populates a `plugins` array with at least `name`, `version`, and `status` fields for each installed plugin. **Why this matters:** automation that calls `plugins list --output-format json` to discover installed plugin inventory receives only a whitespace-aligned prose table in a string field, with `reload_runtime:false` and `target:null` as the only other machine-readable signals — identical noise to what a failed enable command returns. Source: Jobdori live dogfood, `e939777f`, 2026-04-30.
|
416. **DONE — plugins list returns structured `plugins[]` array** — verified 2026-06-04: returns `{plugins:[{name,version,enabled,path,...}], summary:{total,enabled,disabled,load_failures}}`. No `reload_runtime` in list envelope.
|
||||||
|
|
||||||
|
|
||||||
418. **`system-prompt --output-format json` exposes `"__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__"` as a literal element in the `sections` array — an internal split delimiter leaked into the public structured output** — dogfooded 2026-04-30 by Jobdori on `e939777f`. Running `claw system-prompt --output-format json` returns `{"kind":"system-prompt","message":"<full prose>","sections":["You are an interactive agent...", "# System\n...", "# Doing tasks\n...", "# Executing actions with care\n...", "__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__", "# Environment context\n...", "# Project context\n...", "# Claude instructions\n...", "# Runtime config\n..."]}`. The `sections` array has 9 elements; element index 4 is the raw string `"__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__"`. This internal sentinel marks the boundary between the static and dynamic sections of the compiled system prompt, used during assembly to split the prompt at injection time. It appears in the public JSON output verbatim as a first-class section, indistinguishable from real sections by type alone. Automation that iterates `sections[]` must special-case this sentinel or it will process an internal implementation string as if it were a real system prompt section. **Required fix shape:** (a) strip `"__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__"` and any similar internal delimiters from the `sections` array before serializing to JSON; (b) if the static/dynamic boundary is semantically meaningful for callers, expose it as a structured metadata field such as `boundary_index:4` or as a `section_type:"static"|"dynamic"` field on each section entry, not as a raw sentinel string in the array; (c) rename the `sections` type from `string[]` to `[{id, type, content}]` to enable this without breaking the boundary signal; (d) add regression coverage proving the `system-prompt --output-format json` output's `sections` array contains no elements whose value equals `"__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__"` or matches `/__[A-Z_]+__/`. **Why this matters:** internal sentinel strings in public JSON are a contract liability — they couple the wire format to internal implementation details. Any refactor that renames or removes the sentinel breaks callers that don't special-case it, and automation that doesn't know to filter it will miscount, misparse, or misrender the system prompt. Source: Jobdori live dogfood, `e939777f`, 2026-04-30.
|
418. **DONE — system-prompt boundary sentinel filtered from sections** — fixed 2026-06-04: `__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__` filtered from `sections` array; `boundary_index:4` exposed as structured field for callers that need the static/dynamic split point.
|
||||||
|
|
||||||
|
|
||||||
419. **DONE — MCP unknown sub-actions return typed error with exit 1** — verified 2026-06-04: `mcp add` returns `{action:"error", error_kind:"unsupported_action", ok:false}` with exit 1.
|
419. **DONE — MCP unknown sub-actions return typed error with exit 1** — verified 2026-06-04: `mcp add` returns `{action:"error", error_kind:"unsupported_action", ok:false}` with exit 1.
|
||||||
|
|||||||
@@ -4691,6 +4691,16 @@ fn print_system_prompt(
|
|||||||
|
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
|
// #418: filter out the internal boundary sentinel from the sections array
|
||||||
|
// and expose the boundary index as a structured field.
|
||||||
|
let filtered_sections: Vec<&str> = sections
|
||||||
|
.iter()
|
||||||
|
.filter(|s| !s.contains("__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__"))
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.collect();
|
||||||
|
let boundary_index = sections
|
||||||
|
.iter()
|
||||||
|
.position(|s| s.contains("__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__"));
|
||||||
match output_format {
|
match output_format {
|
||||||
CliOutputFormat::Text => println!("{message}"),
|
CliOutputFormat::Text => println!("{message}"),
|
||||||
CliOutputFormat::Json => println!(
|
CliOutputFormat::Json => println!(
|
||||||
@@ -4700,13 +4710,15 @@ fn print_system_prompt(
|
|||||||
"action": "show",
|
"action": "show",
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"message": message,
|
"message": message,
|
||||||
"sections": sections,
|
"sections": filtered_sections,
|
||||||
|
"boundary_index": boundary_index,
|
||||||
"memory_file_count": memory_files.len(),
|
"memory_file_count": memory_files.len(),
|
||||||
"memory_files": memory_files_json(&memory_files),
|
"memory_files": memory_files_json(&memory_files),
|
||||||
}))?
|
}))?
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_version(output_format: CliOutputFormat) -> Result<(), Box<dyn std::error::Error>> {
|
fn print_version(output_format: CliOutputFormat) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|||||||
Reference in New Issue
Block a user