Skip to content

Commit 5f203b5

Browse files
committed
Merge origin/main
2 parents 4bb42ee + 63ec5c1 commit 5f203b5

82 files changed

Lines changed: 1154 additions & 837 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ When refactoring replaces an old path, delete the old one. No back-compat shims,
3434
- Bug fixes start with a failing test that reproduces the bug. Do not start by patching.
3535
- `tests/unit/` shared unit tests and helpers · co-located `src/**/*.test.ts` for module logic · `tests/fixtures/` fixture repos · `tests/integration/` reactor/permission harness. Planned: `tests/e2e/` (fixture-repo runs).
3636
- A test must not depend on another file having run, or on the default file order. It must pass under `bun test ./src ./tests ./evals --randomize`. If a test mutates module-level state or calls `mock.module`, it must restore that state itself (`afterEach`/`afterAll`), not rely on the process happening to reset it. When capturing a module's real exports to restore later, shallow-copy them (`{ ...moduleNamespace }`) at capture time, whether the namespace came from `await import(path)` or a static `import * as ns from "path"` — Bun mutates the live namespace object in place when the module is mocked, so holding a bare reference to it (either form) silently turns into the mocked exports.
37+
- Never call `mock.module` directly. Bun runs every test file in one process, so a `mock.module` call without its own teardown stays installed for the rest of the run and silently replaces the real module for other files — producing failures in files the change never touched, with no obvious link to the cause and no signal from `tsc` or a per-file run (CL-6967). Use `withMockedModule`/`withMockedModuleDuring` from `tests/helpers/mock-module.ts`, which capture the real module and register their own restore. An eslint rule (`no-restricted-syntax` in `eslint.config.js`) rejects bare `mock.module` calls in `*.test.ts` files.
3738

3839
## Build & Validation
3940

CHANGELOG.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,73 @@ parallel copies under `docs/` or `scripts/notes/`. At cut time: rename
2222
longer aborts as a long-window quota. Clear usage/quota body markers still
2323
abort. Transcript shows "Rate limited — retrying…" instead of "Quota exhausted".
2424

25+
- **`apply_patch` can update files again.** Its Update operation read the target
26+
through the line-numbered `read_file` view and then tried to match the patch's
27+
raw context lines against it, so every context-bearing update failed with
28+
"failed to find expected lines in file." Updates now read the file's real
29+
content. This was the codex-family edit path, and its most likely symptom was
30+
the model retrying the identical patch.
31+
32+
- **A worker that succeeds is no longer refused on its next dispatch.** Salvage
33+
classification matched free-text substrings, so a finished report whose Summary
34+
merely mentioned "no progress," "cancelled," or "long silence" was recorded as a
35+
forced stop and blocked an identical re-dispatch for the rest of the session.
36+
Classification is now exact. A parallel wave where one worker salvages and
37+
another succeeds also stops leaving the brief blocked.
38+
39+
- **Large files read in one pass instead of many.** A truncated `read_file` now
40+
returns a continuation handle that resumes exactly where it stopped, rather
41+
than telling the model to re-read the same path at a new offset. Following a
42+
spent handle explains what happened and names the file and offset to resume
43+
from, instead of a bare "not found."
44+
45+
- **Edits show what changed.** `edit_file`, `write_file`, `delete_file`, and
46+
`apply_patch` return a bounded diff of the change, so nothing needs a
47+
verification read to confirm the edit landed. The harness already re-read and
48+
compared after every write; that result now reaches the operator and the model.
49+
50+
- **Reasoning survives resume, and corrected retries win.** On the Responses
51+
adapters, reasoning items were dropped whenever a turn carried no model field —
52+
including on resume — while the tool calls they produced were kept, a shape
53+
known to degenerate reasoning models. Duplicate tool results also kept the
54+
stale first copy, discarding a corrected retry. Tool names now route through
55+
the wire-safe codec on all three adapters.
56+
57+
- **Compaction stops discarding the prompt cache.** Compacted prompt prefixes are
58+
byte-stable across passes, so a compaction no longer invalidates the cache in
59+
full, and a shallow reclaim no longer re-triggers compaction immediately.
60+
61+
- **Per-model loop visibility.** Mid-stream degenerate-repetition aborts are
62+
recorded per detector with the measured value beside its threshold, so the loop
63+
rate can be read per model instead of inferred.
64+
65+
### Interface
66+
67+
- **The context meter tells the truth after `/new`, `/clear`, and compaction.**
68+
It resets rather than reporting pre-clear usage, and re-syncs to the compacted
69+
size rather than blanking to zero.
70+
71+
- **Compaction is visible when it happens.** Folding turns away now shows in the
72+
TUI instead of the transcript quietly shrinking.
73+
74+
- **The status indicator no longer flashes "awaiting response" mid-fan-out.**
75+
During parallel tool calls the first tool to finish used to mark the turn idle
76+
while its siblings were still running.
77+
78+
### Safety
79+
80+
- **Connecting a provider verifies the token can actually be used.** OAuth
81+
onboarding completed on tokens carrying no API scope, so the first real request
82+
failed in a way that looked like a Corbits bug. Onboarding now probes the
83+
provider and blocks with an actionable message when the token definitively
84+
lacks access — a network failure or rate limit does not block.
85+
86+
- **Approval volume is measurable.** Every permission ask and how it settled is
87+
recorded — tool, rule, mode, outcome, and timings, with no command text, path,
88+
or argument in the record — with `bun run scripts/approval-forensics.ts` to
89+
read it back.
90+
91+
2592
- **Resuming a session no longer shows a blank error when the saved history
2693
has one corrupted line.** A malformed or schema-invalid line anywhere in the
2794
saved transcript used to abort the entire resume load. The TUI's resume view

