{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/philschmid-de/2026-09-01-how-foundational-models-became-superhuman-in-bash/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "If you have used coding agents, you may have noticed that they started to use Bash for tasks that would have traditionally used dedicated tools such as `read`, `edit`, and `search`. Well, models became superhuman in bash and might consider dedicated tools not good enough.\n\nEvery few months, I rebuild an [agent harness](https://www.philschmid.de/agent-harness-2026) from scratch to learn what changed and what I can delete. This time I removed every model-facing tool except for a `bash` and a `media` viewer.\n\nThe outcome matched what I had already seen in daily use. Task completion stayed in the same range, while the agent moved through complex work and verifications with fewer tool boundaries.\n\nFrontier models have become superhuman in Bash. I mean that in a narrow sense: they synthesize disposable command-line programs, in seconds, that most developers would need much longer to assemble and verify.\n\n## What “superhuman in Bash” means\n\nYou probably know the individual pieces `git`, `rg`, `jq`, Python, temporary files, process substitution, and test runners. Writing a one-off 40-line workflow that combines them under time pressure is different. Most of us work through the problem interactively, checking syntax and state between steps.\n\nCoding agents assemble these workflows from patterns learned across POSIX utilities, programming languages, build systems, and public source code. They still make mistakes. The difference is how much correct orchestration they can attempt at once.\n\nBash is the routing layer here. The heavy work may happen in Python, Git, SQLite, a compiler, or a project-specific CLI. One shell interface lets the agent compose all of them without forcing the runtime designer to predict every useful operation.\n\nThe following are simplified examples from my personal agent traces.\n\n### Example 1: In-place multi-file edits\n\nYou work on a project and you want to rename a variable across an implementation, its callers, and the tests.\n\nBash\n\n```bash\npython3 <<'PY'\nfrom pathlib import Path\n \nchanges = {\n    \"src/services/billing.py\": [\n        (\n            \"def charge_customer(customer_id: str, amount: int):\",\n            \"def charge_customer(customer_id: str, amount_cents: int):\",\n            1,\n        ),\n        (\n            \"client.charge(token, amount)\",\n            \"client.charge(token, amount_cents)\",\n            1,\n        ),\n    ],\n    \"src/api/checkout.py\": [\n        (\n            \"charge_customer(order.customer_id, order.total)\",\n            \"charge_customer(order.customer_id, order.total_cents)\",\n            1,\n        ),\n    ],\n    \"src/jobs/retry_payment.py\": [\n        (\n            \"charge_customer(job.customer_id, job.amount)\",\n            \"charge_customer(job.customer_id, job.amount_cents)\",\n            1,\n        ),\n    ],\n    \"tests/test_billing.py\": [\n        (\n            \"charge_customer(customer.id, 1200)\",\n            \"charge_customer(customer.id, amount_cents=1200)\",\n            2,\n        ),\n    ],\n}\n \nprepared = {}\nfor filename, replacements in changes.items():\n    path = Path(filename)\n    text = path.read_text()\n    for old, new, expected in replacements:\n        found = text.count(old)\n        if found != expected:\n            raise SystemExit(\n                f\"{filename}: expected {expected} occurrences of {old!r}, found {found}\"\n            )\n        text = text.replace(old, new)\n    prepared[path] = text\n \nfor path, text in prepared.items():\n    path.write_text(text)\nPY\n \nruff format src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py tests/test_billing.py\nruff check src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py tests/test_billing.py\npyright src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py\npytest -q tests/test_billing.py tests/test_checkout.py tests/test_retry_payment.py\ngit diff --check\ngit diff --stat -- src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py tests/test_billing.py\n```\n\nThe script first counts every old snippet. If a file has the wrong number of matches, it exits before writing anything. Only then does it apply the four-file rename in one pass, so you never sit in a half-updated tree. After the write it formats, lints, type-checks, runs the focused tests, and prints a compact diff. Failed checks stay in the working tree, so the next turn can inspect and fix them. That verify-before-done cycle is the [inner loop](https://www.philschmid.de/inner-loop-vs-outer-loop).\n\n### Example 2: Reproducing and bisecting a flaky regression\n\nYou have a test that fails only sometimes. You want the first commit that introduced the flake, without moving your current checkout.\n\nBash\n\n```bash\nset -euo pipefail\n \ntest -n \"${GOOD_REV:-}\"\nrepo=$(pwd -P)\nscratch=$(mktemp -d)\n \ncleanup() {\n  git -C \"$repo\" worktree remove --force \"$scratch/repo\" >/dev/null 2>&1 || true\n  rm -rf \"$scratch\"\n}\ntrap cleanup EXIT\n \ngit worktree add --detach \"$scratch/repo\" HEAD >/dev/null\ncd \"$scratch/repo\"\ngit bisect start HEAD \"$GOOD_REV\"\n \ngit bisect run bash -c '\n  failures=0\n  for seed in 11 29 47 71 101; do\n    if ! TEST_SEED=$seed pytest -q tests/test_checkout_race.py; then\n      failures=$((failures + 1))\n    fi\n  done\n  test \"$failures\" -lt 3\n' >\"$scratch/bisect.log\" 2>&1\n \nbad=$(git rev-parse HEAD)\ngit bisect reset >/dev/null\n \nprintf 'first bad commit: %s\\n' \"$bad\"\ngit show --no-ext-diff --format=fuller --stat \"$bad\"\ngit diff --no-ext-diff \"$bad^\" \"$bad\" -- src/checkout tests/test_checkout_race.py | sed -n '1,220p'\nprintf '\\nlast bisect events:\\n'\ntail -n 40 \"$scratch/bisect.log\"\n```\n\nThe extra worktree keeps your local files in place. Each commit is classified with five seeds, and it only counts as good if fewer than three runs fail, so one unlucky flake does not poison the bisect. When it lands, the script prints the first bad commit, a truncated diff of the suspected change, and the tail of the log. You get a diagnosis, not every test run in the prompt.\n\n### Example 3: Correlating compressed production logs\n\nYou have compressed production logs that are too large to load into the model. You want the failing endpoints, their error classes, and P95 latency.\n\nBash\n\n```bash\npython3 <<'PY'\nimport gzip\nimport glob\nimport json\nimport sqlite3\nimport tempfile\nfrom pathlib import Path\n \ndef records(pattern):\n    for name in sorted(glob.glob(pattern)):\n        path = Path(name)\n        opener = gzip.open if path.suffix == \".gz\" else open\n        with opener(path, \"rt\", encoding=\"utf-8\") as stream:\n            for line in stream:\n                try:\n                    yield json.loads(line)\n                except json.JSONDecodeError:\n                    continue\n \nwith tempfile.TemporaryDirectory() as directory:\n    db = sqlite3.connect(Path(directory) / \"logs.sqlite\")\n    db.executescript(\"\"\"\n        PRAGMA journal_mode = OFF;\n        PRAGMA synchronous = OFF;\n        PRAGMA temp_store = FILE;\n \n        CREATE TABLE access (\n            req_id TEXT PRIMARY KEY,\n            path TEXT NOT NULL,\n            status INTEGER NOT NULL,\n            latency_ms REAL NOT NULL\n        );\n \n        CREATE TABLE errors (\n            req_id TEXT PRIMARY KEY,\n            kind TEXT NOT NULL\n        );\n    \"\"\")\n \n    access_batch = []\n    for row in records(\"logs/access.jsonl*\"):\n        access_batch.append((\n            row[\"req_id\"],\n            row[\"path\"],\n            int(row[\"status\"]),\n            float(row[\"latency_ms\"]),\n        ))\n        if len(access_batch) == 10_000:\n            db.executemany(\"INSERT OR REPLACE INTO access VALUES (?, ?, ?, ?)\", access_batch)\n            access_batch.clear()\n    db.executemany(\"INSERT OR REPLACE INTO access VALUES (?, ?, ?, ?)\", access_batch)\n \n    error_batch = []\n    for row in records(\"logs/error.jsonl*\"):\n        error_batch.append((row[\"req_id\"], row[\"error_class\"]))\n        if len(error_batch) == 10_000:\n            db.executemany(\"INSERT OR REPLACE INTO errors VALUES (?, ?)\", error_batch)\n            error_batch.clear()\n    db.executemany(\"INSERT OR REPLACE INTO errors VALUES (?, ?)\", error_batch)\n \n    db.executescript(\"\"\"\n        CREATE INDEX access_status_req ON access(status, req_id);\n        CREATE INDEX errors_req ON errors(req_id);\n    \"\"\")\n \n    rows = db.execute(\"\"\"\n        WITH matched AS (\n            SELECT a.path, a.latency_ms, e.kind\n            FROM access AS a\n            JOIN errors AS e USING (req_id)\n            WHERE a.status >= 500\n        ),\n        ranked AS (\n            SELECT\n                path,\n                kind,\n                latency_ms,\n                row_number() OVER (\n                    PARTITION BY path ORDER BY latency_ms\n                ) AS position,\n                count(*) OVER (\n                    PARTITION BY path\n                ) AS samples\n            FROM matched\n        )\n        SELECT\n            path,\n            count(*) AS failures,\n            round(avg(latency_ms), 1) AS average_ms,\n            max(\n                CASE\n                    WHEN position = CAST((samples * 95 + 99) / 100 AS INTEGER)\n                    THEN latency_ms\n                END\n            ) AS p95_ms,\n            group_concat(DISTINCT kind) AS error_classes\n        FROM ranked\n        GROUP BY path\n        ORDER BY failures DESC\n        LIMIT 5\n    \"\"\").fetchall()\n \n    print(json.dumps([\n        {\n            \"endpoint\": path,\n            \"failures\": failures,\n            \"average_ms\": average,\n            \"p95_ms\": p95,\n            \"error_classes\": kinds.split(\",\"),\n        }\n        for path, failures, average, p95, kinds in rows\n    ], indent=2))\nPY\n```\n\nPython reads the rotated files in batches and never dumps them into the prompt. SQLite does the join, the P95 ranking, and the error-class aggregation. What comes back is five JSON records: endpoint, failure count, average latency, P95, and error classes. [Context offloading](https://www.philschmid.de/context-engineering-part-2) keeps the intermediate data in the environment, and puts only the summary in the model.\n\n## Why atomic tools were right\n\nA few months ago, I argued that coding agents should [start with robust atomic tools](https://www.philschmid.de/agent-harness-2026) and avoid shell commands such as `cat`, `sed`, and `echo`. A careless command could flood the context window, return an opaque error, or corrupt an edit through bad quoting. Text output also cannot carry visual information into a vision model.\n\nThose limits still apply to an uninstrumented shell. Foundational models are now much better at composing small Python patchers, Git commands, quoted heredocs, and focused tests. The Harness also absorbs the protections that made atomic tools useful:\n\n- **Output control:** truncate large results and tell the agent how to request a narrower slice.\n- **Diagnostics:** return the exit status, duration, timeout state, and process information.\n- **Isolation and policy:** gate paths, network access, and destructive actions outside the model.\n- **Asynchronous processes:** let the agent start, inspect, and stop long-running commands without blocking a turn.\n\n**The one exception multimodal input:**\n\nText output cannot make a screenshot/image visible. A shell command can render a page, capture a chart, or save a video frame, but the pixels still need to enter the model through a multimodal channel.\n\n## What this means for harness engineering\n\nI compared a shell-centered setup with a configuration that exposed separate tools for file reading, writing, editing, and search. Both ran against the same coding task set under the same conditions. The shell-centered setup achieved on par or better performance.\n\nThat is [the Bitter Lesson](http://www.incompleteideas.net/IncIdeas/BitterLesson.html) applied to harness design: general methods that scale with computation beat hand-built shortcuts. Bash is that general computation layer.\n\nA good agent should have more tools when they provide a better interface to a capability. If the model can do something better with a tool, it should use the tool. Browser control is a good example.\n\nA browser tool can navigate, click, type, wait for the page to settle, and return a screenshot in the same call. Doing this through Bash means invoking a CLI, locating the captured artifact, and passing it through `view_media` in a second step. Service integrations can benefit in a [similar way](https://www.philschmid.de/mcp-best-practices), or stay on the shell with something like [mcp-cli](https://www.philschmid.de/mcp-cli) so the schemas never sit in the prompt.\n\nHere is what to try:\n\n- **Delete the micro-tools:** Try to use Bash to handle file reading, search, multi-file edits, diffs, and verification.\n- **Use subagents as execution firewalls:** Delegate messy exploration and debugging, then return a clean result to the parent context.\n- **Keep the system instructions minimal:** Give the agent more room for repository instructions, domain knowledge, and the task itself.\n\nOur goal is a smaller interface with a larger action space.\n",
  "authors": [],
  "captured_at": "2026-09-27T19:29:15.293681927+00:00",
  "category": "programming",
  "content_html": "<p>If you have used coding agents, you may have noticed that they started to use Bash for tasks that would have traditionally used dedicated tools such as <code>read</code>, <code>edit</code>, and <code>search</code>. Well, models became superhuman in bash and might consider dedicated tools not good enough.</p>\n<p>Every few months, I rebuild an <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.philschmid.de/agent-harness-2026\">agent harness</a> from scratch to learn what changed and what I can delete. This time I removed every model-facing tool except for a <code>bash</code> and a <code>media</code> viewer.</p>\n<p>The outcome matched what I had already seen in daily use. Task completion stayed in the same range, while the agent moved through complex work and verifications with fewer tool boundaries.</p>\n<p>Frontier models have become superhuman in Bash. I mean that in a narrow sense: they synthesize disposable command-line programs, in seconds, that most developers would need much longer to assemble and verify.</p>\n<h2>What “superhuman in Bash” means</h2>\n<p>You probably know the individual pieces <code>git</code>, <code>rg</code>, <code>jq</code>, Python, temporary files, process substitution, and test runners. Writing a one-off 40-line workflow that combines them under time pressure is different. Most of us work through the problem interactively, checking syntax and state between steps.</p>\n<p>Coding agents assemble these workflows from patterns learned across POSIX utilities, programming languages, build systems, and public source code. They still make mistakes. The difference is how much correct orchestration they can attempt at once.</p>\n<p>Bash is the routing layer here. The heavy work may happen in Python, Git, SQLite, a compiler, or a project-specific CLI. One shell interface lets the agent compose all of them without forcing the runtime designer to predict every useful operation.</p>\n<p>The following are simplified examples from my personal agent traces.</p>\n<h3>Example 1: In-place multi-file edits</h3>\n<p>You work on a project and you want to rename a variable across an implementation, its callers, and the tests.</p>\n<p>Bash</p>\n<pre><code class=\"language-bash\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">python3</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> <span class=\"syntax-string syntax-unquoted syntax-heredoc syntax-shell\"><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell\">&lt;&lt;</span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&#39;</span><span class=\"syntax-keyword syntax-control syntax-heredoc-token syntax-shell\">PY</span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&#39;</span></span><span class=\"syntax-string syntax-unquoted syntax-heredoc syntax-shell\">\nfrom pathlib import Path\n \nchanges = {\n    &quot;src/services/billing.py&quot;: [\n        (\n            &quot;def charge_customer(customer_id: str, amount: int):&quot;,\n            &quot;def charge_customer(customer_id: str, amount_cents: int):&quot;,\n            1,\n        ),\n        (\n            &quot;client.charge(token, amount)&quot;,\n            &quot;client.charge(token, amount_cents)&quot;,\n            1,\n        ),\n    ],\n    &quot;src/api/checkout.py&quot;: [\n        (\n            &quot;charge_customer(order.customer_id, order.total)&quot;,\n            &quot;charge_customer(order.customer_id, order.total_cents)&quot;,\n            1,\n        ),\n    ],\n    &quot;src/jobs/retry_payment.py&quot;: [\n        (\n            &quot;charge_customer(job.customer_id, job.amount)&quot;,\n            &quot;charge_customer(job.customer_id, job.amount_cents)&quot;,\n            1,\n        ),\n    ],\n    &quot;tests/test_billing.py&quot;: [\n        (\n            &quot;charge_customer(customer.id, 1200)&quot;,\n            &quot;charge_customer(customer.id, amount_cents=1200)&quot;,\n            2,\n        ),\n    ],\n}\n \nprepared = {}\nfor filename, replacements in changes.items():\n    path = Path(filename)\n    text = path.read_text()\n    for old, new, expected in replacements:\n        found = text.count(old)\n        if found != expected:\n            raise SystemExit(\n                f&quot;{filename}: expected {expected} occurrences of {old!r}, found {found}&quot;\n            )\n        text = text.replace(old, new)\n    prepared[path] = text\n \nfor path, text in prepared.items():\n    path.write_text(text)\n<span class=\"syntax-keyword syntax-control syntax-heredoc-token syntax-shell\">PY</span></span></span>\n \n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">ruff</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> format src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py tests/test_billing.py</span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">ruff</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> check src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py tests/test_billing.py</span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">pyright</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py</span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">pytest</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"><span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>q</span> tests/test_billing.py tests/test_checkout.py tests/test_retry_payment.py</span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">git</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> diff<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> --</span>check</span></span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">git</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> diff<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> --</span>stat</span><span class=\"syntax-keyword syntax-operator syntax-end-of-options syntax-shell\"> --</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py tests/test_billing.py</span>\n</span></span></code></pre>\n<p>The script first counts every old snippet. If a file has the wrong number of matches, it exits before writing anything. Only then does it apply the four-file rename in one pass, so you never sit in a half-updated tree. After the write it formats, lints, type-checks, runs the focused tests, and prints a compact diff. Failed checks stay in the working tree, so the next turn can inspect and fix them. That verify-before-done cycle is the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.philschmid.de/inner-loop-vs-outer-loop\">inner loop</a>.</p>\n<h3>Example 2: Reproducing and bisecting a flaky regression</h3>\n<p>You have a test that fails only sometimes. You want the first commit that introduced the flake, without moving your current checkout.</p>\n<p>Bash</p>\n<pre><code class=\"language-bash\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-support syntax-function syntax-set syntax-shell\">set</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> -euo pipefail</span>\n \n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">test</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"><span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>n</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-punctuation syntax-section syntax-expansion syntax-parameter syntax-begin syntax-shell\">{</span></span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">GOOD_REV</span></span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-shell\">:-</span></span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-section syntax-expansion syntax-parameter syntax-end syntax-shell\">}</span></span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span></span>\n<span class=\"syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell\">repo</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-shell\">=</span><span class=\"syntax-string syntax-unquoted syntax-shell\"><span class=\"syntax-meta syntax-group syntax-expansion syntax-command syntax-parens syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-punctuation syntax-section syntax-parens syntax-begin syntax-shell\">(</span><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-support syntax-function syntax-pwd syntax-shell\">pwd</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> <span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\">-</span>P</span></span><span class=\"syntax-punctuation syntax-section syntax-parens syntax-end syntax-shell\">)</span></span></span>\n<span class=\"syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell\">scratch</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-shell\">=</span><span class=\"syntax-string syntax-unquoted syntax-shell\"><span class=\"syntax-meta syntax-group syntax-expansion syntax-command syntax-parens syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-punctuation syntax-section syntax-parens syntax-begin syntax-shell\">(</span><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">mktemp</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"><span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>d</span></span><span class=\"syntax-punctuation syntax-section syntax-parens syntax-end syntax-shell\">)</span></span></span>\n \n<span class=\"syntax-meta syntax-function syntax-shell\"><span class=\"syntax-entity syntax-name syntax-function syntax-shell\">cleanup</span><span class=\"syntax-punctuation syntax-section syntax-parens syntax-begin syntax-shell\">(</span><span class=\"syntax-punctuation syntax-section syntax-parens syntax-end syntax-shell\">)</span> <span class=\"syntax-punctuation syntax-section syntax-braces syntax-begin syntax-shell\">{</span>\n  <span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">git</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"><span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>C</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">repo</span></span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span> worktree remove<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> --</span>force</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">scratch</span></span>/repo<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell\">&gt;</span>/dev/null <span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-file-descriptor syntax-shell\">2</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell\">&gt;&amp;</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-file-descriptor syntax-shell\">1</span> </span><span class=\"syntax-keyword syntax-operator syntax-logical syntax-or syntax-shell\">||</span> <span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">true</span></span>\n  <span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">rm</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"><span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>rf</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">scratch</span></span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span></span>\n<span class=\"syntax-punctuation syntax-section syntax-braces syntax-end syntax-shell\">}</span></span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-support syntax-function syntax-trap syntax-shell\">trap</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> cleanup EXIT</span>\n \n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">git</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> worktree add<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> --</span>detach</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">scratch</span></span>/repo<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span> HEAD <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell\">&gt;</span>/dev/null</span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-support syntax-function syntax-cd syntax-shell\">cd</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">scratch</span></span>/repo<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span></span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">git</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> bisect start HEAD <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">GOOD_REV</span></span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span></span>\n \n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">git</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> bisect run bash<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>c</span> <span class=\"syntax-string syntax-quoted syntax-single syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&#39;</span>\n  failures=0\n  for seed in 11 29 47 71 101; do\n    if ! TEST_SEED=$seed pytest -q tests/test_checkout_race.py; then\n      failures=$((failures + 1))\n    fi\n  done\n  test &quot;$failures&quot; -lt 3\n<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&#39;</span></span> <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell\">&gt;</span><span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">scratch</span></span>/bisect.log<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span> <span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-file-descriptor syntax-shell\">2</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell\">&gt;&amp;</span><span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-file-descriptor syntax-shell\">1</span>\n</span> \n<span class=\"syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell\">bad</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-shell\">=</span><span class=\"syntax-string syntax-unquoted syntax-shell\"><span class=\"syntax-meta syntax-group syntax-expansion syntax-command syntax-parens syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-punctuation syntax-section syntax-parens syntax-begin syntax-shell\">(</span><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">git</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> rev-parse HEAD</span><span class=\"syntax-punctuation syntax-section syntax-parens syntax-end syntax-shell\">)</span></span></span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">git</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> bisect reset <span class=\"syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell\">&gt;</span>/dev/null</span>\n \n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-support syntax-function syntax-printf syntax-shell\">printf</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> <span class=\"syntax-string syntax-quoted syntax-single syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&#39;</span>first bad commit: %s\\n<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&#39;</span></span> <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">bad</span></span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span></span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">git</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> show<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> --</span>no-ext-diff</span><span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> --</span>format</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-option syntax-shell\">=</span>fuller<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> --</span>stat</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">bad</span></span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span></span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">git</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> diff<span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> --</span>no-ext-diff</span> <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">bad</span></span>^<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span> <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">bad</span></span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span><span class=\"syntax-keyword syntax-operator syntax-end-of-options syntax-shell\"> --</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> src/checkout tests/test_checkout_race.py </span><span class=\"syntax-keyword syntax-operator syntax-logical syntax-pipe syntax-shell\">|</span> <span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">sed</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"><span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>n</span> <span class=\"syntax-string syntax-quoted syntax-single syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&#39;</span>1,220p<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&#39;</span></span></span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-support syntax-function syntax-printf syntax-shell\">printf</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> <span class=\"syntax-string syntax-quoted syntax-single syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&#39;</span>\\nlast bisect events:\\n<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&#39;</span></span></span>\n<span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">tail</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"><span class=\"syntax-variable syntax-parameter syntax-option syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-parameter syntax-shell\"> -</span>n</span> 40 <span class=\"syntax-string syntax-quoted syntax-double syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&quot;</span><span class=\"syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell\"><span class=\"syntax-punctuation syntax-definition syntax-variable syntax-shell\">$</span><span class=\"syntax-variable syntax-other syntax-readwrite syntax-shell\">scratch</span></span>/bisect.log<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&quot;</span></span></span>\n</span></span></code></pre>\n<p>The extra worktree keeps your local files in place. Each commit is classified with five seeds, and it only counts as good if fewer than three runs fail, so one unlucky flake does not poison the bisect. When it lands, the script prints the first bad commit, a truncated diff of the suspected change, and the tail of the log. You get a diagnosis, not every test run in the prompt.</p>\n<h3>Example 3: Correlating compressed production logs</h3>\n<p>You have compressed production logs that are too large to load into the model. You want the failing endpoints, their error classes, and P95 latency.</p>\n<p>Bash</p>\n<pre><code class=\"language-bash\"><span class=\"code-snippet\" data-language=\"Shell\"><span class=\"syntax-source syntax-shell syntax-bash\"><span class=\"syntax-meta syntax-function-call syntax-shell\"><span class=\"syntax-variable syntax-function syntax-shell\">python3</span></span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-shell\"> <span class=\"syntax-string syntax-unquoted syntax-heredoc syntax-shell\"><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell\">&lt;&lt;</span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell\">&#39;</span><span class=\"syntax-keyword syntax-control syntax-heredoc-token syntax-shell\">PY</span><span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell\">&#39;</span></span><span class=\"syntax-string syntax-unquoted syntax-heredoc syntax-shell\">\nimport gzip\nimport glob\nimport json\nimport sqlite3\nimport tempfile\nfrom pathlib import Path\n \ndef records(pattern):\n    for name in sorted(glob.glob(pattern)):\n        path = Path(name)\n        opener = gzip.open if path.suffix == &quot;.gz&quot; else open\n        with opener(path, &quot;rt&quot;, encoding=&quot;utf-8&quot;) as stream:\n            for line in stream:\n                try:\n                    yield json.loads(line)\n                except json.JSONDecodeError:\n                    continue\n \nwith tempfile.TemporaryDirectory() as directory:\n    db = sqlite3.connect(Path(directory) / &quot;logs.sqlite&quot;)\n    db.executescript(&quot;&quot;&quot;\n        PRAGMA journal_mode = OFF;\n        PRAGMA synchronous = OFF;\n        PRAGMA temp_store = FILE;\n \n        CREATE TABLE access (\n            req_id TEXT PRIMARY KEY,\n            path TEXT NOT NULL,\n            status INTEGER NOT NULL,\n            latency_ms REAL NOT NULL\n        );\n \n        CREATE TABLE errors (\n            req_id TEXT PRIMARY KEY,\n            kind TEXT NOT NULL\n        );\n    &quot;&quot;&quot;)\n \n    access_batch = []\n    for row in records(&quot;logs/access.jsonl*&quot;):\n        access_batch.append((\n            row[&quot;req_id&quot;],\n            row[&quot;path&quot;],\n            int(row[&quot;status&quot;]),\n            float(row[&quot;latency_ms&quot;]),\n        ))\n        if len(access_batch) == 10_000:\n            db.executemany(&quot;INSERT OR REPLACE INTO access VALUES (?, ?, ?, ?)&quot;, access_batch)\n            access_batch.clear()\n    db.executemany(&quot;INSERT OR REPLACE INTO access VALUES (?, ?, ?, ?)&quot;, access_batch)\n \n    error_batch = []\n    for row in records(&quot;logs/error.jsonl*&quot;):\n        error_batch.append((row[&quot;req_id&quot;], row[&quot;error_class&quot;]))\n        if len(error_batch) == 10_000:\n            db.executemany(&quot;INSERT OR REPLACE INTO errors VALUES (?, ?)&quot;, error_batch)\n            error_batch.clear()\n    db.executemany(&quot;INSERT OR REPLACE INTO errors VALUES (?, ?)&quot;, error_batch)\n \n    db.executescript(&quot;&quot;&quot;\n        CREATE INDEX access_status_req ON access(status, req_id);\n        CREATE INDEX errors_req ON errors(req_id);\n    &quot;&quot;&quot;)\n \n    rows = db.execute(&quot;&quot;&quot;\n        WITH matched AS (\n            SELECT a.path, a.latency_ms, e.kind\n            FROM access AS a\n            JOIN errors AS e USING (req_id)\n            WHERE a.status &gt;= 500\n        ),\n        ranked AS (\n            SELECT\n                path,\n                kind,\n                latency_ms,\n                row_number() OVER (\n                    PARTITION BY path ORDER BY latency_ms\n                ) AS position,\n                count(*) OVER (\n                    PARTITION BY path\n                ) AS samples\n            FROM matched\n        )\n        SELECT\n            path,\n            count(*) AS failures,\n            round(avg(latency_ms), 1) AS average_ms,\n            max(\n                CASE\n                    WHEN position = CAST((samples * 95 + 99) / 100 AS INTEGER)\n                    THEN latency_ms\n                END\n            ) AS p95_ms,\n            group_concat(DISTINCT kind) AS error_classes\n        FROM ranked\n        GROUP BY path\n        ORDER BY failures DESC\n        LIMIT 5\n    &quot;&quot;&quot;).fetchall()\n \n    print(json.dumps([\n        {\n            &quot;endpoint&quot;: path,\n            &quot;failures&quot;: failures,\n            &quot;average_ms&quot;: average,\n            &quot;p95_ms&quot;: p95,\n            &quot;error_classes&quot;: kinds.split(&quot;,&quot;),\n        }\n        for path, failures, average, p95, kinds in rows\n    ], indent=2))\n<span class=\"syntax-keyword syntax-control syntax-heredoc-token syntax-shell\">PY</span></span></span>\n</span></span></code></pre>\n<p>Python reads the rotated files in batches and never dumps them into the prompt. SQLite does the join, the P95 ranking, and the error-class aggregation. What comes back is five JSON records: endpoint, failure count, average latency, P95, and error classes. <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.philschmid.de/context-engineering-part-2\">Context offloading</a> keeps the intermediate data in the environment, and puts only the summary in the model.</p>\n<h2>Why atomic tools were right</h2>\n<p>A few months ago, I argued that coding agents should <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.philschmid.de/agent-harness-2026\">start with robust atomic tools</a> and avoid shell commands such as <code>cat</code>, <code>sed</code>, and <code>echo</code>. A careless command could flood the context window, return an opaque error, or corrupt an edit through bad quoting. Text output also cannot carry visual information into a vision model.</p>\n<p>Those limits still apply to an uninstrumented shell. Foundational models are now much better at composing small Python patchers, Git commands, quoted heredocs, and focused tests. The Harness also absorbs the protections that made atomic tools useful:</p>\n<ul>\n<li><strong>Output control:</strong> truncate large results and tell the agent how to request a narrower slice.</li>\n<li><strong>Diagnostics:</strong> return the exit status, duration, timeout state, and process information.</li>\n<li><strong>Isolation and policy:</strong> gate paths, network access, and destructive actions outside the model.</li>\n<li><strong>Asynchronous processes:</strong> let the agent start, inspect, and stop long-running commands without blocking a turn.</li>\n</ul>\n<p><strong>The one exception multimodal input:</strong></p>\n<p>Text output cannot make a screenshot/image visible. A shell command can render a page, capture a chart, or save a video frame, but the pixels still need to enter the model through a multimodal channel.</p>\n<h2>What this means for harness engineering</h2>\n<p>I compared a shell-centered setup with a configuration that exposed separate tools for file reading, writing, editing, and search. Both ran against the same coding task set under the same conditions. The shell-centered setup achieved on par or better performance.</p>\n<p>That is <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"http://www.incompleteideas.net/IncIdeas/BitterLesson.html\">the Bitter Lesson</a> applied to harness design: general methods that scale with computation beat hand-built shortcuts. Bash is that general computation layer.</p>\n<p>A good agent should have more tools when they provide a better interface to a capability. If the model can do something better with a tool, it should use the tool. Browser control is a good example.</p>\n<p>A browser tool can navigate, click, type, wait for the page to settle, and return a screenshot in the same call. Doing this through Bash means invoking a CLI, locating the captured artifact, and passing it through <code>view_media</code> in a second step. Service integrations can benefit in a <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.philschmid.de/mcp-best-practices\">similar way</a>, or stay on the shell with something like <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.philschmid.de/mcp-cli\">mcp-cli</a> so the schemas never sit in the prompt.</p>\n<p>Here is what to try:</p>\n<ul>\n<li><strong>Delete the micro-tools:</strong> Try to use Bash to handle file reading, search, multi-file edits, diffs, and verification.</li>\n<li><strong>Use subagents as execution firewalls:</strong> Delegate messy exploration and debugging, then return a clean result to the parent context.</li>\n<li><strong>Keep the system instructions minimal:</strong> Give the agent more room for repository instructions, domain knowledge, and the task itself.</li>\n</ul>\n<p>Our goal is a smaller interface with a larger action space.</p>\n",
  "content_markdown": "If you have used coding agents, you may have noticed that they started to use Bash for tasks that would have traditionally used dedicated tools such as `read`, `edit`, and `search`. Well, models became superhuman in bash and might consider dedicated tools not good enough.\n\nEvery few months, I rebuild an [agent harness](https://www.philschmid.de/agent-harness-2026) from scratch to learn what changed and what I can delete. This time I removed every model-facing tool except for a `bash` and a `media` viewer.\n\nThe outcome matched what I had already seen in daily use. Task completion stayed in the same range, while the agent moved through complex work and verifications with fewer tool boundaries.\n\nFrontier models have become superhuman in Bash. I mean that in a narrow sense: they synthesize disposable command-line programs, in seconds, that most developers would need much longer to assemble and verify.\n\n## What “superhuman in Bash” means\n\nYou probably know the individual pieces `git`, `rg`, `jq`, Python, temporary files, process substitution, and test runners. Writing a one-off 40-line workflow that combines them under time pressure is different. Most of us work through the problem interactively, checking syntax and state between steps.\n\nCoding agents assemble these workflows from patterns learned across POSIX utilities, programming languages, build systems, and public source code. They still make mistakes. The difference is how much correct orchestration they can attempt at once.\n\nBash is the routing layer here. The heavy work may happen in Python, Git, SQLite, a compiler, or a project-specific CLI. One shell interface lets the agent compose all of them without forcing the runtime designer to predict every useful operation.\n\nThe following are simplified examples from my personal agent traces.\n\n### Example 1: In-place multi-file edits\n\nYou work on a project and you want to rename a variable across an implementation, its callers, and the tests.\n\nBash\n\n```bash\npython3 <<'PY'\nfrom pathlib import Path\n \nchanges = {\n    \"src/services/billing.py\": [\n        (\n            \"def charge_customer(customer_id: str, amount: int):\",\n            \"def charge_customer(customer_id: str, amount_cents: int):\",\n            1,\n        ),\n        (\n            \"client.charge(token, amount)\",\n            \"client.charge(token, amount_cents)\",\n            1,\n        ),\n    ],\n    \"src/api/checkout.py\": [\n        (\n            \"charge_customer(order.customer_id, order.total)\",\n            \"charge_customer(order.customer_id, order.total_cents)\",\n            1,\n        ),\n    ],\n    \"src/jobs/retry_payment.py\": [\n        (\n            \"charge_customer(job.customer_id, job.amount)\",\n            \"charge_customer(job.customer_id, job.amount_cents)\",\n            1,\n        ),\n    ],\n    \"tests/test_billing.py\": [\n        (\n            \"charge_customer(customer.id, 1200)\",\n            \"charge_customer(customer.id, amount_cents=1200)\",\n            2,\n        ),\n    ],\n}\n \nprepared = {}\nfor filename, replacements in changes.items():\n    path = Path(filename)\n    text = path.read_text()\n    for old, new, expected in replacements:\n        found = text.count(old)\n        if found != expected:\n            raise SystemExit(\n                f\"{filename}: expected {expected} occurrences of {old!r}, found {found}\"\n            )\n        text = text.replace(old, new)\n    prepared[path] = text\n \nfor path, text in prepared.items():\n    path.write_text(text)\nPY\n \nruff format src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py tests/test_billing.py\nruff check src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py tests/test_billing.py\npyright src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py\npytest -q tests/test_billing.py tests/test_checkout.py tests/test_retry_payment.py\ngit diff --check\ngit diff --stat -- src/services/billing.py src/api/checkout.py src/jobs/retry_payment.py tests/test_billing.py\n```\n\nThe script first counts every old snippet. If a file has the wrong number of matches, it exits before writing anything. Only then does it apply the four-file rename in one pass, so you never sit in a half-updated tree. After the write it formats, lints, type-checks, runs the focused tests, and prints a compact diff. Failed checks stay in the working tree, so the next turn can inspect and fix them. That verify-before-done cycle is the [inner loop](https://www.philschmid.de/inner-loop-vs-outer-loop).\n\n### Example 2: Reproducing and bisecting a flaky regression\n\nYou have a test that fails only sometimes. You want the first commit that introduced the flake, without moving your current checkout.\n\nBash\n\n```bash\nset -euo pipefail\n \ntest -n \"${GOOD_REV:-}\"\nrepo=$(pwd -P)\nscratch=$(mktemp -d)\n \ncleanup() {\n  git -C \"$repo\" worktree remove --force \"$scratch/repo\" >/dev/null 2>&1 || true\n  rm -rf \"$scratch\"\n}\ntrap cleanup EXIT\n \ngit worktree add --detach \"$scratch/repo\" HEAD >/dev/null\ncd \"$scratch/repo\"\ngit bisect start HEAD \"$GOOD_REV\"\n \ngit bisect run bash -c '\n  failures=0\n  for seed in 11 29 47 71 101; do\n    if ! TEST_SEED=$seed pytest -q tests/test_checkout_race.py; then\n      failures=$((failures + 1))\n    fi\n  done\n  test \"$failures\" -lt 3\n' >\"$scratch/bisect.log\" 2>&1\n \nbad=$(git rev-parse HEAD)\ngit bisect reset >/dev/null\n \nprintf 'first bad commit: %s\\n' \"$bad\"\ngit show --no-ext-diff --format=fuller --stat \"$bad\"\ngit diff --no-ext-diff \"$bad^\" \"$bad\" -- src/checkout tests/test_checkout_race.py | sed -n '1,220p'\nprintf '\\nlast bisect events:\\n'\ntail -n 40 \"$scratch/bisect.log\"\n```\n\nThe extra worktree keeps your local files in place. Each commit is classified with five seeds, and it only counts as good if fewer than three runs fail, so one unlucky flake does not poison the bisect. When it lands, the script prints the first bad commit, a truncated diff of the suspected change, and the tail of the log. You get a diagnosis, not every test run in the prompt.\n\n### Example 3: Correlating compressed production logs\n\nYou have compressed production logs that are too large to load into the model. You want the failing endpoints, their error classes, and P95 latency.\n\nBash\n\n```bash\npython3 <<'PY'\nimport gzip\nimport glob\nimport json\nimport sqlite3\nimport tempfile\nfrom pathlib import Path\n \ndef records(pattern):\n    for name in sorted(glob.glob(pattern)):\n        path = Path(name)\n        opener = gzip.open if path.suffix == \".gz\" else open\n        with opener(path, \"rt\", encoding=\"utf-8\") as stream:\n            for line in stream:\n                try:\n                    yield json.loads(line)\n                except json.JSONDecodeError:\n                    continue\n \nwith tempfile.TemporaryDirectory() as directory:\n    db = sqlite3.connect(Path(directory) / \"logs.sqlite\")\n    db.executescript(\"\"\"\n        PRAGMA journal_mode = OFF;\n        PRAGMA synchronous = OFF;\n        PRAGMA temp_store = FILE;\n \n        CREATE TABLE access (\n            req_id TEXT PRIMARY KEY,\n            path TEXT NOT NULL,\n            status INTEGER NOT NULL,\n            latency_ms REAL NOT NULL\n        );\n \n        CREATE TABLE errors (\n            req_id TEXT PRIMARY KEY,\n            kind TEXT NOT NULL\n        );\n    \"\"\")\n \n    access_batch = []\n    for row in records(\"logs/access.jsonl*\"):\n        access_batch.append((\n            row[\"req_id\"],\n            row[\"path\"],\n            int(row[\"status\"]),\n            float(row[\"latency_ms\"]),\n        ))\n        if len(access_batch) == 10_000:\n            db.executemany(\"INSERT OR REPLACE INTO access VALUES (?, ?, ?, ?)\", access_batch)\n            access_batch.clear()\n    db.executemany(\"INSERT OR REPLACE INTO access VALUES (?, ?, ?, ?)\", access_batch)\n \n    error_batch = []\n    for row in records(\"logs/error.jsonl*\"):\n        error_batch.append((row[\"req_id\"], row[\"error_class\"]))\n        if len(error_batch) == 10_000:\n            db.executemany(\"INSERT OR REPLACE INTO errors VALUES (?, ?)\", error_batch)\n            error_batch.clear()\n    db.executemany(\"INSERT OR REPLACE INTO errors VALUES (?, ?)\", error_batch)\n \n    db.executescript(\"\"\"\n        CREATE INDEX access_status_req ON access(status, req_id);\n        CREATE INDEX errors_req ON errors(req_id);\n    \"\"\")\n \n    rows = db.execute(\"\"\"\n        WITH matched AS (\n            SELECT a.path, a.latency_ms, e.kind\n            FROM access AS a\n            JOIN errors AS e USING (req_id)\n            WHERE a.status >= 500\n        ),\n        ranked AS (\n            SELECT\n                path,\n                kind,\n                latency_ms,\n                row_number() OVER (\n                    PARTITION BY path ORDER BY latency_ms\n                ) AS position,\n                count(*) OVER (\n                    PARTITION BY path\n                ) AS samples\n            FROM matched\n        )\n        SELECT\n            path,\n            count(*) AS failures,\n            round(avg(latency_ms), 1) AS average_ms,\n            max(\n                CASE\n                    WHEN position = CAST((samples * 95 + 99) / 100 AS INTEGER)\n                    THEN latency_ms\n                END\n            ) AS p95_ms,\n            group_concat(DISTINCT kind) AS error_classes\n        FROM ranked\n        GROUP BY path\n        ORDER BY failures DESC\n        LIMIT 5\n    \"\"\").fetchall()\n \n    print(json.dumps([\n        {\n            \"endpoint\": path,\n            \"failures\": failures,\n            \"average_ms\": average,\n            \"p95_ms\": p95,\n            \"error_classes\": kinds.split(\",\"),\n        }\n        for path, failures, average, p95, kinds in rows\n    ], indent=2))\nPY\n```\n\nPython reads the rotated files in batches and never dumps them into the prompt. SQLite does the join, the P95 ranking, and the error-class aggregation. What comes back is five JSON records: endpoint, failure count, average latency, P95, and error classes. [Context offloading](https://www.philschmid.de/context-engineering-part-2) keeps the intermediate data in the environment, and puts only the summary in the model.\n\n## Why atomic tools were right\n\nA few months ago, I argued that coding agents should [start with robust atomic tools](https://www.philschmid.de/agent-harness-2026) and avoid shell commands such as `cat`, `sed`, and `echo`. A careless command could flood the context window, return an opaque error, or corrupt an edit through bad quoting. Text output also cannot carry visual information into a vision model.\n\nThose limits still apply to an uninstrumented shell. Foundational models are now much better at composing small Python patchers, Git commands, quoted heredocs, and focused tests. The Harness also absorbs the protections that made atomic tools useful:\n\n- **Output control:** truncate large results and tell the agent how to request a narrower slice.\n- **Diagnostics:** return the exit status, duration, timeout state, and process information.\n- **Isolation and policy:** gate paths, network access, and destructive actions outside the model.\n- **Asynchronous processes:** let the agent start, inspect, and stop long-running commands without blocking a turn.\n\n**The one exception multimodal input:**\n\nText output cannot make a screenshot/image visible. A shell command can render a page, capture a chart, or save a video frame, but the pixels still need to enter the model through a multimodal channel.\n\n## What this means for harness engineering\n\nI compared a shell-centered setup with a configuration that exposed separate tools for file reading, writing, editing, and search. Both ran against the same coding task set under the same conditions. The shell-centered setup achieved on par or better performance.\n\nThat is [the Bitter Lesson](http://www.incompleteideas.net/IncIdeas/BitterLesson.html) applied to harness design: general methods that scale with computation beat hand-built shortcuts. Bash is that general computation layer.\n\nA good agent should have more tools when they provide a better interface to a capability. If the model can do something better with a tool, it should use the tool. Browser control is a good example.\n\nA browser tool can navigate, click, type, wait for the page to settle, and return a screenshot in the same call. Doing this through Bash means invoking a CLI, locating the captured artifact, and passing it through `view_media` in a second step. Service integrations can benefit in a [similar way](https://www.philschmid.de/mcp-best-practices), or stay on the shell with something like [mcp-cli](https://www.philschmid.de/mcp-cli) so the schemas never sit in the prompt.\n\nHere is what to try:\n\n- **Delete the micro-tools:** Try to use Bash to handle file reading, search, multi-file edits, diffs, and verification.\n- **Use subagents as execution firewalls:** Delegate messy exploration and debugging, then return a clean result to the parent context.\n- **Keep the system instructions minimal:** Give the agent more room for repository instructions, domain knowledge, and the task itself.\n\nOur goal is a smaller interface with a larger action space.\n",
  "dateCreated": "2026-09-27T19:29:15.293681927+00:00",
  "date_created": "2026-09-01T00:00:00+00:00",
  "date_modified": "2026-09-01T00:00:00+00:00",
  "date_published": "2026-09-01T00:00:00+00:00",
  "description": "Frontier coding agents can compose shell workflows that replace entire catalogs of file tools. Here is what changes when Bash becomes the primary execution interface.",
  "external_url": "https://www.philschmid.de/superhuman-bash",
  "git": {
    "commit": "ebb0941c5c06a2ab2377342d607535262a3e969e",
    "path": "items/philschmid-de/2026/09/2026-09-01-how-foundational-models-became-superhuman-in-bash.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/ebb0941c5c06a2ab2377342d607535262a3e969e/items/philschmid-de/2026/09/2026-09-01-how-foundational-models-became-superhuman-in-bash.md"
  },
  "id": "urn:aggr:item:ceb1fdcf781efc014d65097dffa3824f73704076",
  "isBasedOn": {
    "@id": "https://www.philschmid.de/superhuman-bash"
  },
  "mainEntity": {
    "@id": "https://www.philschmid.de/superhuman-bash",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/philschmid-de/2026-09-01-how-foundational-models-became-superhuman-in-bash/#webpage"
    },
    "dateModified": "2026-09-01T00:00:00+00:00",
    "datePublished": "2026-09-01T00:00:00+00:00",
    "headline": "How Foundational Models Became Superhuman in Bash",
    "timeRequired": "PT8M",
    "url": "https://www.philschmid.de/superhuman-bash",
    "wordCount": 1643
  },
  "name": "Archived snapshot: How Foundational Models Became Superhuman in Bash",
  "preview": null,
  "reading_minutes": 8,
  "replicated_at": null,
  "source": "philschmid-de",
  "source_name": "philschmid.de - RSS feed",
  "summary": "Frontier coding agents can compose shell workflows that replace entire catalogs of file tools. Here is what changes when Bash becomes the primary execution interface.",
  "tags": [],
  "temporalCoverage": "2026-09-27T19:29:15.293681927+00:00",
  "title": "How Foundational Models Became Superhuman in Bash",
  "url": "https://aggr.aymericbeaumet.com/items/philschmid-de/2026-09-01-how-foundational-models-became-superhuman-in-bash/",
  "word_count": 1643
}