{
  "shell.native_background": {
    "score": 1,
    "evidence": "bin 2.1.311@229600845 Bash result: `Command running in background with ID: <id>. Output is being written to: <path>.` Bash schema @226532951: `Set to true to run this command in the background`; background wall clock 30 min default / 2 h max (`var mYs=1800000,BWr=7200000` @226150330). The task id is returned to the model, not a shell `&` escape hatch."
  },
  "shell.timeout_cap": {
    "score": 1,
    "evidence": "bin 2.1.311@26150072: `var a=120000,s=600000; function iue(){...BASH_DEFAULT_TIMEOUT_MS...} function QDe(){...BASH_MAX_TIMEOUT_MS...return Math.max(s,iue(n))}`; the `timeout` input is clamped by `gYs/hYs`. Default 120000 ms, hard max 600000 ms, model-settable."
  },
  "shell.persistent": {
    "score": 0.5,
    "evidence": "bin 2.1.311@222476783: `Your working directory persists between calls... Shell state, such as env vars and functions, does not persist - the shell has been initialized from the user's profile.` The cwd survives; env vars and functions do not (also @228050305 `cd carries to the next call only in the main session`). Partial."
  },
  "shell.powershell": {
    "score": 0.5,
    "evidence": "bin 2.1.311 has a real PowerShell tool (`var At=\"PowerShell\"`, own prompt, parser `K0e`, read-only classifier) but the gate @217289496 is `yv(){let e=CLAUDE_CODE_USE_POWERSHELL_TOOL; if(O()!==\"windows\") return e===true; if(e!==void 0) return e; ...}`: default-on only on Windows, off by default on this audited macOS install (needs the env flag)."
  },
  "shell.middle_out": {
    "score": 0,
    "evidence": "No middle-out. bin 2.1.311@266693006: `(output truncated: the command printed <bytes>; only the start was returned... The first <64 KiB> of the command's output are in the file <path>)`; the generic persist path @224943060 `Output too large (...)` + @224943280 `Preview (first 2KB)`. Head only; the tail is never kept."
  },
  "shell.full_log": {
    "score": 1,
    "evidence": "bin 2.1.311@266693006 returns `persistedOutputPath` in the result (`... a saved copy ... in the file <path>. If the file is gone, run the command again ...`); @217948690 settings text: `Output past this is saved to a file and Claude receives a short preview plus the path.` Default `bashOutputMaxChars` is 30000."
  },
  "shell.omitted_counts": {
    "score": 0.5,
    "evidence": "bin 2.1.311@266693006 states the total size (`the command printed <bytes>`) and gives the file path, and the inline preview is bounded (first 2000 bytes / first 64 KiB), but it never states how much was omitted; the model must subtract. @224943280 `Preview (first 2KB)` has the same gap."
  },
  "shell.signal_honesty": {
    "score": 0.5,
    "evidence": "bin 2.1.311@226835140 `var v3=137,Avt=143`; the kill path records the code (`#E(e??v3)` @226841430) and @229612035 maps `if(n===143||n===137)return \"exit_killed\"`, so 128+N is modelled for background tasks. But no 128+signal conversion exists in the foreground Bash result path (every `128+` literal in the binary is CLI relaunch/plugin code) and the model-facing line is `Exit code <code>` @231329065 from the child exit code, so a signal death can surface as a null/raw code. Partial."
  },
  "shell.benign_exit": {
    "score": 1,
    "evidence": "bin 2.1.311@228003260 `Cys=new Map([[\"grep\",HM(\"No matches found\")],[\"rg\",...],[\"egrep\",...],[\"fgrep\",...],[\"find\",HM(\"Some directories were inaccessible\")],[\"diff\",HM(\"Files differ\")],[\"test\",HM(\"Condition is false\")],[\"[\" ...]])` with `HM=(e)=>(n,r,s)=>({isError:n>=2,message:n===1?e:void 0})`; git diff/grep handled too. Exit 1 is annotated instead of treated as failure."
  },
  "shell.untrusted_fencing": {
    "score": 0,
    "evidence": "No fencing of process output. Searched for `<bash_stdout>`, `BEGIN BASH`, and `untrusted` near shell output: none. `untrusted data` fences exist only for artifact HTML (`<cowritten-artifact-html>`) and web content; `<bash-input>` is the user's `!` command echo, not model-run output. Bash stdout/stderr are returned as plain text."
  },
  "shell.bg_output_reader": {
    "score": 1,
    "evidence": "bin 2.1.311@229600845 `... Output is being written to: <path>.` and @226727863 `use Read on that path for interim output` (`too(){return ' Output is being written to <path>; use Read on that path for interim output.'}`). The dedicated BashOutput/TaskOutput tool was removed (@217948999 `the TaskOutput tool was removed. Read a background task's output file with the Read tool instead`), so reading is via Read on the returned file."
  },
  "shell.wait_modes": {
    "score": 0.5,
    "evidence": "Return-now = `run_in_background` (@226532951). Wait-for-exit is push (`You will be notified when it completes` @229601529) or a documented foreground `until` loop; wait-for-new-output is the separate Monitor tool. There is no wait-mode parameter on a task tool (TaskOutput removed @217948999), so the three modes do not exist on one API. Partial."
  },
  "shell.cursor_reads": {
    "score": 0,
    "evidence": "No byte cursor. The task-output reader was removed (bin 2.1.311@217948999) and `Read` on the `*.output` file takes line `offset`/`limit`, not a per-job byte cursor that guarantees no byte is repeated. `getTaskOutputDelta` exists (@223241337) but is internal file-task plumbing, not a model-facing tool."
  },
  "shell.list_tasks": {
    "score": 0,
    "evidence": "No model-facing list of live shell jobs. Registered names include `TaskStop`, `TaskList` (a team to-do list, not jobs) and `ListAgents`; grep for `list background tasks` = 0. `/tasks` is a UI command, not a tool. Not found."
  },
  "shell.kill_id": {
    "score": 1,
    "evidence": "bin 2.1.311@235300972 `Stop a running background task by ID`; schema @235300029 `task_id: The ID of the background task to stop... shell_id: Deprecated: use task_id instead`; alias map @217224532 `KillShell:\"TaskStop\", KillBash:\"TaskStop\"`."
  },
  "shell.kill_pid_port": {
    "score": 0,
    "evidence": "TaskStop input is only `{task_id, shell_id}` (@235300029); no pid, pgid or port parameter, and no other kill tool. Searched for a port-kill tool (lsof/port) in the registry: none. Not found."
  },
  "shell.sigterm_escalation": {
    "score": 1,
    "evidence": "bin 2.1.311@219952199 `async function Vbe(e,t,n=\"SIGTERM\"){... process.kill(-e,n) ... setTimeout(...{ if(...) return; process.kill(-r,\"SIGKILL\") ...},5000,e,t) ...}` - SIGTERM, then SIGKILL after 5 s if the process is still there."
  },
  "shell.group_kill": {
    "score": 1,
    "evidence": "bin 2.1.311@219952199 `Vbe` refuses `e<=1`, calls `f(e,t)` which compares the target's recorded start time (`Kbe`) and returns early on `other`, and only then does `process.kill(-e,n)`; the fallback `eN/qw` also refuses pids <=1 (@220427514). It does not signal an unverified process group."
  },
  "shell.monitor": {
    "score": 1,
    "evidence": "bin 2.1.311@226365589 Monitor tool prompt: `Start a background monitor that streams events from a long-running script. Each stdout line is an event - you keep working and notifications arrive in the chat.` Registered as `MonitorTool`, tool name `var ti=\"Monitor\"` (@222354303), and enabled by default on non-Windows (`Ad(){if(O()!==\"windows\") return true; ...}` @222497827). The Bash prompt tells the model to use it instead of sleeping."
  },
  "shell.sleep": {
    "score": 0,
    "evidence": "No Sleep tool: grep for `=\"Sleep\"` / `name:\"Sleep\"` = 0. The Bash prompt blocks sleeping (`Long leading sleep commands are blocked` @228047615) and points to Monitor until-loops. `ScheduleWakeup` is a cron-style self-wake for /loop, not a park that wakes early on user input. Not found."
  },
  "shell.cron": {
    "score": 1,
    "evidence": "bin 2.1.311@226143314 `CronCreate/CronDelete/CronList`; durable flag @226144023 `true = persist to .claude/scheduled_tasks.json and survive restarts`; @226147092 `Durable jobs persist to .claude/scheduled_tasks.json and survive session restarts - on next launch they resume automatically.` Gates `tengu_kairos_cron` and `tengu_kairos_cron_durable` default true."
  },
  "shell.bg_subagents": {
    "score": 1,
    "evidence": "bin 2.1.311@229779277 `When an agent runs in the background, you will be automatically notified when it completes - do NOT sleep, poll, or proactively check on its progress.` The Agent tool (Task alias) takes `run_in_background` (@227153467) and results arrive as task notifications; SendMessage can ask a running agent (@226057589 ListAgents). No explicit wait tool since TaskOutput was removed."
  },
  "shell.safe_classification": {
    "score": 1,
    "evidence": "bin 2.1.311@222750306 `Shell keyword '...' as command name - tree-sitter mis-parse`; the parsed command object carries `treeSitterAnalysis`, `pipePositions` and `redirectionNodes` (@227603536) and drives the Bash permission/read-only matcher; PowerShell has its own parser `K0e`. Syntax/AST based, not a token allowlist."
  },
  "shell.workspace_boundary": {
    "score": 1,
    "evidence": "bin 2.1.311@217750379 permission reasons `Path is outside allowed working directories` (`OUTSIDE_WORKING_DIRECTORIES_ASK_REASON`, gated as an ask) and @217750546 setting `blockReadsOutsideWorkingDirectories` for a hard block (default off). Paths are extracted from commands and checked, so outside paths are detected and gated rather than silently run."
  },
  "read.line_window": {
    "score": 1,
    "evidence": "bin 2.1.311@22485538 prompt `By default, it reads up to <Zzt> lines starting from the beginning of the file` with `Zzt=2000` (@222485031); input schema has `offset`/`limit` (@229657539)."
  },
  "read.second_ceiling": {
    "score": 1,
    "evidence": "bin 2.1.311@221019619 `File content (<e> tokens) exceeds maximum allowed tokens (<t>). Use offset and limit parameters...`; defaults `J8o=25000` tokens (@221021137) and `Sxt=262144` bytes max file size. Two ceilings besides the 2000-line window."
  },
  "read.per_line_clamp": {
    "score": 0,
    "evidence": "No per-line clamp, and the tool is built around its absence: bin 2.1.311@229685565 `[Truncated: PARTIAL view - ... showing the first N of M characters ...; this file has very long lines and cannot be paginated by line. Use Grep ... or Read with offset/limit ...]`. Long lines are truncated only as part of the whole-window/token budget, not clamped per line."
  },
  "read.line_prefixes": {
    "score": 1,
    "evidence": "bin 2.1.311@222485092 prompt `Results are returned using cat -n format, with line numbers starting at 1`; formatter `reo(e,n,r)` emits `<n><sep><s>` with `n` starting at `startLine` (1-based) and a tab or colon separator."
  },
  "read.resume_offset": {
    "score": 1,
    "evidence": "bin 2.1.311@229685279 `Call Read with offset=<mn+1> limit=<mn> for the next page` inside `[Truncated: PARTIAL view - <file>: showing lines 1-N of M total ...]`. The next offset is named; no arithmetic."
  },
  "read.empty_file_note": {
    "score": 1,
    "evidence": "bin 2.1.311@229671196 `<system-reminder>Warning: the file exists but the contents are empty.</system-reminder>` - explicitly distinguishable from a broken tool."
  },
  "read.eof_note": {
    "score": 1,
    "evidence": "bin 2.1.311@229671313 `<system-reminder>Warning: the file exists but is shorter than the provided offset (N). The file has M lines.</system-reminder>` - a note, not an Error."
  },
  "read.streaming": {
    "score": 1,
    "evidence": "bin 2.1.311 read path `U_e` reads small files whole but routes larger files to `Ir(...)` with `hardCapBytes`, `maxBytes`, partial-chunk decoding and `this.stream.destroy(new F6e(...))` on overflow (@224431518), so large reads stream and memory is bounded."
  },
  "read.deferred_chunk": {
    "score": 1,
    "evidence": "The 'more remains' page note is only emitted when a limit was actually hit (`truncatedByTokenCap` / `truncatedByBytes`) and requires `mn < dt`, where `dt` is the total line count the reader computed (bin 2.1.311@229685279). It does not claim more remains before knowing."
  },
  "read.dedup": {
    "score": 1,
    "evidence": "bin 2.1.311@229673205: on `wt.offset===n && wt.limit===r` and an unchanged mtime it returns `{type:\"file_unchanged\"}`, rendered as `File unchanged since last read. The content from the earlier Read tool_result in this conversation is still current - refer to that instead of re-reading.` (@222484415). Caveat: the ledger entry is not consumed; repeated identical reads keep returning the stub until the file changes."
  },
  "read.ledger": {
    "score": 1,
    "evidence": "bin 2.1.311@219324557 Write guard message `File has not been read yet. Read it first before writing to it.`; `h1o` checks the stored read timestamp against the current mtime and throws `File content has changed since it was last read...` (@227966483). Writes consult what the model has seen."
  },
  "read.partial_ledger": {
    "score": 1,
    "evidence": "bin 2.1.311@227966483 `async function h1o({...lastRead:s,...}){ if(!s||s.isPartialView){ ... throw new Ree(ckn) }` - a partial view is recorded by Read (`isPartialView:!0` @229685845) and refuses the overwrite with ckn = `File has not been read yet...`."
  },
  "read.did_you_mean": {
    "score": 1,
    "evidence": "bin 2.1.311@229674067 on ENOENT: `File does not exist. <note>.` + `Did you mean <name>?` where `vme`/`BUe` (findSimilarFile) scans the directory for a close name (NFC-normalised compare). A miss suggests close filenames."
  },
  "read.unicode_retry": {
    "score": 0.5,
    "evidence": "bin 2.1.311@229657114 `function B7e(e){ ... /^(.+)([ \\u202F])(AM|PM)(\\.png)$/ ... }` is retried first on ENOENT (retry uses `B7e(ht)` @229674067); that covers only the narrow no-break-space spelling in macOS screenshot names. No general NFC/NFD filename retry. Partial."
  },
  "read.confusables": {
    "score": 0,
    "evidence": "No zero-width/bidi disclosure in file content. Searched `bidi`, `zero-width`, `U+200B`, `invisible character` near Read: the hits are plugin-name/session-title input validators and path validation, not a content warning; Read returns content verbatim. Not found."
  },
  "read.image_vision": {
    "score": 1,
    "evidence": "bin 2.1.311@22485538 prompt `This tool allows Claude Code to read images (eg PNG, JPG, etc). When reading an image file the contents are presented visually`; result @229670080 maps to `{type:\"image\", source:{type:\"base64\", data:..., media_type:...}}`."
  },
  "read.coord_scale": {
    "score": 1,
    "evidence": "bin 2.1.311@235269131: `<n> images were sent to the chat as scaled-down copies, because the server accepts images at most <N> pixels wide or tall: <path>: sent at W x H pixels; the original (origW x origH) ...` - the scale and original dimensions are disclosed."
  },
  "read.magic_bytes": {
    "score": 0,
    "evidence": "Routing is by extension, not bytes: `s$e(t)` returns `wkn.has(ext)||ext===\"pdf\"` for png/jpg/jpeg/gif/webp/pdf (@220031850), and every other binary extension is refused with `This tool cannot read binary files. The file appears to be a binary .docx file...` (@229668688). Magic sniffing only picks the image media type after extension routing, so a .png of text is still treated as an image."
  },
  "read.notebooks": {
    "score": 1,
    "evidence": "bin 2.1.311@22485538 prompt: notebooks come back `as cells with outputs`; the result carries `cellType`, `cell_id`, `language`, `execution_count`, `outputs` (@229662683) and an oversized output is replaced with `Outputs are too large to include. Use Bash with: cat ... | jq '.cells[N].outputs'` (@229651239)."
  },
  "read.docx": {
    "score": 0,
    "evidence": "No Word rendering. The binary extension blocklist (`hLe`) includes .docx and Read refuses it before I/O: `This tool cannot read binary files. The file appears to be a binary .docx file. Use a skill for this file type...` (bin 2.1.311@229668688); the byte sniffer only names `ZIP archive (Office documents such as .pptx/.docx/.xlsx are ZIPs)` (@222196768). No OOXML parser in the binary."
  },
  "read.pptx": {
    "score": 0,
    "evidence": "No PowerPoint rendering and no speaker notes. `.pptx` is in the `hLe` binary blocklist and Read refuses it with `This tool cannot read binary files. The file appears to be a binary .pptx file...` (bin 2.1.311@229668688); the sniffer only says the file is a ZIP (@222196768). No slide/notes parser in the binary."
  },
  "read.xlsx": {
    "score": 0,
    "evidence": "No spreadsheet rendering. `.xlsx`/`.xls` are in the `hLe` binary blocklist and Read refuses them with `This tool cannot read binary files...` (bin 2.1.311@229668688); the sniffer only reports a ZIP (@222196768). No workbook parser in the binary."
  },
  "read.odf": {
    "score": 0,
    "evidence": "No OpenDocument rendering. `.odt`/`.ods`/`.odp` are in the `hLe` binary blocklist and Read refuses them with `This tool cannot read binary files...` (bin 2.1.311@229668688). No ODF parser in the binary."
  },
  "read.rtf_epub": {
    "score": 0,
    "evidence": "No RTF or EPUB conversion. `.epub` is in the `hLe` binary blocklist and is refused (@229668688). `.rtf` is not converted either: it is treated as a text-like extension and the raw control-word markup is returned, so the text is not readable as text. Not found."
  },
  "read.pdf_text": {
    "score": 0.5,
    "evidence": "No text extraction. `case\"pdf\"` returns a short text line plus a `{type:\"document\", source:{type:\"base64\", media_type:\"application/pdf\", data:...}}` block (bin 2.1.311@229670080); page extraction renders JPEGs via `pdftoppm -jpeg -r 100` (@218400095). The model reads the PDF text layer from the attached document, but the harness never extracts text. Partial."
  },
  "read.scanned_pdf": {
    "score": 0.5,
    "evidence": "A scan is not identified and no recovery command is printed. A text-less PDF is served as a document block or its pages are rendered to images (`PDF pages extracted: N page(s) from ...` @229670192); poppler install advice appears only when rendering is unavailable (`pdftoppm is not installed. Install poppler-utils...` @218399748). Partial."
  },
  "read.docs_windowed": {
    "score": 0.5,
    "evidence": "Documents are windowed by page, not by the line window: the Read schema has `pages` for PDFs (e.g. \"1-5\", max 20 pages/request) alongside `offset`/`limit` for text (bin 2.1.311@229657539). Office documents cannot be read at all. Partial."
  },
  "read.docs_bulk": {
    "score": 0,
    "evidence": "Read takes one `file_path`; documents do not participate in a multi-file read. The only bulk path is a separate Glob/Grep call. Not found."
  },
  "read.converter_on_demand": {
    "score": 0.5,
    "evidence": "The harness starts with no converter and fetches none on first use; PDF page rendering needs a user-installed poppler: bin 2.1.311@218399748 `pdftoppm is not installed. Install poppler-utils (e.g. brew install poppler or apt-get install poppler-utils) to enable PDF page rendering.` Startup requirement met; fetch-on-first-use not implemented. Partial."
  },
  "read.many_files": {
    "score": 0,
    "evidence": "Read input schema is `{file_path, offset, limit, pages, allow_large}` (bin 2.1.311@229657539): one file per call. No `paths` array. Bulk reads are a separate Glob/Grep tool."
  },
  "read.globs": {
    "score": 0,
    "evidence": "No glob expansion in Read: `file_path` is `The absolute path to the file to read` (bin 2.1.311@229657539) and a glob is not expanded. Glob is a separate tool."
  },
  "read.per_file_range": {
    "score": 0,
    "evidence": "No per-entry ranges: one `file_path` with one `offset`/`limit` pair (bin 2.1.311@229657539). Not found."
  },
  "read.exclusions_gitignore": {
    "score": 0,
    "evidence": "No excludes or .gitignore handling in Read; it reads the single explicit path. The ripgrep-based Grep/Glob tools honour .gitignore, but that is not the read tool. Not found."
  },
  "read.aggregate_cap": {
    "score": 0,
    "evidence": "No aggregate budget across files: only one file is read per call, and the 25k-token / 256 KiB cap (`J8o`, `Sxt`, bin 2.1.311@221021137) applies to that one read. Not found."
  },
  "read.aliased_input": {
    "score": 0.5,
    "evidence": "bin 2.1.311@229654843 `coerceInput: cNr` repairs one-element arrays for offset/limit, numeric strings (`$7e`), `length` -> `limit`, deletes a negative `offset` and an extra `description`. No `path`->`file_path` or other alias spellings, so only common coercions are repaired. Partial."
  },
  "read.negative_offset": {
    "score": 0,
    "evidence": "A negative offset is dropped, not used as a tail read: bin 2.1.311@229654843 `if(s!==void 0&&s<0) delete n.offset, r.push(\"offset_neg\")`, so the read starts at line 1 (schema is `E().int().nonnegative()` @229657539). Not found."
  },
  "read.device_blocklist": {
    "score": 1,
    "evidence": "bin 2.1.311@219084541 `var H=new Set([\"/dev/zero\",\"/dev/random\",\"/dev/urandom\",\"/dev/full\",\"/dev/stdin\",\"/dev/tty\",\"/dev/console\",\"/proc/kmsg\",\"/dev/stdout\",\"/dev/stderr\",\"/dev/fd/0\",\"/dev/fd/1\",\"/dev/fd/2\"])`; @219084318 `Cannot read '...': this path names a device or process file that this tool does not read.` Refused before I/O."
  }
}
