7.1 KiB
date, tasks, title, status, target_version, branch
| date | tasks | title | status | target_version | branch | |||
|---|---|---|---|---|---|---|---|---|
| 2026-05-07 |
|
Agent UX improvements: ingest log consistency + invocation flags + schema list sync | approved | 0.3.3 | feat/p9-fb-26-fb-28-agent-ux |
Agent UX improvements
Three bundled changes shipped as one PR: CLAUDE.md wire schema list sync (doc-only), ingest log consistency fix (fb-26), and agent invocation flags (fb-28).
§1 — CLAUDE.md wire schema list sync
Problem
CLAUDE.md §Wire schema v1 lists schemas that do not exist on disk, and omits schemas that do.
| Schema | CLAUDE.md | docs/wire-schema/v1/ |
|---|---|---|
eval_run.v1 |
listed | missing |
eval_compare.v1 |
listed | missing |
list_docs.v1 |
listed | missing |
chunk_inspection.v1 |
absent | present |
citation.v1 |
absent | present |
doc_summary.v1 |
absent | present |
Fix
Update the schema list in CLAUDE.md to match docs/wire-schema/v1/ exactly. No other changes.
Correct list: ingest_report.v1, ingest_progress.v1, search_hit.v1, answer.v1, doctor.v1, reset_report.v1, schema.v1, error.v1, chunk_inspection.v1, citation.v1, doc_summary.v1
§2 — fb-26: Ingest log consistency
Problem
crates/kebab-cli/src/progress.rs has two bugs that break the TTY/non-TTY symmetry:
Abortedhandler (L170-188):writeln!is unconditional — fires in TTY mode too, printing a duplicate summary below the spinner's abandoned message.CompletedTTY path (L153-169):bar.finish_and_clear()clears the bar with no subsequent summary line. Users see the run end silently.
Additionally, there is no escape hatch for CI environments that emulate a TTY (pty wrapper), which causes unintended spinner output in CI logs.
Design
Behavioral contract (Option A — already the intent, bug-fixed):
| Mode | Progress | Final summary |
|---|---|---|
| TTY | indicatif in-place spinner → progress bar | single ingest: complete / aborted writeln after bar clears |
| non-TTY | append-only writeln per event | same ingest: complete / aborted writeln |
--json |
silent stderr | ingest_report.v1 stdout only |
Changes to handle_human:
CompletedTTY: afterbar.finish_and_clear(), addwriteln!(stderr, "ingest: complete (...)")— same format as non-TTY branch.AbortedTTY: wrap the existing unconditionalwriteln!inif !tty { ... }. Thebar.abandon_with_message(...)already prints the spinner's final state on TTY.- Unify summary format string:
ingest: complete (scanned={} new={} updated={} skipped={} errors={})andingest: aborted (...)— identical prefix in both modes.
KEBAB_PROGRESS=plain env override:
- When set (any non-empty value), force non-TTY branch regardless of
IsTerminal. - Implemented in
ProgressMode::from_flags— checkKEBAB_PROGRESS=plainenv, settty=falsewhen present. - Allows CI with pty wrappers to opt-in to append-only output explicitly.
Testing
- Snapshot test: non-TTY stream for a minimal ingest (2-file TempDir KB) captures
ScanStarted,ScanCompleted,AssetStarted × 2,Completedwith correct prefixes. KEBAB_PROGRESS=plainenv: TTY path still uses append-only output.KEBAB_PROGRESS=plain+--json:--jsontakes precedence, no human lines.- Manual smoke:
kebab ingest --config /tmp/... 2>&1 | catshows all event lines + final summary.
§3 — fb-28: Agent invocation flags
Problem
Agents invoking kebab face two issues:
- No way to enforce read-only KB access — a hallucinating agent could call
kebab resetorkebab ingestunexpectedly. - Progress/spinner output leaks to stderr even in non-TTY agent invocations where TTY is emulated, adding noise to agent context.
Design
Global flags on Cli
kebab [--readonly] [--quiet] <subcommand> [...]
Both are global flags added to the Cli struct in main.rs. Evaluated before subcommand dispatch.
--readonly / KEBAB_READONLY=1
- Environment variable
KEBAB_READONLY=1is equivalent to passing--flag(checked inmainbefore dispatch; env wins if set). - Blocked subcommands:
ingest,ingest-file,ingest-stdin,reset(all write-path commands). (nukedoes not exist as a subcommand.) - Allowed:
search,ask,doctor,schema,mcp,tui(read-path). - On block: exit code 1 + error output:
--jsonmode:error.v1ndjson to stderr (code: "readonly_mode",message: "kebab: readonly mode — mutating commands are disabled")- plain mode: single
kebab: readonly mode — mutating commands are disabled\nto stderr
- Implementation:
fn is_mutating(cmd: &Cmd) -> bool+ guard block inmain()after flag parsing, beforematch cli.cmd.
--quiet
- Suppresses all human-readable stderr output: progress lines, hint messages.
- Does not suppress
error.v1ndjson (in--jsonmode) or plain error text — errors always reach stderr. --jsonflag automatically implies--quietbehavior (already the case in practice; this makes it explicit and documented).- Implementation: extend
ProgressMode::Human { tty: bool }→Human { tty: bool, quiet: bool }. UpdateProgressMode::from_flags(json: bool, quiet: bool, plain_env: bool) -> Self. Whenquiet=true(or--json),Human { tty: _, quiet: true }overrides draw target tohiddenand skips allwriteln!(stderr, ...)calls in non-TTY branch.ProgressDisplay::new(mode: ProgressMode)signature unchanged. --quietwithout--jsonstill emitsingest_report.v1to stdout at end (not suppressed).
New error.v1 code
Construct ErrorV1 { code: "readonly_mode", ... } directly in the guard block in main.rs — no change to classify() (which dispatches on anyhow::Error types, not user-triggered state). Document the new code in tasks/HOTFIXES.md.
Testing
kebab --readonly ingest→ exit 1, error message contains "readonly mode".kebab --readonly ingest --json→ exit 1, stderr containserror.v1withcode: "readonly_mode".KEBAB_READONLY=1 kebab ingest→ same as--readonly.kebab --readonly search "q"→ passes through normally.kebab --quiet ingest→ stderr silent during run,ingest_report.v1still on stdout.kebab ingest --json→ no human lines on stderr (auto-quiet behavior documented).
Bundling rationale
All three changes are small and independent — no shared code paths. Bundled into one branch to avoid PR noise for minor UX polish. The CLAUDE.md fix is doc-only and safe to merge first if needed.
Files changed (expected)
| File | Change |
|---|---|
CLAUDE.md |
schema list update |
crates/kebab-cli/src/main.rs |
--readonly, --quiet global flags + guard block |
crates/kebab-cli/src/progress.rs |
Aborted/Completed bug fix, KEBAB_PROGRESS=plain, quiet threading |
crates/kebab-app/src/error_wire.rs |
"readonly_mode" code |
tasks/HOTFIXES.md |
new entry for readonly_mode error code |
tasks/p9/p9-fb-26-ingest-log-consistency.md |
status → merged |
tasks/p9/p9-fb-28-agent-invocation-flags.md |
status → merged |
tasks/INDEX.md |
status update |
HANDOFF.md |
one-liner |