docs/ARCHITECTURE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ Profiles with `orchestrator: true` may themselves call `task` (one hop only): ne
229229

230230
#### Closed director fleet (`src/agent/directors/`)
231231

232-
Every shipped specialist is a **director package** — a prompt-first `DirectorPackage` (system prompt, tool envelope, spawn rights, nudge budget, report contract, optional `writePaths`, `modelRole`) registered in a **closed** set of 16 ids. There is no catch-all worker: `task` without `agent` or non-general `intent`, and `task(intent="general")`, fail closed so the primary reclassifies. Nested directors with a spawn allowlist reject off-list children at `createTaskTool` (not prompt-only). Skywalker is the primary session identity: `task(agent="skywalker")` is refused, and `directorProfiles()` omits it from the spawn catalog.
232+
Every shipped specialist is a **director package** — a prompt-first `DirectorPackage` (system prompt, tool envelope, spawn rights, nudge budget, report contract, `modelRole`) registered in a **closed** set of 16 ids. There is no catch-all worker: `task` without `agent` or non-general `intent`, and `task(intent="general")`, fail closed so the primary reclassifies. Nested directors with a spawn allowlist reject off-list children at `createTaskTool` (not prompt-only). Skywalker is the primary session identity: `task(agent="skywalker")` is refused, and `directorProfiles()` omits it from the spawn catalog.
233233

234234
**Primary**
235235

@@ -285,7 +285,7 @@ Every shipped specialist is a **director package** — a prompt-first `DirectorP
285285
| greybeard | intern, explore, critique only |
286286
| All other directors | no `task` |
287287

288-
**Tool envelopes** prefer small `tools.allow` mounts over deny-everything. Shipped docs/design directors (shakespeare, brand-reviewer, bruckheimer) mount write tools with **no** package `writePaths`. Lane routing is spawn policy (shakespeare = P/A/I docs, brand-reviewer = DESIGN.md, bruckheimer = product discovery), not a file lock. Optional `writePaths` still exists; the permission gate enforces it when a profile sets it.
288+
**Tool envelopes** prefer small `tools.allow` mounts over deny-everything. Shipped docs/design directors (shakespeare, brand-reviewer, bruckheimer) mount write tools with no path-level lock. Lane routing is spawn policy (shakespeare = P/A/I docs, brand-reviewer = DESIGN.md, bruckheimer = product discovery), not a file lock. There is no static per-package write-path declaration (CL-6952 removed it — no shipped director ever set one); instead the task tool records, without blocking, when two concurrently running dispatches land on the same cwd (see `intervention-log.ts`'s `conflict` class).
289289

290290
**Typical chain:** bruckheimer → plan → greybeard → build (+ intern) → critique (+ optional neckbeard), with skywalker coordinating throughout.
291291

@@ -299,7 +299,7 @@ Data-only agent plugins (`src/plugins/data-only-agent.ts`) synthesize `agentPlug
299299

300300
### System Prompt (`src/agent/prompts.ts`)
301301

302-
The primary session identity is **Skywalker** (`buildChatRole` → `createSkywalkerSystemPrompt`). Product name remains Corbits Code; when asked its name, the primary answers Skywalker. Role: orchestrate — classify, DIY tiny/single-file/one-route product edits, dispatch closed directors via `task` for substantial work, track the fleet, synthesize. Product mutation tools (`write_file` / `edit_file` / `delete_file`) are mounted on the primary session (CORE and `SKYWALKER_TOOLS`) so Skywalker can DIY bounded edits; spawn remains the default for substantial, multi-file, parallel, or specialist work (hard cap 4 workers). Shell file-writes stay denied by auto-shell policy. MCP tools are not re-filtered by a product-write deny list (that list is gone). Leaf `writePaths` only apply to path-keyed product tools when a profile sets them. A frontier model already knows how to code; the static prompt carries harness-specific facts and the closed-fleet orchestration policy. The base is three individually-exported sections:
302+
The primary session identity is **Skywalker** (`buildChatRole` → `createSkywalkerSystemPrompt`). Product name remains Corbits Code; when asked its name, the primary answers Skywalker. Role: orchestrate — classify, DIY tiny/single-file/one-route product edits, dispatch closed directors via `task` for substantial work, track the fleet, synthesize. Product mutation tools (`write_file` / `edit_file` / `delete_file`) are mounted on the primary session (CORE and `SKYWALKER_TOOLS`) so Skywalker can DIY bounded edits; spawn remains the default for substantial, multi-file, parallel, or specialist work (hard cap 4 workers). Shell file-writes stay denied by auto-shell policy. MCP tools are not re-filtered by a product-write deny list (that list is gone). There is no static per-leaf write-path lock; concurrent lanes sharing a cwd are instead flagged (not blocked) as a `conflict` intervention. A frontier model already knows how to code; the static prompt carries harness-specific facts and the closed-fleet orchestration policy. The base is three individually-exported sections:
303303

304304
- `buildChatRole` — Skywalker primary identity (orchestrate; DIY tiny/bounded product edits; spawn for substantial work).
305305
- `buildHarnessFacts` — the non-derivable rules: shell file-writes are blocked, path tools are the DIY surface on primary (spawn build/docs directors for substantial work), dependency installs and off-limits paths need approval, images are native multimodal input, only core tools are resident (load the rest via `tool_search`; use `search_agents` before dispatching specialists), workflows run only from slash-command steps, and session memory lives at `.corbits/MEMORY.md`.

0 commit comments

Comments
 (0)