The note under the hero picture said the frames before and after show the
model name and the Ask setting; the Edit frame next to Approval shows
neither. It now says the Prompt and Result frames show them, which the
capture's flags confirm at both widths, in all 18 locales and the GT catalog.
Founder decision 156: the dated OrcaRouter offer strip from CW-7933 is
restored unchanged (partner-promo.ts, markup and styles from cc0d42bf4a,
English only, referral disclosure, days left computed at render, hidden
after 2026-11-21), placed after the model section, so the first screen is
as accepted.
The CHANGELOG said the page names several agents only while the release has
it; only the facts row is gated. Corrected, and it now says the offer strip
stays below the first screen. TUI changelog resynced.
Checks: npm test 1301 passed + 7 node, tsc, lint, check:locales, check:tokens
and build all exit 0.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The earlier home reorganization (CW-7933) is replaced by version B, which no
longer carries the dated OrcaRouter offer strip or the browser-filled GitHub
community panel; that entry described a page that no longer exists, so it is
folded into the rebuild's line. TUI changelog slice resynced.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The site sets unicode-bidi: plaintext on RTL paragraphs, so a paragraph
that opens with the Latin brand name took a left-to-right base and the
Arabic intro came out reversed around "Codewhale" and "Rust." Wrapping the
brand in bdi isolates it and the paragraph takes the Arabic direction.
Measured by word positions in the rendered /ar page before the fix.
Adds the CHANGELOG line for the rebuilt home page and syncs the TUI
changelog slice.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The first build put about a dozen things on the first screen. This keeps
five: the founder's line, one sentence that says what Codewhale is, the
install command, the released terminal session as one wide picture, and a
row of facts from data (Rust, licence, provider routes and models of the
published release, many agents when released). The route moves to one
smaller section lower down and carries the whale; the belief paragraph and
the text-only agents section are removed.
New home strings in 17 locales are machine drafts. Not yet run: npm test,
lint, build (version comparison commit; checks run on the recommended one).
tsc --noEmit: exit 0. check-locales: PASS.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Chinese ships with the same footer line as the other translated locales
until a native reader has reviewed it. English stays the reference.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The home is rebuilt on the chosen direction. Under the two-sentence
headline the product is one line on the horizon: solid through the parts
that are released, dashed and dotted through the ones that are not, and off
the edge of the page. Line style, station shape and a word carry each state,
from product-map.ts; colour never does alone. The whale rests where the solid
line ends and is never drawn over a part that is not released. The drawing is
hidden from assistive technology and an ordered list beside it says the same
thing.
The terminal that hangs from the Terminal app station is the released
v0.10.2 capture as selectable text. It opens on the approval step, with a
real tablist (Prompt, Edit, Approval, Result) that arrow keys, Home and End
move through. Its caption comes from capture-caption.ts, and frames that do
not show the model name or the Ask setting say so. Section 3 carries no
frame: no released capture shows several agents, and a development-build
frame is not true of the released version.
On a phone the first screen holds the headline, a compact route with the
whale, and the install box; the full list of parts appears once, in section
4, as a printed tree. The install box puts Copy in the choice row so the
command has the whole line, and on a phone the command breaks at the pipe.
Every visible word comes from home.ts. Eight keys nothing renders are removed
in all 18 locales; six frame summaries and a plural-aware contributor label
are added. The translations of the new keys are machine drafts, like the
rest of those locales. The merged list shows titles and faces and no times;
the date the count was read stays a title and screen-reader text.
The footer sits on the page's own ground under one hairline, as four columns
that fold into four disclosures on a phone, and the mirror links leave it for
/install#mirrors. With the navy band gone the stage roles and sea tokens go
too. The home's sky is the page ground itself, so the header paints the same
water and there is no seam under it. export-design-tokens.py now exports the
design kit's surface light, which the sky uses.
Removed with the old home: Strata, LivingHeroWhale and its light lattice,
the model chips, newest list, work-pattern grid, plugins heading, install
band and stats renderers, and the partner strip with its expiry helper. The
home reads only the facts and the committed activity snapshot, so it renders
with no request to GitHub and revalidates like /install.
Whale B's geometry is vendored unchanged with its provenance.
Checks, from web/: npm test 7 + 1291 passed (90 files), 0 failed; lint, tsc,
check:locales, check:tokens, check:docs, check:facts and build all pass.
Measured on the rendered page in en, zh and ar, light and dark, at 1440 and
375: no horizontal overflow from 320 to 1920 px in all 18 locales; site text
contrast at least 5.08:1; no animation under reduced motion.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The Git tool's review_scope action passed --no-lazy-fetch on every
command; git older than 2.44 rejects that option, so the action failed on
older git. The same protection is available as the GIT_NO_LAZY_FETCH
environment variable, which newer git honors and older git ignores. The
argument and environment are built in apply_review_git_options.
Checks: farm nextest -p codewhale-engine --lib git_tool: 8 run, 8 passed;
farm clippy -p codewhale-engine --all-targets -D warnings: clean;
cargo fmt --all -- --check and scripts/preflight.sh --check: OK.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The workspace arm serves four typed methods: read, list, write and exec.
Any other method is an unknown_method refusal and unknown fields are
invalid_input. Nothing is passed through.
Each act is one nested core call through the invocation's own context:
read_file, list_dir, write_file, or the Git tool's review_scope. So the
session's permission mode decides it like any other agent action (Ask
asks, Auto-Review judges, Full Access proceeds, Plan and deny lists
refuse), the per-invocation cap of 50 nested calls is the runaway
ceiling, and every act is receipted on the tool result.
Before the card the arm refuses when the live posture's deny list or a
child's grant would refuse the direct tool, and confines every path with
the existing resolve_strict_authority_path: relative, or absolute under
the workspace; no parent traversal; no symlink at any component, even
one pointing inside. The core tool receives the canonical path.
Confinement runs on the blocking pool behind a four-permit gate and stops
waiting on cancel.
exec takes a literal argv. argv[0] must be exactly git, and the rest
(after an optional --no-optional-locks) must equal one of the six
read-only metadata shapes the Git tool already runs, with cwd at the
workspace root if given. No shell, program path, -c or extra flag. The
Git tool runs it process-tree contained under a deadline and the call's
cancel token.
write is the read-write overlay. It is refused unless the plugin holds
the reviewed overlay grant, checked before any path is read; there is
no production source for that grant yet, so it is refused outside
tests. In the overlay, the workspace root and any .git or .codewhale
segment are refused. Writes are bounded to 1 MiB.
Refusals tell the plugin only what it needs; paths and gate messages go
to the log. The arm holds no state beyond the call.
src/dsh/workspace.ts is the matching facade, with bounds checked locally.
It is not registered and nothing is advertised: no service is granted.
Two seam tests that expected every unfilled stub to answer not_available
now skip the workspace arm, as they skip the network, remote and terminal
arms. The blocking-call budget records the two canonicalize calls in the
sync helpers that run only under spawn_blocking.
Known limits: a path is checked by the arm and resolved again by the core
tool, so a symlink swapped in between by a process that can write the
workspace is not caught here. Unmodified synchronous node:fs and
child_process calls stay refused by the Native sandbox; no OS sandbox
grant is added. The Git tool passes --no-lazy-fetch, which git older
than 2.44 rejects (exit 129), so exec fails on such hosts.
Checks (Linux farm): cargo nextest run --locked -p codewhale-engine --lib
-E 'test(extension_host)': 214 run, 214 passed, 6989 skipped, including
the seven new dsh_workspace unit tests and the new workspace broker test.
cargo clippy --locked -p codewhale-engine --all-targets -- -D warnings:
clean. Both on the tree rebased onto the grant slice. Extension host:
typecheck clean, build exit 0, node 455 tests 448 pass 7 skipped, bun 455
tests 449 pass 6 skip. cargo fmt --check and scripts/preflight.sh --check
pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Repair round 2.
- trust_reviewed now compares, inside the locked state closure, the
generation on disk with the one the registry read, for an agent
approver, and refuses with "state or review changed" when they differ.
check_approver's "no review held for every project" rule read the
registry's in-memory copy, so a registry read before a person's review
(a long-lived terminal session, or a race) let the agent replace that
review (re-review probe E). A plugin with no entry is generation 0 on
both sides, so a first approval after an install still passes.
- Ruling R22: check_approver refuses the agent when the plugin's valid
record is an agent's of another project (outside_reach); that agent
asks the person. Before, the agent of B could take over A's agent
record and turn the plugin off in A.
Tests: the_agent_cannot_replace_a_review_made_after_its_registry_was_read
and the_agent_of_another_project_cannot_take_over_an_agents_record. On
343a721580 both fail (farm run 20261111T132431Z-gaps-r2before-2bc61b:
15 run, 13 passed, 2 failed, unwrap_err on Ok). After: farm run
20261111T132426Z-gaps-r2test-b0f9bb, 438 run, 438 passed, 6990 skipped
(codewhale-plugins 231, engine extension_host 207). Clippy -D warnings
(20261111T132428Z-gaps-r2clippy-cadf9e) exit 0. preflight --check OK,
cargo fmt --check clean. Docs sentence names the other project's agent;
agent-docs regenerated.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Repair round 1, per coordinator ruling R21 (amends R4's "a folder in the
workspace"). An install lands in the user's plugin folder, so the scope
test of dec4b298c1 refused exactly the plugin R4 is about, and it let a
linked <project>/.codewhale reach outside the project. check_approver now
holds what the crate can know:
- no workspace-folder scope test;
- refuses the agent for a built-in plugin;
- refuses the agent when the plugin's current record is valid and belongs
to the reviewed cohort (a person, the command line, or a review from
before 0.10.3): ask the person. This also closes the review's finding 3,
where the agent's trust_reviewed replaced a person's record and walked
around enable_by's refusal;
- for a Workspace-scoped plugin, requires its canonical folder to sit
inside the canonicalized project; a canonicalize failure refuses.
enable_by keeps its record test (a record the agent made for this
project, not outside_reach) ahead of check_approver. CR-03 and the command
line check that the agent wrote and installed the plugin.
Tests: the_agent_never_replaces_a_review_held_for_every_project (probe D,
the command line, a pre-0.10.3 receipt, a built-in) and, on unix,
a_project_plugin_folder_that_resolves_outside_the_project_is_not_the_agents
(symlinked .codewhale refused; a real project folder accepted). The test
fixtures write plugins to the user folder again; the engine fixture
revokes the person's review before the agent reviews. Docs sentence
rewritten to R21; agent-docs regenerated.
Boat farm run 20261111T120302Z-gaps-r1test-7bff0d: 436 run, 436 passed,
6990 skipped (codewhale-plugins 229, engine extension_host 207).
scripts/preflight.sh --check OK; cargo fmt --check clean.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
web/data/agent-docs.generated.json regenerated with derive-facts.
scripts/preflight.sh --check: OK.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The plugins route called trust and enable, which record
{person, via: terminal}. It now calls trust_reviewed and enable_by with
PersonVia::App. The receipt stays in review_history, so the label is fixed
before the tag. The native-UI route test reads the record back.
Boat farm run 20261111T102948Z-gaps-test-4768b2: app-server plugin tests
11 run, 11 passed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
desired_owners read the record with .ok(), so a read error between
active_component_sources and this read inserted the owner with an empty
grant and its cohort unknown (G6 fail-open). A read error now keeps the
plugin out with a diagnostic naming the reason, and the scan also refuses
when the in-memory snapshot says the agent approved it. The loop is split
into desired_owners_from so a test can break the record between the two
reads.
Test: a_record_that_cannot_be_read_at_the_scan_loads_no_owner (turns the
plugin off from another registry between the reads). Boat farm run
20261111T102948Z-gaps-test-4768b2: 447 run, 447 passed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Closes three gaps the grant slice's review found in the crate itself, so
they hold whatever a caller does:
- enable_by now checks an agent approver as trust_reviewed does (its own
project, the thread, turn and tool call) and refuses when the plugin's
record was approved for every project (by a person or the command line)
or by the agent of another project. Before, an agent of project B could
turn on a plugin limited to project A, and an agent with no call identity
could turn on a plugin a person reviewed, which then ran everywhere.
R4 and section 148.5: outside the agent's scope the caller asks the person
through the normal card, so the approver it passes is then a person.
- check_approver refuses an agent approval unless the plugin was found in
the workspace's own plugin folder (scope Workspace), R4's "a folder in the
workspace". Before, the agent's review of a user-scope plugin a person had
reviewed replaced that review.
- PluginApprover::CommandLine with a review token is refused: the command
line shows no review, so it cannot record a host-service grant. Without a
token it records trust as before. A command line process carrying the
agent's mark is the agent (R14) and is checked as the agent.
The engine's grant test fixture writes its plugins into the workspace
plugin folder, because its agent-approved plugin must now live there.
Tests: four new or changed in host_service_tests (the three gaps, and the
command line test, whose agent now turns on only its own record). Boat farm
run 20261111T102948Z-gaps-test-4768b2: 447 run, 447 passed, 7837 skipped
(codewhale-plugins 228, engine extension_host 207, app-server plugin 11).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A failed output write (including the 30 s write limit), a non-EOF read error and
an over-8 MiB non-response frame used to end the connection and drop the prompt
task, so the interrupted turn never reached drive_prompt's settle and
checkpoint save. Each now records the failure, sends the prompt Eof and
notifies shutdown exactly as a clean EOF does; the output pump keeps draining
and discarding, the read error ends the input stream, and the oversize sink
frame is dropped. serve_connection returns the recorded error after the prompt
has settled.
Tests: the guard that used a failing emit closure (unreachable in production)
is restored to a real failing writer through serve_connection, with a failing
reader, a failing writer at the permission request, and an oversize-update sink
test. The three end-to-end tests fail on the previous commit (0 passed, 3
failed) and pass now.
Farm: lib acp_server 43 passed, acp_journey 2, acp_conformance 1,
single_turn_loop 14, clippy exit 0.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
CapturedAcpFrontend::serve keeps its signature and now drives an
agent-client-protocol v1 agent over our own bounded Lines transport: the SDK
owns framing, ids, ordering and error envelopes. Replaces the handwritten
read loop (AcpServer::serve), AcpDispatch, the write_* JSON helpers,
JsonRpcResponseIdPolicy, is_jsonrpc_response and the permission id counter.
- Inbound: BoundedLines stream, blank lines skipped, an over-limit line is
answered -32700 with the connection kept; daemon/input_closed (sent without
a jsonrpc member) ends the stream as EOF before the SDK sees it.
- Outbound: channel sink drained into the borrowed writer; an over-8 MiB
response becomes the same -32603, anything else ends the connection.
- Handlers are catch-alls over Value params, so today's validators, session
cap, posture checks and Codewhale methods are unchanged and requests before
initialize are still served. A prompt runs in cx.spawn behind a
synchronously set busy flag; cancel, permission answers and EOF reach
project_turn through an inbox. Every spawned task and callback returns
Ok, prompt failures answer -32603 through the responder.
- Frames are built from the official v1 types. modelSelection and models are
gone, list createdAt/messageCount move to _meta.codewhale, root sessionId
stays on session/load. Terminal auth methods only for clients that enabled
them, a permission request always carries text, notifications are never
answered, session/list, config setters and provider methods are served
while a turn runs, and the unused session cursor is deleted.
Tests changed: initialize_* (typed builder; modelSelection asserted absent),
the four wire-helper tests (replaced by one connection test), the image
update test (typed update), Rig::prompt and permission cases (run the real
agent over a duplex), status lists (omitted pending default counts as
pending). Added: oversize inbound, oversize outbound, session cap, mid-turn
requests. Conformance allowlist shrinks to the load sessionId row.
Farm (Linux): acp_journey 2 passed, acp_conformance 1 passed, lib acp_server
40 passed, core single_turn_loop 14 passed, clippy -D warnings clean.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A bundled skill was one text body. It can now carry files beside SKILL.md
(references/, templates/): install_companion_files writes each one only when
absent, so a file the person edited is never overwritten on upgrade, and an
edited SKILL.md still stops that skill's install as before. The four DSH
creator skills ship as generation 21 system skills, with every file equal to
the CR-00 pin; skills-catalog-matrix.json gains the four rows.
load_skill prints DSH's body unchanged, then 'Base directory for this skill',
then a 'Host notes: Codewhale' block, for an unedited bundled copy that
carries files or notes (bundled_skill_load_context). The notes are
Codewhale's own text, one short file per skill, and hold only what section 2.4
of the design says the agent is told instead of DSH's sentence.
Gating: the plugin_creator switch does not exist yet. The skills are visible
now; CREATOR_SKILL_NAMES lets the switch's slice hide them by adding the names
to the effective bundled 'disabled' list, with no file moved (a test proves
the policy hides them). Not met here: reading a template with the read tool
in Ask without approval needs the owned skills dir in
ToolContext.trusted_external_paths (engine.rs, not mine); requested in the
review packet.
Checks (farm, Linux): codewhale-skills nextest 188/188 passed; clippy
-D warnings clean on codewhale-skills (all targets) and codewhale-engine
(lib, tests); codewhale-engine tools::skill nextest 16/16 passed. One
existing test updated: the marker for a customised copy now moves to 21
because the new skills install. Local: cargo fmt --check, preflight --check.
Squashed with repair round 1 (03e4bfd7cd) at landing, per the second review (CW/artifacts/creator-mode-20261111/CR-00-01/REVIEW-r2.md): the four creator skills are hidden by default behind set_creator_skills_enabled (process-wide, off, no caller; MODE-01 replaces it with a per-thread flag); every installed skill folder carries DeepSeek's MIT LICENSE; no new code comments; CHANGELOG corrected. Review farm run on 1e2106e63a: 204 of 204.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Byte-for-byte copies, so Codewhale hosts DSH's own Creator text and tool code
rather than a rewrite: the 33 files of the four dsh-agent-preset skills, the
dsh-plugin-manager tools.js and dsh-tool-cordis index.js. Each pin has an
UPSTREAM.json naming package, version, upstream path and SHA-256 with
verbatim: true, plus the MIT licence text and a THIRD_PARTY_NOTICES section
naming the four packages. .gitattributes keeps LF so Windows checkouts hash
the same.
The dsh-sandbox approveEscalation excerpt is recorded as pending in
UPSTREAM.json: that package is not in the unpacked app and needs the
published tarball.
Checks: node --test creator-pin.test.mjs 3/3; cargo nextest run -p
codewhale-skills (farm) includes dsh_creator_pin 3/3, whole crate 188/188;
diff -r against the source skills folder empty.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
EXTENSIONS.md gains the host services section with who may approve, the
reach of each approver, that the agent's mark is a label it can remove
in Full Access, and the per-kind sharing rule. The CHANGELOG line says
plainly that Codewhale 0.10.2 turns plugins off on a state file in which
a plugin was reviewed or turned on by this release.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The global granted list is gone. A broker ticket is minted, and
redeemed, only when the service is offered by this build, the caller is
the current plugin-tier owner with a reviewed authority, no plugin
activation loaded into the shared host process has since left it, and
every live plugin-tier owner holds the service in its review record.
Plugins share one host process and loaded code cannot be unloaded, so
the rule counts what the process has loaded: once an owner is revoked,
superseded, failed or faulted, no service is given until that process
exits. The host is then retired by the existing planned restart when
idle, and reconcile loads only what is turned on. A built-in module
never gets a broker ticket.
Mint reads the owner record, which carries the grant read from disk when
the owner began. Redeem checks the rule again and re-reads the persisted
record of every loaded plugin, repeats that every 50 ms while the arm
runs, and once more before the answer is released, so a plugin turned
off, revoked or reviewed again from any process loses the service for
tickets already minted and for a call in flight.
A plugin that is refused gets one answer, not_granted, whether its own
review lacks the service or another plugin's does. The plugin that
withholds a service is named in host diagnostics only. A spent broker
ticket is typed limit instead of ticket_refused.
Limits shared by the host: a ticket revoked in the last 10 seconds is
refused but no longer counts toward the burst that ends the host, and
one cap of 50 nested core calls per tool call now covers core/call and
broker arms together.
The reconcile scan reads the reviewed record once per plugin (approver
and grant). An owner whose record says the agent approved it is not
loaded into the reviewed host: it fails closed with one diagnostic that
asks for a person's review. So the owner registry holds only the
reviewed cohort and its sharing rule, departed flag and withheld report
are that cohort's; agent-approved plugins get their own host later.
Checks (Boat farm, on this tree): nextest plugins + extension_host 427
run, 427 passed, 6981 skipped; clippy plugins and engine --all-targets
-D warnings exit 0; check app-server and tui --all-targets exit 0.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A plugin asks for host services in its manifest
(capabilities.host_services, the seven broker service names). The list
is part of the inventory and enters the capability hash only when it is
not empty, so every bundle that asks for nothing keeps its hash and its
review, and a bundle that changes what it asks for needs a new review.
A review grants: TrustReceipt gains version, approved_by and
host_services. trust_reviewed is the one writer. It takes the token of
the service review that was shown and recomputes that review under the
state lock; a different token refuses and records nothing. trust(name)
records a person in the terminal with no grant.
approved_by is one tagged shape: a person (via terminal, app or an
approval card), the agent (posture full_access, auto_review or
command_line, with the project and, for the first two, the thread, turn
and tool call), or the command line. PluginApprover::command_line gives
the agent's record when the calling process carries the agent's mark and
the command line's otherwise. An agent approver is refused without a
review token, without its call identity, or for a project other than
the registry's own, before anything is staged. enabled_by beside
enabled records who turned a plugin on and is cleared when it is turned
off or reviewed again. No per-service state is stored.
Cohorts: a record approved by the agent belongs to that project's agent
cohort, every other record to the reviewed cohort. The review lists as
peers only records of the same cohort, and its token covers the cohort.
A record approved by the agent is active only in the project it names
(LoadedPlugin::outside_reach, one more term in active()).
A grant is read back only for the exact reviewed content: enabled, same
generation and hashes, receipt version 2, every granted name among the
asked names, and the capability hash recomputed from the recorded
inventory. A receipt written before this change grants nothing. A
built-in review carried across an upgrade keeps the older receipt shape
when its prior review recorded no approver.
HOST_SERVICE_INJECTS is the one table from a DSH inject name to a host
service, and it is empty: this build offers nothing.
v0.10.2 refuses unknown fields in the state file. A test reads this
build's files through copies of v0.10.2's types: readable until a plugin
is reviewed or turned on here, then refused (unknown field version or
enabled_by).
Checks (Boat farm, Linux x86_64, on this tree): cargo nextest run -p
codewhale-plugins -p codewhale-engine --lib with the plugins package and
extension_host tests: 427 run, 427 passed (225 plugins, 202 engine),
6981 skipped. cargo clippy -p codewhale-plugins -p codewhale-engine
--all-targets -D warnings: exit 0. cargo check -p codewhale-app-server
-p codewhale-tui --all-targets: exit 0. Local: cargo fmt --check and
scripts/preflight.sh --check pass.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
From the third review (artifacts/queue-dispatch-20261111/CW-8021-S1/REVIEW-r3.md,
verdict land). Finding 1: docs/SANDBOX.md said a Fleet local worker reads only
the default managed path; it reads the user config's managed_config_path and
then the default path, so a user config can still point a worker at a missing
file (forwarding the variable to workers is a follow-up before slice 2).
Finding 4: the runtime info route reads the live configuration; a check made
once at start does not cover a later reload. Finding 2: the CHANGELOG line now
says a managed configuration named by the variable applies on every load and
a plugin-declared provider fails to load under it.
Checks: scripts/preflight.sh --check OK; agent docs regenerated.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
apply_managed_overrides took its path from config.managed_config_path, which
the environment override sets only after the provider identity resolves. A
user file whose provider is registered later by a provider plugin returned
early, so CODEWHALE_MANAGED_CONFIG_PATH was skipped and the launcher vanished
without an error. The variable is now read first (managed_path_from_env),
then the user file's path, then the default. Test: the late-provider load
either keeps the launcher or fails, never succeeds without it.
Also asserts sandbox.kind and sandbox.tool_launcher in the runtime-info test
and documents the sandbox object in RUNTIME_API.md.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The bubblewrap command builder gained two synchronous filesystem reads
for the tool launcher: one `metadata` per ancestor to decide whether the
launcher's user can search a directory, and one `canonicalize` per denied
path before lifting its mask. Both run only when a launcher is configured
and sit in the same synchronous builder that already canonicalizes its
roots, so the budget for crates/sandbox/src/bwrap.rs rises from 1 to 3
`std::fs` sites instead of moving the builder off the calling thread.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Review repair for the tool launcher key.
- The launcher is read only when the managed file is the one named by
CODEWHALE_MANAGED_CONFIG_PATH or the default managed path. A sealed file
that a user config reaches through `managed_config_path` is refused, so
the code now matches the error text and docs/SANDBOX.md.
- The sandboxed `sandbox_mode` that must sit beside the launcher is read
from the managed file itself. A user config's `sandbox_mode` no longer
satisfies it.
- A project config that sets `tool_launcher` stops the launch with a
project-config error instead of being silently dropped by the overlay
whitelist.
- docs/SANDBOX.md states all three, and tells a host that relies on the
launcher to set CODEWHALE_MANAGED_CONFIG_PATH and check
`sandbox.tool_launcher` in runtime info: without the variable a user
config can point `managed_config_path` at a missing file and the Engine
starts with no launcher.
Tests: one runtime test for the path refusal and the managed-only
sandbox_mode, one TUI test for the project refusal.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
A host that keeps tool commands away from the Engine's own files can now
name a `tool_launcher` argv prefix (for example `sudo -n -u <user> --
<exec-wrapper>`) in its managed configuration. The sandbox manager puts it
in front of bubblewrap, so the command becomes
`<launcher...> /usr/bin/bwrap ... -- <program>`: the uid change happens
before confinement, which is the only order that works, because every
unprivileged confinement sets no-new-privileges and a setuid launcher
inside it cannot switch user. Without the key nothing changes.
Config (crates/runtime): the key is `#[serde(skip)]`, so no config
document can set it. `apply_managed_overrides` reads it from the raw
managed file, beside `strip_external_credential_consent`, and refuses it
on non-Linux, in a managed profile, when it is not an absolute-program
argv, when the Engine's uid can replace the file through it or any
non-sticky writable ancestor, or when the managed `sandbox_mode` is not
`workspace-write` or `read-only`. A user config or user profile naming it
is a load error that points at the managed layer.
Sandbox (crates/sandbox), per the P0 review's conditions:
- Fail closed. With a launcher, `try_prepare` refuses instead of running
unwrapped: a policy that skips the sandbox (`danger-full-access`,
`external-sandbox`), `prefer_bwrap = false`, a failed launcher probe or a
non-Linux host. `prepare` keeps its infallible shape for callers that
cannot return an error (prepare_runner, the extension host) and turns a
refusal into a process that prints the reason and exits 126.
- Probe through the launcher. `bwrap::probe_launcher` runs the launcher
once for its uid and groups, then one wrapped `/bin/true` with the deny
masks, and caches success only.
- Masks for the launcher's user, on every command. bwrap running as that
user cannot create a mask under a directory it cannot search ("Can't
mkdir ... Permission denied"), and the built-in deny list names such
paths on a multi-user host. Each denied path is masked at the highest ancestor the
launcher's user cannot search (the P0 review's R4 rule); writable roots,
protected descendants, extra roots, device roots and readable paths it
cannot reach are dropped. Deny exceptions are not applied.
- Private /tmp. Under a launcher the host /tmp and TMPDIR are not bound;
each command gets a fresh tmpfs, so the tool user cannot leave files for
the Engine's user.
- PTY: `spawn_pty` finds the bwrap element and the first `--` after it,
because the launcher carries its own `--`.
Escalation clamp (crates/engine/src/tools/shell.rs): before this change
an approved `danger-full-access` escalation turned a managed
`workspace-write` call into DangerFullAccess, which the manager runs
unwrapped. Under a launcher the Bash tool now refuses that escalation
before the approval prompt, and the manager refuses it at execution.
Wiring: the key travels with the deny-list wiring in
runtime_threads/manager.rs, core/engine.rs (EngineConfig), the TUI
`sandbox run` command, exec_agent.rs and frame.rs (one EngineConfig line
needed to compile). `GET /v1/runtime/info` gains
`sandbox: { kind, tool_launcher }`.
Network (D2-A, Engine side only): a managed `sandbox_network_access =
true` with managed `sandbox_mode = "workspace-write"` already yields a
networked workspace-write policy under Ask; a test now pins that. No
default changes.
Known limits are written beside `SandboxManager::set_tool_launcher` and
in docs/SANDBOX.md "Running tools as another user". A real uid change is
not proved here: the farm runs as one user. The `/usr/bin/env` launcher
test proves the prefix, NoNewPrivs and the workspace write.
Checks (farm, Linux x86_64):
- cargo test --locked -p codewhale-sandbox -p codewhale-runtime:
runtime 918 passed, 0 failed, 2 ignored; sandbox 106 passed, 0 failed
(rerun on the final sandbox source: 106 passed; the /usr/bin/env
launcher test ran, not skipped).
- cargo test --locked -p codewhale-engine --lib (authority tests, sandbox
hint tests, launcher clamp): 22 passed, 0 failed.
- cargo test --locked -p codewhale-app-server --lib runtime_info, after
rebasing onto 34a29a0f2a: 8 passed, 0 failed.
- cargo clippy --locked -p codewhale-sandbox -p codewhale-runtime
-p codewhale-engine -p codewhale-app-server -p codewhale-tui
--all-targets -D warnings: clean (before the rebase, which applied
without conflicts).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A Runtime thread's receipt counted turns and printed them as provider calls,
which they are not. thread_tokens no longer sets the count, and both
docs/RECEIPTS.md samples drop it. The opening sentence now says what is true
for this change: tokens on every receipt, the cost line on terminal sessions
only; a thread receipt's cost follows in a later slice.
Regenerated web/data/agent-docs.generated.json.
Checks, rebased on origin/main fc78dc86d8 (Boat farm, Linux x86_64, local-test
evidence only): receipts::tests, metrics::, debug:: lib tests passed 43, 3, 28,
23 and 106 across the five crates, 0 failed, 0 ignored; clippy --all-targets
-D warnings on the same five crates exit 0. Locally: cargo fmt --all --check
and scripts/preflight.sh --check OK.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A receipt gains an additive usage section on codewhale.receipt/v1: provider
token counts and the report /cost prints, built by the same formatter
(cost_report) so wording and numbers cannot drift. An unpriced route reads
"Estimated total: unknown" with its reason; an amount appears only when a
turn had an authoritative price. A single-turn receipt leaves usage out and
says so; a Runtime thread receipt carries tokens and points to the usage
endpoint for cost.
codewhale metrics now counts per-call usage saved in terminal sessions on its
own line instead of saying none was recorded.
The /cost key map and replacement helper move from the TUI into the engine so
the CLI receipt renders the same English text. docs/RECEIPTS.md (and zh_hans)
say where cost lives and name the terminal-session gaps next to the example.
Checks (Boat farm, Linux x86_64, local-test evidence only): receipts and
metrics lib tests passed 43, 3, 28, 23 and 106 across the five crates, 0 failed.
web check-facts OK after regenerating agent-docs.generated.json.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The terminal arm serves six typed methods: list, run, send, wait, cancel
and reset. Any other method is an unknown_method refusal and unknown fields
are invalid_input. Nothing is passed through.
The owner stays tools/terminal_session. run, send, wait, cancel and reset
are each one nested core call on the existing terminal/* tool through the
invocation's own context, so planning, deny lists, the approval card, the
live posture, a child agent's grant, the per-invocation call cap and the
receipt on the tool result are the ones a core/call already has. list is a
read of the same registry, scoped to the invocation's workspace and thread.
A plugin's terminals are named dsh:<plugin id>:<name> inside the thread's
own owner, so a plugin cannot name the model's shells or another plugin's,
while the thread's existing terminal routes, resize, kill, delete-thread
cleanup and the restart instance id keep working on them unchanged.
Before the card, the arm refuses when the caller's shell policy or deny
list would refuse the direct tool (re-read from the live posture), when the
named terminal is not this plugin's, and when the plugin already holds 8
terminals in the session. Names, commands, input and timeouts are bounded.
Registry reads run on the blocking pool behind a four-permit gate and stop
waiting when the call is cancelled. A call whose cancel has fired does
nothing. Gate refusals reach the plugin as a short typed refusal; the
detail goes to the log. On platforms without the terminal owner every
method answers not_available after decoding.
Not reachable in production: no service is granted, the adapter
(src/dsh/terminal.ts) is not registered and nothing is advertised.
Known limits: list leaves a log line, not a receipt on the tool result
(arms cannot add one). A plugin's shells are not ended when the plugin is
disabled or revoked; they end with the thread. A failed command returns
its output without the exit code. No resize, kill or non-consuming read
for the plugin itself.
The two seam tests that expected every stub to answer not_available now
skip the terminal arm as they skip the network arm.
Checks (Linux farm): cargo nextest run --locked -p codewhale-engine --lib
extension_host: 191 run, 191 passed, 6979 skipped, including the four new
terminal tests. cargo clippy --locked -p codewhale-engine --all-targets
-- -D warnings: clean (run before a two-line test-only change). Extension
host: typecheck and build clean, node 451 tests 444 pass 7 skipped, bun 451
tests 445 pass 6 skip.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
cargo-deny's advisories job failed on ci/coord-push6 (run 42991087859) with
RUSTSEC-2026-0486, unsound, against rustix 1.1.4, which main also pins. The
advisory's solution is rustix >= 1.1.6. `cargo update -p rustix --precise 1.1.6`
also re-resolved windows-sys for nine crates that share it (colored, errno,
fd-lock, nu-ansi-term, terminal_size and others), each within its declared
range. Lockfile only.
Checks: hosted CI on this exact commit (cargo-deny, Windows, macOS, Linux).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The client still sends session/set_model to an agent that offers only the
pre-v1 models object. The check never saw that frame, so the deviation was
kept without being listed. A third leg drives the fixture agent with a plan
flag that offers the old models object, applies the model through it, and
allowlists the two frames that result: the client's session/set_model request
and the fixture's models field in its session/new response.
The two existing entries stay: the options fixture sends two fields that
exist only behind unstable schema features, and each entry names its feature.
Check on Linux x86_64: the_acp_client_and_its_fixture_agents_speak_official_v1_shapes
passed in the same 27-test run as the port.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The Engine's one ACP client keeps its whole public API and replaces only the
wire. Each client now runs one named connection thread with a current-thread
runtime that drives the official agent-client-protocol v1 client over the
worker's pipes, behind the shared bounded line reader. The fleet host still
owns the process; the SDK is given pipes only, and no process, stdio or
unstable feature is enabled. The caller's thread keeps its loop: one request
at a time, the same deadline and the same 25 ms startup-abort poll, now over
an event channel instead of raw lines.
What we send is built from the official types (initialize, session/new, load,
resume, set_config_option, set_mode, prompt, session/cancel, the permission
outcome and the elicitation decline). session/set_model stays hand-built: it
is a pre-v1 method with no official type, sent only to an agent that offers
the old models object. What an agent sends is still read as JSON, so legacy
fields keep working.
Every handler and response callback on the connection thread returns Ok and
reports failure to the caller as an event or through the responder, because
an Err there ends the whole connection. A request is consumed by its response
callback and never dropped, so a missed deadline sends nothing to the agent.
Wire changes: request ids are UUID strings; fields at their defaults may now
appear; session/resume no longer sends an empty mcpServers; a malformed
response from an agent is dropped, so its request fails at the deadline; a
malformed line that is not a response is answered with an id-null -32600.
Behavior changes beyond the wire:
- Sign-in is detected by the -32000 error code only, no longer by any error
whose text contains "auth".
- A permission request the agent withdraws with $/cancel_request is answered
cancelled, never with a selection. The handler is not interrupted; that
needs a change to the handler interface.
- An oversized or non-UTF-8 line ends the connection with the shared reader's
refusal text.
- An agent's error text now includes its string data, bounded.
Tests. invalid_acp_envelopes_and_protocol_versions_are_refused changes: the
case with no jsonrpc member and the case with neither result nor error now
fail at a 3 s deadline instead of at once, because the SDK drops a malformed
response; the protocol version 2 case is unchanged. Added: a line over 8 MiB
ends the connection unparsed; another session's update is ignored and its
permission request answered cancelled; a withdrawn permission request is
answered cancelled; a missed deadline sends no $/cancel_request and ids are
strings; and a runtime test that an agent ignoring session/cancel is stopped
after the 10 s grace with the killed reason, using a new fixture plan flag
(the runtime launches the fixture with an empty environment, so the flag is
read from the plan file like the fixture's other switches).
Checks on Linux x86_64: cargo nextest run -p codewhale-engine --lib --test
acp_client_conformance fleet::acp external_agent the_acp_client: 27 run,
27 passed. cargo clippy -p codewhale-engine -p codewhale-app-server --tests:
clean. Windows was not compiled.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The comment above the retry loop in publish_private_state_file still said
every holder of the stable file lets go quickly and that only the rename is
re-attempted. Since fc78dc86d8 a plugin UI grant can hold the file for up to
an hour, and access denied falls back to std::fs::rename with a flush through
a handle opened beforehand. Wording from the review in
artifacts/win-plugin-ui-20261111/REVIEW.md, question 8.
Known limit, recorded here since the fix's commit message omitted it: Windows
10 before 1709 and Server 2016 lack POSIX rename, so the 409 remains there for
as long as a grant lives.
Checks: rustfmt --check on the file. Comment-only.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The snapshot and the topic builder move to lib/release-docs.ts, imported
only by /llms-full.txt and /docs-markdown. llms.txt reads a small
agent-docs.release-index.json (tag, present and omitted slugs) that the same
script writes and the check compares with the snapshot. agent-docs.ts is
back to the main-branch file only, so the docs pages no longer bundle it.
- release.yml, job sync-release-record only: run derive-release-docs after
derive-facts, check-release-docs in the prove step, and carry the snapshot
and its index in the proposed paths.
- derive-release-docs now exits 1 when the tag is not in the checkout and the
committed snapshot is for a different release than the published one.
- web/maintainer/derived.txt no longer lists the snapshot: that job checks out
without tags and cannot regenerate it.
- The zh runtime-reference description drops the words for "main branch".
- A source missing at the tag reads "<path> was added after <tag> and is not
part of this release; the document below is what <tag> has."
Checks: npm test 91 files / 1304 tests, lint, tsc, check:locales, check:docs,
check:facts, build, and scripts/preflight.sh --check pass.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The machine-readable docs embedded main-branch documents under a label that
said so, so they could describe unreleased work. They now serve a committed
snapshot of the documents as they were at the published release tag.
- web/data/agent-docs.release.json is produced by derive-release-docs.mjs from
`git show <tag>:<path>` for every registered docs source, and records the tag
it was made from. Run by prebuild; with no tag in the checkout it keeps the
committed file and warns, as the release baseline does.
- check-release-docs.mjs (in check:facts and preflight --check) fails when the
snapshot tag differs from the published release read by facts-lib, when a
hash does not match, when a registered topic is missing, and, if the tag
resolves, when the snapshot differs from the tagged source.
- data/agent-docs.generated.json and its check are unchanged and still feed
the docs pages.
- A topic with no source at the tag is omitted from the routes and the index.
A topic with some sources keeps them and says which file does not exist at
the tag: today only docs/RUNTIME_ROUTES.md.
- The runtime reference description no longer says "main branch".
Checks: npm test 91 files / 1302 tests, lint, tsc, check:locales, build, and
scripts/preflight.sh --check pass.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
After a hard kill, the Engine's socket owner record can outlive both the
process and its data directory. Every later start then failed with
"Failed to resolve ... tasks/runtime".
Host startup now passes its selected store into the owner probe. The
record is retired only when all of these hold: the owning process is
dead, the record's data directory equals the selected store's root in
the form RuntimeStoreBinding::for_store_dir records
(checked_runtime_store_root, which creates nothing), and that root is
definitively NotFound. The only path inspected is the selected store's
own; the recorded path is compared, never followed. A record naming
another store, or the selected store while it still exists, keeps the
existing refusal and stays on disk, which is what the test
stale_publication_cannot_select_another_store_or_execution_scope
protects.
admit_runtime_host passes the store it already holds: one call-site line
in runtime_api.rs.
The regression probes first with a different selected store while the
named store is gone and requires the record to stay, then with the named
store and requires the discard.
Known limits. For a store that is gone, the root can only be normalised
lexically, while a host records the symlink-resolved root. A selected
store spelled through a symlink (a home under macOS /tmp or
/var/folders), one spelled in another letter case, and a Windows
verbatim root do not match, so those starts keep the existing refusal.
A store that is only temporarily absent, such as an unmounted volume,
now gets a first start with an empty store, as it already does after a
clean stop; no store is deleted.
This squashes the first change and its repair after review.
Checks (Linux x86_64 build farm, cargo nextest, CARGO_INCREMENTAL=0,
RUST_MIN_STACK=8388608):
- run 20261111T063638Z-stalefix-nextest-fc68fe: 58 passed, 0 failed
- control with the previous probe behaviour, run
20261111T064458Z-stalefix-control-523e53: 16 passed, 1 failed (the
regression, as intended)
- clippy for the crate with CI's flags, run
20261111T070931Z-stalefix-clippy2-ba438c: exit 0
- independent review rerun on a later base, run
20261111T073145Z-rvstale2-nextest-216f38: 58 run, 58 passed
Not run: Windows, macOS, hosted CI, an installed binary.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The trusted local ACP profile now offers reviewed plugin tools and the tools
of MCP servers configured in Codewhale, under the same permission setting.
The reference still said MCP was unavailable on this transport, which
contradicted docs/EXTENSIONS.md. Client-supplied MCP servers stay unavailable.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The journey looked for the workspace path as a substring of the frames
re-serialized as JSON. A Windows path contains backslashes, which JSON
escapes, so that substring can never match there, and the three-OS suite runs
this test on every landing. The test now parses the plugin's completed tool
result, takes its workspace value and compares it with the session's
workspace after canonicalizing both, so it also holds where the temporary
directory is reached through a symlink or a short name.
Checks on the stack tip (farm, Linux x86_64, cargo nextest; run
20261111T062016Z-acpplugland-tests-74e101: 97 run, 97 passed, 0 failed):
- acp_journey: 2 passed, 0 failed
Not run: Windows and macOS, which are the platforms this change is for.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The ordinary tool build drops MCP tools for a child engine. The ACP build now
does the same: for a child it adds no MCP tool to the catalog or to the turn's
allow list. No child engine runs an ACP turn today (children use the Child
profile and ACP turns offer no spawn tool), so this closes a path that cannot
be reached yet and keeps the two builds from drifting apart.
acp_tool_build_with_mcp becomes pub(super) so the child-host tests can call
it. The new test child_engine_gets_no_mcp_tools_from_the_acp_tool_build hands
the same live MCP tool to a root ACP engine (in the catalog, on the allow
list) and to a child engine (in neither).
Checks on the stack tip (farm, Linux x86_64, cargo nextest; run
20261111T062016Z-acpplugland-tests-74e101: 97 run, 97 passed, 0 failed):
- codewhale-engine tests matching acp, host_profile or child_host:
40 passed, 0 failed, the new test among them
- cargo clippy --workspace --all-targets --all-features --locked (CI flags):
exit 0, no lint output (run 20261111T062101Z-acpplugland-clippy-4c7ea7)
Not run: the new test against the build without this change. It asserts on
the three values the change alters (the MCP names, the catalog and the allow
list), and the root half shows they are set when the engine is not a child.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The ACP tool build now installs the same extension-host plugin tools and the
session's connected MCP tools that an ordinary turn gets, through the same
host attachment and MCP pool; nothing new loads or registers tools. Plugin and
MCP calls reach the editor as ACP tool calls and ask through the permission
setting. initialize advertises the real state under agentCapabilities._meta
(plugins and configuredMcpServers follow the extension_host and mcp flags;
client-supplied mcpServers stay refused). acp_journey drives one fixture plugin
tool and one fixture MCP tool from an ACP prompt.
Landing: the journey test connects through the shared ACP test client
(tests/acp_support) that the schema check introduced, the ACP line in
CHANGELOG.md says what is now attached, and the agent docs are regenerated
for the EXTENSIONS.md sentence. This commit does not build alone: the next
one calls acp_turn_tool_build.
Checks on the stack tip (farm, Linux x86_64, cargo nextest, CARGO_INCREMENTAL=0,
RUST_MIN_STACK=8388608; run 20261111T062016Z-acpplugland-tests-74e101:
97 run, 97 passed, 0 failed):
- acp_journey: 2 passed, 0 failed
(editor_drives_a_session_through_permission_cancel_and_load,
plugin_and_mcp_tools_run_as_acp_tool_calls_through_the_permission_dial)
- acp_conformance: 1 passed, 0 failed
- acp_client_conformance: 1 passed, 0 failed
- single_turn_loop: 14 passed, 0 failed
- codewhale-app-server tests matching acp_server: 39 passed, 0 failed
- codewhale-engine tests matching acp, host_profile or child_host:
40 passed, 0 failed
- cargo clippy --workspace --all-targets --all-features --locked (CI flags):
exit 0, no lint output (run 20261111T062101Z-acpplugland-clippy-4c7ea7)
- Mac: cargo fmt --all -- --check clean; scripts/preflight.sh --check OK
Not run: Windows, macOS, hosted CI, a real editor.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Fable 5.2 <[email protected]>
DSH-13. The remote arm decodes four typed methods (status, pair, clients,
revoke) and refuses any other method as unknown_method. A plugin never
receives a pairing secret or a client token: pair returns only a grant id,
the host reads the secret through RemoteGrants::awaiting_host to present
it to the person, a remote client redeems it once (a replay is refused),
and RemoteGrants::authorize admits that client only for the session the
grant was made in. Grants are bound to plugin id and session, so another
plugin or another session cannot list or revoke them, and they outlive the
invocation that asked, so a later invocation in the same session can
still revoke them. Pairings expire after 10 minutes, clients after 30, and
at most 4 pairings may await the host per plugin and session. No public
listener or tunnel exists: status says so and listen/tunnel are unknown
methods.
Known limits: the Runtime API does not yet read or redeem these grants
(exact request in the review packet), and whether a plugin may use the
remote service at all is not checked before the arm (SEAM-CONTEXT).
The TypeScript side adds the remote adapter (ctx.remote) that forwards
each method through host.broker with a typed payload, plus its
compatibility name and regenerated dist.
Checks, on the landing tip (this commit with the test commit after it):
- Linux x86_64: sh scripts/with-hermetic-test-home.sh cargo nextest run
--locked --no-fail-fast -p codewhale-engine -E 'test(extension_host)'
191 tests run: 191 passed (1 slow), 6981 skipped
- Linux x86_64: cargo clippy --workspace --all-targets --all-features
--locked -- -D warnings -A clippy::uninlined_format_args
-A clippy::too_many_arguments -A clippy::unnecessary_map_or
exit 0, no warnings
- extension-host: npm run typecheck exit 0; npm run build exit 0, tree
unchanged; npm test 451 tests, 444 pass, 0 fail, 7 skipped
Tests under the broker grant check (folded in from the landing):
The remote arm lands on top of the one grant check: broker tickets are
minted only for granted services, and no service is granted outside
tests. Two tests have to say so:
- the remote journey test grants workspace and remote for its own thread
with TestGrants, as the network arm's test does, so it gets the remote
ticket it pairs with and the workspace ticket it presents as the wrong
service;
- the grant test's "the landed stub still refuses" assertion skips remote
as it skips network: the filled arm decodes first and answers
unknown_method for the probe method. The remote journey test asserts
that refusal through the real table.
No production change. The remote service is granted to no plugin, so no
remote ticket is minted and the arm cannot be reached until a plugin's
reviewed record grants it.
Checks, on this commit (Linux x86_64): the engine's extension_host tests
under cargo nextest, 191 tests run: 191 passed (1 slow), 6981 skipped.
The remote journey test, the grant test and the ticket test passed by
name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Fable 5.2 <[email protected]>
On Windows, disabling or revoking a plugin whose UI had just been opened was
refused with 409:
disabled failed for 'panel': failed to atomically and durably persist
...\runtime\plugins\state.json: Access is denied. (0x80070005)
A plugin UI grant keeps the plugin state file open for up to an hour, so that
every asset request can prove the state file is still the one the grant was
minted against. The Windows publication replaced the file with MoveFileExW, and
Windows refuses that replacement for as long as any handle is open on the
destination, whatever its share mode. The bounded retry around it was sized for
a scanner or an ACL pass that lets go within about a second, so it ran out and
the refusal surfaced. While any grant was live, every state change of every
plugin was refused, from this process or another: trust, enable, disable,
revoke and the state prune of an uninstall.
When MoveFileExW answers access denied, publication now opens the hardened
temporary and falls back to std::fs::rename, which replaces the destination
with POSIX rename semantics. A reader that shares delete access keeps its
handle on the superseded file and no longer blocks the replacement, which is
what Unix already did. That path does not write through, so the published file
is flushed through the handle opened before the rename, and a flush failure is
reported. A holder that denies delete sharing still gets the bounded retry and
then the refusal, with the stable file untouched.
The grant code is unchanged. After a replacement the grant's handle identifies
the superseded file, its identity check fails, and the grant is stale, as on
Unix.
Not covered: a volume without POSIX rename (FAT, exFAT, some network shares)
keeps the old refusal while a grant is open.
The plugin UI tests now print the response body when disable, enable or revoke
is refused; a failing run used to show only the status.
No released version is affected: the grant arrived after v0.10.2, so there is
no CHANGELOG line.
Evidence
- Hosted Windows before the fix (run 42972872196, the two test changes alone):
the new registry test failed with "failed to atomically and durably persist
...\state.json: Access is denied. (0x80070005)" (3 run, 2 passed, 1 failed),
and plugin_ui ran 3, passed 1, failed 2 with the 409 above for disable and
the same error for revoke.
- Hosted Windows with this change on main ecfbdbccaa (run 42978481553):
codewhale-plugins --lib 200 run, 200 passed, 1 skipped; plugin_ui 3 run, 3
passed under nextest in the hermetic test home, and "test result: ok. 3
passed; 0 failed" under cargo test. Run 42975064244 gave the same result on
main 8ddd9025c0.
- Linux build farm with this change on main ecfbdbccaa: nextest 214 run, 214
passed (3 plugin_ui, 211 codewhale-plugins). On main 8ddd9025c0, clippy for
both crates with all targets and all features was clean.
- cargo fmt --all -- --check and scripts/preflight.sh --check pass.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Every /docs-markdown topic except install embeds the repository's
documents as they are on main, and /llms-full.txt carries all of them after
the llms.txt text. Those documents describe behaviour the published release
does not have (a mode set without Operate, and commands and settings that
exist only in source), and nothing in the served text said so: the modes
topic was introduced with the released description and then contradicted
by the document under it.
Each of those documents now starts with the sentence the reference pages
already show for a main-branch document, naming the published tag, and a
link to the repository at that tag. The sentence is the existing
REFERENCE_GUIDE_COPY.scope string; nothing is added to a dictionary. It is
left out when the release is unknown, as on the reference pages.
This labels the statements; it does not remove them. Serving the documents
as they are at the release tag needs the generator in
scripts/docs-sources-lib.mjs to read the tag, which this change does not
touch. A link per document at the tag is not printed because a document can
exist on main and not at the tag.
Test: one more case in lib/llms-txt.test.ts counts the label in the full
text: once for every topic except install. Removing the label fails it.
Checks from web/: tsc --noEmit and eslint are clean; vitest on the four
files that read these texts, 403 of 403.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
An agent asked how to install Codewhale and what it can do today reads
/llms.txt. The file now opens with the locked headline and subhead and says
"The latest release is v{version}." from the published-release object.
The install text is rendered from lib/content/install.ts, the module the
/install page renders: one command per platform and channel, what the
installer adds to PATH and the opt-out, the verify command and its output,
the three first steps, usage counting and how to turn it off, update,
removal, known problems, the other channels and the mirrors. The page's own
conditions pick the wording: the uninstall command appears only when the
published release has it, and the version-free strings are used when the
release is unknown.
The six parts of the product are printed with their state words from
lib/content/product-map.ts and the locked map strings. A row whose computed
state no longer matches the state its sentence was written for prints the
state without the sentence, so a desktop release object flips that line to
"Released" and drops "no public download yet".
/docs-markdown/install.md serves the same install text as a page, in place
of the main-branch install guide, with the facts stamp at its end. Like
/install, it links the full guide at the release tag. /llms-full.txt
carries the new head and that page.
Removed from llms.txt because they contradict the released version or the
copy rules:
- the old identity line and summary sentence;
- the publish date on the release line (the changelog carries it); the date
the release was read stays, inside the provenance stamp;
- the "Unreleased on main" list of changelog headlines;
- the entries for pages that now redirect (product, runtime, constitution,
roadmap, feed, digest, community) and for /signin, which the sitemap no
longer lists;
- the hand-typed states on the /app entry, which now prints the typed
surface states the page prints.
The install entries use the install page's own description.
Not changed: the routes stay static and read the build's facts, while
/install revalidates against the runtime facts snapshot, so after a new
release the page can name it before the text does, until the next deploy.
The licence, default-model and sandbox lines still read the source tree.
Tests: lib/llms-txt.test.ts is rewritten around invariants (9 tests): the
head and the index, no link to a redirected route, the version equals the
release object and the source version never appears, every statement the
rendered /install page makes from the install content is in both texts,
each map row with its computed state and a fixture desktop release flipping
only the desktop line, no price, plan or date outside the stamp, unknown
stays unknown, and the routes. The three assertions in
lib/facts-invariants.test.ts that quoted the old release line now quote the
new one. Nine single-line mutations of the builders each fail one of these
tests.
Checks from web/: prebuild, check:facts, check:docs (29 topics) and
check:locales pass; tsc --noEmit and eslint are clean; node --test 7 of 7;
vitest 90 files, 1294 of 1294; next build succeeds.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Adds agent-client-protocol =5.2.0 (default-features = false; no process,
stdio or unstable_* feature) as a workspace dependency of codewhale-engine
and codewhale-app-server, ground for porting both ACP directions onto it.
Moves BoundedLines, FrameRefusal and is_frame_refusal from the app-server
stdio module into codewhale_util::bounded_lines so the server and the client
directions share one 8 MiB reader. stdio.rs re-exports the three items, so
its call sites are unchanged, and a const assertion keeps the new
MAX_FRAME_BYTES equal to MAX_RUNTIME_IMAGE_BODY_BYTES. The two framing tests
that read the private pending buffer move next to the reader.
Lockfile: 11 packages added, none existing moved. The resolver also
re-pointed Windows-only windows-sys edges and tempfile's getrandom edge;
those edges were restored to the previously locked versions, so the only
removed lines are schemars/indexmap name disambiguation. cargo deny bans and
licenses stay ok with 10 duplicate warnings before and after.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds agent-client-protocol-schema =1.17.0 (default features off, stable
v1 only) as a dev-dependency of codewhale-app-server and codewhale-engine;
`cargo tree -e normal` shows no runtime path to it.
A shared checker (crates/engine/tests/support/acp_conformance.rs)
deserializes every frame into the official v1 type for its method,
re-serializes it and fails on any key the official type drops (outside
_meta), on a non-v1 method, and on a reply to no outstanding request.
Known deviations live in one allowlist per test; a new deviation or an
allowlisted one that disappears fails the test.
- Server: acp_conformance drives the real ACP frontend through
initialize, $/cancel_request, error paths, session/new, set_mode,
set_config_option, a prompt with a tool call, permission and plan tool,
a cancelled prompt, session/list and session/load with replay, with
requests built from the official types. 7 allowlisted deviations.
- Client: acp_client_conformance runs fleet::acp::AcpClient against both
fixture agents through a capture wrapper (acp-tee.mjs) without touching
the client or fixtures. Our client's frames and acp-agent.mjs are clean;
acp-options-agent.mjs sends two unstable-only fields (allowlisted).
- acp_journey now shares the harness in tests/acp_support.
No runtime code or wire behavior changes. Findings and the
schema-vs-SDK decision table are in the task artifacts.
acp-tee.mjs starts a fixture agent script as a child and appends both
directions of its newline-framed stdio, in arrival order, to a log file:
"C <line>" for what the client wrote, "A <line>" for what the agent wrote.
The client test calls new_session and reopen_session with REF-09's
identity and additional-directories arguments (empty list).
Lockfile: only the schema crate's packages are added (agent-client-
protocol-schema, bs58, hashbrown 0.12.3, indexmap 1.9.3, schemars 0.9.0,
serde_with, serde_with_macros, time-macros) plus the indexmap/schemars
name disambiguation and the deranged/time feature edges; no existing
package moves. The resolver's re-pointing of Windows-only windows-sys
edges and tempfile's getrandom edge was restored to the locked versions.
Checks on the stack tip with ACP-SDK-S0 (Rust sources and lockfile
identical to farm runs 20261111T052504Z-acpland3-tests-edecfa and
-052503Z-acpland3-clippy-5bd65b; main has since moved in web/ and docs
only; Boat farm, Linux, RUST_MIN_STACK=8388608):
- acp_conformance: 1 passed, 0 failed
- acp_client_conformance: 1 passed, 0 failed (node v24 present, no skip)
- acp_journey: 1 passed, 0 failed
- codewhale-engine --lib fleet::acp::: 6 passed, 0 failed
- cargo clippy --workspace --all-targets --all-features --locked (CI
flags): exit 0, no lint output
- Mac: cargo fmt --all -- --check clean; scripts/preflight.sh --check OK;
cargo deny --offline check licenses bans ok, 10 duplicate warnings,
same set as base; cargo metadata --locked --offline ok
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review repair: rustfmt wrap of the new test line, oldest-first draft pop so a
backlog is not sent out of order, ordering test added, tautological
rejected-key test removed (failed_*_activation rollback tests cover it).
The doc comment on cancel_queued_draft_edit no longer says the draft goes
back where pop_last_queued_into_draft took it from; it goes back at the tail.
Checks (Linux x86_64, on the landing stack):
- cargo test --locked -p codewhale-tui --lib -- route_activation offline_label
tui::ui::tests::failed_custom_provider_activation
tui::ui::tests::failed_native_xai_oauth queued
test result: ok. 38 passed; 0 failed; 0 ignored; 5706 filtered out
- cargo fmt --all -- --check: clean
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
choose_offline_explore sets offline_mode, but route activation through
switch_provider only cleared the explore label, so every later message was
held as "Saved for later" until restart. clear_offline_explore_on_route_activation
now also clears offline_mode and returns a held message to the composer; the
rejected-key path (apply.rs) still sets offline_mode and is unchanged.
Checks (Boat farm, RUST_MIN_STACK=64MiB, cargo test -p codewhale-tui --lib,
filters tui::onboarding, route_activation_after_offline, tui::ui::tests::failed_,
provider_, reselecting): 68 passed, 0 failed. Without RUST_MIN_STACK two
failed_*_activation tests overflow the stack on unmodified main too.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The network arm decodes four typed methods (fetch, credentials.describe,
credentials.resolve, credentials.set); any other method is a typed
unknown_method refusal and malformed input is invalid_input. No pass-through.
Authority is a NetworkAuthority value: the plugin's reviewed network hosts,
the invocation's ToolContext (its network policy decider) and the credential
vault. BrokerCall carries none of these yet (SEAM-CONTEXT has not landed), so
serve() runs with a withheld authority and every method that would act
refuses with not_available. serve_with() holds the whole behaviour and is
what SEAM-CONTEXT's fields plug into; nothing is advertised to plugins.
fetch reuses the existing owners: the host must exactly match a reviewed
network host; then tools::web::guard::validate_fetch_target applies the
session's deny list and approval posture and the SSRF/DNS guard, and the
connection is pinned to the validated address through
codewhale_net::guard::guarded_reqwest_client_builder (no ambient proxy).
Redirects are followed manually, at most five, only within the requested
origin (scheme, host, port), and each hop is re-validated, so a redirect,
rename or rebinding never widens the grant.
Credentials live in codewhale_secrets under dsh-plugin:<plugin>:<ref>.
resolve and describe return only the handle codewhale-credential:<ref>;
fetch substitutes the secret into header values only after the hop passed
egress, refuses handles in the URL, and scrubs the secret back to its handle
in response headers and body. Logs name plugin, host, ref and status only.
TypeScript: src/dsh/network.ts adds credentialsAdapter (resolve, describe,
set, fetch) and brokeredFetch over AdapterHost.broker, unregistered until
the Rust authority is wired.
core_call_tests.rs: the seam's all-services not_available loop skips
network (its arm now answers unknown_method for "anything"), and one new
regression drives the real TABLE through serve_broker_in with minted
tickets: unknown method, withheld authority, another plugin, another
service's ticket, the core ticket, an ended invocation, a revoked owner.
Checks (farm, Linux x86_64, base 4133ed98ec):
- sh scripts/with-hermetic-test-home.sh cargo test --locked -p codewhale-engine --lib -- extension_host
test result: ok. 182 passed; 0 failed; 0 ignored; 6975 filtered out
- extension-host: npm run typecheck exit 0; npm run build exit 0, tree unchanged;
npm test 448 tests, 441 pass, 0 fail, 7 skipped; npm run test:bun 442 pass, 6 skip, 0 fail
Co-Authored-By: Claude Opus 5.5 <[email protected]>
DSH-16 SEAM-CONTEXT. A DSH broker arm used to get only owner, session,
service, method and input. It now acts inside the invocation whose ticket
was redeemed, and nowhere else. This is additive: the Arm type,
BrokerFuture, every serve signature and the seven dsh_<service>.rs stubs
are unchanged.
- BrokerCall gains one field, `context: BrokerContext` (defined in
core_call.rs and re-exported from dsh_broker):
- tool(): the invocation's own ToolContext, captured at begin_scoped,
with its nested-call gate removed. It carries the workspace, sandbox,
approval and trust posture, disallowed tools and session objects.
Nothing in the host's params feeds it.
- cancel(): the call's token. It is a child of the invocation's token
and fires when the host cancels the request, the invocation ends,
the owner is revoked, the host exits, or serve_broker_in returns.
So work an arm spawns (processes, tasks) can clean up after itself.
- core_call(name, input): a nested core tool call through the
invocation's CodemodeInvoker. It takes the same refusal list, gate,
origin approval, receipts and 4-at-once cap as core/call, and the
call's token withdraws it, open approval card included. All brokers
of one invocation share a cap of 50 such calls. Failures map to the
typed BrokerRefusal::Denied or ::Refused.
- Grant: dsh_broker::granted(service) is the one check. It is applied
when broker tickets are minted (only granted services are ticketed, no
longer all seven), and again in serve_broker_in before the arm runs.
An ungranted service is refused with the existing NotAvailable,
identical to an unbuilt arm, so the refusal reveals nothing. The
production grant list is empty, so no broker ticket is minted today.
Plugin authors see the same behavior as before: every broker service
refused. Tests set grants with dsh_broker::TestGrants. The Engine holds
no per-plugin declaration of the broker services a plugin uses (inject
lists are checked only by the TS host at activation). The source for a
per-plugin grant is a coordinator/founder question in the review
packet.
Known limits: the 50 nested-call cap is separate from the core/call
ticket's 50 uses. cancel() is per broker call, so long-lived resources
(terminals) need an owner-scoped lifetime from their own Runtime owner.
TestGrants is thread-local, so a broker test runs on a current-thread
runtime.
Checks (farm, base 4133ed98ec, these three files shipped):
- with-hermetic-test-home cargo test --locked -p codewhale-engine --lib
-- extension_host: 181 passed, 0 failed, 0 ignored, 6975 filtered out
(run 20261111T034028Z-seamctx-test-8d29a5). Five new tests and the
updated seam test pass by name.
- cargo clippy --locked -p codewhale-engine --all-targets -- -D warnings:
exit 0, 0 warnings (run 20261111T034030Z-seamctx-clippy-ace2ad).
- bash scripts/preflight.sh --check: preflight: OK (local).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Adds the id the Token Plan Supported Models table lists to the reviewed
roster, public list and Token Plan picker, both dialects. The vendor page
https://qwencloud.com/pricing/token-plan (verified 2026-11-11, also read by
the founder 2026-11-10) lists qwen4-max-preview as New with Text Generation,
Reasoning Model and Visual Understanding. Default stays qwen3.8-max.
The page publishes no context window, maximum output, tool calling or
thinking control, so none is recorded. The intrinsic row leaves reasoning
unset and the Token Plan transport no longer claims reasoning support, so
neither dialect sends a thinking field: the OpenAI dialect strips every
thinking field for an id in neither Model Studio thinking list
(apply_modelstudio_route_reasoning_controls in chat.rs), and on the
Anthropic dialect thinking_capable is false, so build_anthropic_body sends
neither a thinking block nor output_config, for any effort including off.
Visual Understanding is vendor-stated, so the transport records
image_input as the sibling Token Plan rows do. The Model Studio Messages
test now pins that no thinking field is sent for this id.
The Coding Plan completion rosters were derived from the Token Plan
transports; they now list their seven ids explicitly so the preview is not
offered there, and the Coding Plan rows in PROVIDERS.md, zh_hans/PROVIDERS.md
and config.example.toml say it omits qwen4-max-preview. The Token Plan rows
in both PROVIDERS.md copies say what is and is not recorded for it. Agent
docs and website facts regenerated.
Not exercised against the live endpoint (founder decision, no live calls):
that the endpoint accepts the id on each dialect, its limits, tool calling
and how it thinks are unproven.
Amended at landing per the coordinator's vendor read: reasoning dropped
from the intrinsic row, image input recorded, receipt cites the verified
vendor page.
Checks: seed render --check ok; seed age ok (0.3 days); catalog_models_dev_test 21 tests OK; web check-facts OK; check-docs PASS; check-runtime-routes PASS; preflight --check OK. Farm (Linux x86_64), stack tested on main a37d7fdd27; the rebased tree differs from the tested one only by main's own commits up to 1f9ad30c1f: cargo nextest run -p codewhale-config -p codewhale-models -p codewhale-engine (engine filtered to client::anthropic), RUST_MIN_STACK=8388608, 989 passed / 0 failed (config lib 834, configured_models 9, engine client::anthropic 38, models 108), run 20261111T043921Z-catalog8038final-test-193af4; CI-style clippy -D warnings --all-targets --all-features for config, models and engine, run 20261111T043925Z-catalog8038final-clippy-40cd61, exit 0, no warnings. The extended modelstudio_messages_body_requests_thinking_with_budget passed in that run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Supersedes pull request 7286. The same discovery, rebuilt on current main with
the review's changes: mistral-large-4-0-8 goes to [discover].skip (Mistral does
not list that id); mistral-large-4 is dated 2026-11-05 per Mistral's page;
muse-ember-1.0 is held (Meta publishes no limits or prices); MiniMax-M3.1-Flash
carries no max_output because MiniMax does not state one. The lock is left at
main's pin so no unreviewed price or limit drift comes in.
apply_discoveries maps the models.dev vendor to a ModelFamily variant through
VENDOR_FAMILY instead of copying the public-list label into the family field
(which the config crate rejects, panicking bundled_models_dev_catalog()). One
unittest pins that every discover vendor maps to a variant parsed from
reviewed.rs. This commit is independent of the qwen4-max-preview commit.
Checks: seed render --check ok; seed age ok (0.3 days); catalog_models_dev_test 21 tests OK; web check-facts OK; check-docs PASS; check-runtime-routes PASS; preflight --check OK. Farm (Linux x86_64), stack tested on main a37d7fdd27; the rebased tree differs from the tested one only by main's own commits up to 1f9ad30c1f: cargo nextest run -p codewhale-config -p codewhale-models -p codewhale-engine (engine filtered to client::anthropic), RUST_MIN_STACK=8388608, 989 passed / 0 failed (config lib 834, configured_models 9, engine client::anthropic 38, models 108), run 20261111T043921Z-catalog8038final-test-193af4; CI-style clippy -D warnings --all-targets --all-features for config, models and engine, run 20261111T043925Z-catalog8038final-clippy-40cd61, exit 0, no warnings. The B' review's own farm run on the source commit: config 834+9, models 108+1 doctest, run 20261111T042035Z-review3-catalogqwen4Bp-efd93c.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PROVIDERS.md and its zh_hans copy now list claude-fable-5-2 and
gemini-4-carbon beside their siblings and give Gemini 4 the 65,536 output
limit Google states. The Google row no longer says an unsigned tool-call replay
fails closed for every thinking model: it does for gemini-3*, gemini-2.5-pro
and gemini-2.5-flash, while Gemini 4 only logs a warning and sends, and Google
does not say whether Gemini 4 requires signatures. Reasoning effort is
described as the reasoning_effort field the Engine sends, not thinking_level.
web/data/agent-docs.generated.json regenerated with derive-facts; on the
stacked base only the PROVIDERS.md entries change.
Checks: agent-docs regenerated with web/scripts/derive-facts.mjs, not merged by hand; seed render --check ok; seed age ok (0.3 days); catalog_models_dev_test 21 tests OK; web check-facts OK; check-docs PASS; check-runtime-routes PASS; preflight --check OK.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The adaptive-thinking branch mapped xhigh to max for every Anthropic model, so
a person choosing xhigh on a model that documents it was sent, and billed for,
max. A model whose catalog row lists xhigh now gets xhigh; a model whose row
does not list it keeps the max mapping. Anthropic's effort page lists xhigh on
Fable 5.2, Fable 5.1, Fable 5, Opus 5.5, Opus 5, Opus 4.8, Opus 4.7, Sonnet 5.5,
Sonnet 5 and Haiku 5.5.
Checks: Farm (Linux x86_64), stack tested on main a37d7fdd27; the rebased tree differs from the tested one only by main's own commits up to 1f9ad30c1f: cargo nextest run -p codewhale-config -p codewhale-models -p codewhale-engine (engine filtered to client::anthropic), RUST_MIN_STACK=8388608, 989 passed / 0 failed (config lib 834, configured_models 9, engine client::anthropic 38, models 108), run 20261111T043921Z-catalog8038final-test-193af4; CI-style clippy -D warnings --all-targets --all-features for config, models and engine, run 20261111T043925Z-catalog8038final-clippy-40cd61, exit 0, no warnings. No live provider call: whether the API accepts xhigh on every row that lists it is unproven.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Google's model pages give gemini-4-carbon and gemini-4-argon 65,536 output
tokens by default; the bundled rows carried Models.dev's 32,768. The limit is
held as a model correction as well as in the reviewed row so a Models.dev
refresh cannot put 32,768 back. gemini-4-argon's reviewed row now says it
reasons. claude-fable-5-2, gemini-4-carbon and gemini-4-argon cite the vendor
pages with receipts in place of models.dev. The Gemini limits test roster now
names both Gemini 4 ids.
The catalog cannot carry a dated price, so the 2026-12-01 Gemini 4 price
change is not recorded here.
Checks: seed render --check ok; seed age ok (0.3 days); catalog_models_dev_test 21 tests OK; web check-facts OK; check-docs PASS; check-runtime-routes PASS; preflight --check OK. Farm (Linux x86_64), stack tested on main a37d7fdd27; the rebased tree differs from the tested one only by main's own commits up to 1f9ad30c1f: cargo nextest run -p codewhale-config -p codewhale-models -p codewhale-engine (engine filtered to client::anthropic), RUST_MIN_STACK=8388608, 989 passed / 0 failed (config lib 834, configured_models 9, engine client::anthropic 38, models 108), run 20261111T043921Z-catalog8038final-test-193af4; CI-style clippy -D warnings --all-targets --all-features for config, models and engine, run 20261111T043925Z-catalog8038final-clippy-40cd61, exit 0, no warnings.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The localization guide, its Chinese copy and the website README still named
web/components/feed-card.tsx and its KIND_LABEL pairs, which the snapshot
change removed. The README tree also listed whale.tsx and ticker.tsx, gone
since 396cead3b4.
Checks: check-facts OK after regenerating the agent docs.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The merged-count paragraph carried its read date in aria-label, which
ARIA does not allow on a paragraph, and the milestone progress had only
a title. Both now keep the title and add the read date as visually
hidden text (the site's sr-only class), so screen readers announce it.
The visible text is unchanged. The repo-activity test now asserts the
hidden read date on the count and on every milestone progress, and no
aria-label on a paragraph; it fails against the previous page.
Removes the JSDoc line added on extractPluralBlocks in
check-locales.mjs (the repository takes no new code comments).
git grep shows nothing uses the .feed-card* classes or imports
lib/truncate.ts (only its own test did) after the feed card was
removed, so the seven CSS rules, lib/truncate.ts and its test go.
Checks from web/: vitest lib/repo-activity.test.ts 20/20 passed;
check:facts, check:docs, check:locales, tsc --noEmit, lint all exit 0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
repo-stats-snapshot.json now carries the merged pull requests of the last 30
days, open versioned milestones and, for the last five published tags, the
authors merged between the previous tag's and that tag's publish dates, all
read by sync-repo-stats under a deadline and byte cap (any failure leaves the
file alone). contributor-exclusions.json is the public exclusion list; its
confirmed flag switches the count noun from accounts to people. /contribute
renders faces, the 30-day count and an id="milestones" release plan from the
snapshot; /api/facts gains liveRecord {source, readAt, fresh (7 days)}.
Removed: the browser GitHub fetch from e4208b5486, /api/github/feed and its
retry, feed-card, feed-retry, roadmap-feed, fetchFeed/loadFeed/fetchFaces/
fetchMilestones, the feed, community and roadmap dictionaries in all locales,
and data-funnel star_exit on the hero star. Count sentences use
Intl.PluralRules keyed forms (lib/i18n/plural.ts) and check-locales enforces
every category a locale needs plus {n}.
Checks: new repo-activity tests 19/19; npm test 90 files 1288 tests; tsc, lint,
check:locales, check:facts, check:docs pass; browser requests to
api.github.com on /en /zh /ar /en/contribute: 12 before, 0 after.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The first segment sliced at pipeAt dropped the space, so at 560px and wider the command read 'install.sh| sh'. Slice at pipeAt + 1; the trailing space hangs under pre-wrap at 375. Static render and 1440/375 screenshots confirm. Rerun: tsc 0, lint 0, vitest 90 files 1314/1314, build 0, shoot 8 screenshots 0 problems.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Under 35rem text-indent -2ch moved only the first line, so every line after a
hard newline in the PATH and npm-prefix blocks sat 2ch right of the first and
read as one soft-wrapped line. The indent now applies to a .code-block-single
modifier set when the command has no newline; multi-line blocks keep pre-wrap
with no indent. The pipe segment is sliced at pipeAt + 1 so "| sh" aligns with
the URL continuation. At 375 the hero command wraps onto three lines.
Checks from web/ under the heavy lock: tsc 0, lint 0, check:locales 0, full
vitest 90 files / 1314 of 1314 passed, build 0, shoot.mjs 16 screenshots, 0
problems, 0 overflow. /en/install at 375 checked by eye (PATH block aligned).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Heading and lead come from the locked home section; version and the uninstall form come from the published release object and its capability row. The embedded v0.10.0 guide becomes a link. The install command wraps at the pipe below 560px with Copy on its own row. The confirmation control is a quiet optional text control.
Checks (web/): tsc clean, check:facts/docs/locales pass, 16 harness screenshots 0 problems, 0 overflow; npm test 1311 of 1314 passed first run, the 3 failures were stale assertions fixed here and rerun green (51 of 51 in the three affected files).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The landing skill still said a push is not a CI request and that routine
commits carry a skip marker, and the release sweep said hosted CI runs once
at the release step. Both contradict the landing gate, which starts full CI
on every push to main. The landing skill also gains the runtime routes check
beside the facts check, with the reason: git has merged docs/RUNTIME_ROUTES.md
wrongly with no conflict marker.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The note under the first task said v0.10.0 gives no warning without a key and
sent the reader to F3. v0.10.2 shows "no model connected · run /provider" on
its first screen and returns the first message to the composer, so the note
now says to connect a model with /provider, and to restart if messages stay
saved for later after connecting. The Chinese guide carries the same
correction. Proposed by the website install slice, which reads the same facts.
Checks: check-facts OK after regenerating the agent docs.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The quota handler loaded settings on a spawn_blocking thread that was not
enrolled in the caller's lock_test_env scope. Under test, settings path
resolution calls with_test_env_lock, so the blocking worker waited on the
mutex the test thread held while the test awaited the HTTP reply: a
deadlock at 0% CPU, which review saw as the e2e test hanging and
GET /v1/agents/external/quota never answering. The handler now carries an
env_scope_ticket into the worker and joins it, as the roster, MCP and
command handlers in runtime_api.rs already do. The cfg-gated lines compile
out of production builds; mobile_page at runtime_api.rs:3591 has the same
shape but no test reaches it while holding the lock.
agents.quota.<id> keys were shape-validated with no bound, so any number of
ids could be persisted and each listed as unknown_agent. Settings::set now
refuses a new id once 32 entries exist (the registry has 10); existing ids
stay editable and clearing one frees a slot. The runtime crate cannot see
the engine registry, so the cap rather than registry membership is the
write-time guard. One runtime unit test covers validation, no value echo,
default-entry removal and the cap.
Checks (local, dev-cargo.sh under the build lock, hermetic HOME):
- test -p codewhale-app-server --lib external_agent: 4 passed, 0 failed
(external_agent_quota e2e ok in 8.01s; previously hung >7m)
- test -p codewhale-config -p codewhale-runtime --lib settings: 5 passed
+ 102 passed, 0 failed, 1 ignored
- clippy -p codewhale-engine -p codewhale-app-server -p codewhale-runtime
--all-targets -D warnings: clean
- test -p codewhale-tui --lib every_persisted_settings_field_is_declared_in_the_schema: 1 passed
- cargo fmt --all --check: clean
Engine unchanged since review (fleet::agents 7 passed there).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
GET /v1/agents/external/quota reports, per registry agent, quota as
{status: "known", source, windows} or {status: "unknown", reason}, with
default_source "agent_reported" (EA-04 stays the default). This wave every
agent reports unknown: flag_off without agents.quota.<agent>,
consent_missing without a consent record, not_implemented when both are
present. A persisted id outside the registry reports unknown_agent.
The flag and consent are set and cleared only through POST /v1/config
(Settings::set): agents.quota.<agent> = on|off and
agents.quota.<agent>.consent = granted|revoked, persisted as
[agents.quota.<agent>] enabled / consented_at in settings.toml and declared
in SETTINGS_SCHEMA as the hidden "agents" entry. Error text never echoes the
submitted value.
No credential is read: LocalSigninReader is an empty enum seam that the
first vendor slice fills with one variant and one reader, without touching
the route. Known limits: vendor ids are shape-validated at write, registry
membership is reported at read; quota is not embedded in the list route.
docs/RUNTIME_ROUTES.md and web/data/agent-docs.generated.json are
regenerated for the new route (check-runtime-routes.mjs --write,
derive-facts.mjs).
Checks: Boat farm runs were queued when this commit was made; counts are in
the review packet, not claimed here.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The permission reply is written unguarded so a cancelled turn still answers
its pending request, but a decision that raced the interrupt could reach the
agent as selected. Force cancelled once the token has fired.
Checks: rustfmt clean; farm run 20261111T035138Z-REF-09-A1-18773e,
cargo test --locked -p codewhale-engine --lib -- fleet::acp external_agent:
23 passed, 0 failed, 7129 filtered out. Independent review rerun
20261111T035953Z-ref09rev2-a1-f25fe5: 23 passed, 0 failed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
AUD-ACP ruled fleet/acp.rs the one ACP client and crates/acp-client stays
unlanded, so no Cargo.toml or Cargo.lock change.
- G1: the session/request_permission response is now written without the
cancel guard, so an interrupt during a pending approval still tells the
agent the request was cancelled. The guard stays on the initial request line.
- G3: AgentCapabilities gains additional_directories; session/new and
session/load|resume send additionalDirectories (the thread's add_dirs) only
when the agent advertises sessionCapabilities.additionalDirectories.
- Projection: plan_update is handled like plan; current_mode_update and
available_commands_update emit turn.agent_mode and turn.agent_commands.
- abort_requests_on semantics are untouched.
Fixture records permission outcomes and session params and can advertise
additionalDirectories or emit the extra updates from fixture-plan.json.
Three new tests (interrupt cancelled answer, additionalDirectories gating,
projection).
Checks: farm run 20261111T032328Z-REF-09-060bd4, cargo test --locked -p
codewhale-engine --lib -- fleet::acp external_agent: 23 passed, 0 failed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
install.bat now passes %* to install.ps1. One static test in install-safety.test.js asserts the forward and the -NoModifyPath param. Install guide (en, zh_hans) says install.bat -NoModifyPath works; agent-docs regenerated by the web prebuild. Never executed on Windows.
Checks: install-safety.test.js 72/72; public-installer-contract vitest 33/33; check-facts, check-docs OK; preflight OK.
Landing adds the CHANGELOG line.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The plugin UI route now takes any of the six seats and an optional dsh_slot.
An unknown or missing seat is a coded 400 (plugin_ui_seat_unknown) instead of
the old uncoded one (DSH-00 defect 4). A native client is admitted only on
customize.plugins.detail; every other seat or a dsh_slot is a 404
plugin_ui_seat_not_admitted.
A DSH-converted plugin is no longer refused outright. Its retained
client_closures receipt (DSH-01) is re-verified byte for byte against the
reviewed file hashes, each file against the importer's own SHA-256, and the
declared slot keys found in the closure are mapped through the contract seat
table. A key whose seat is unsupported is a 422 plugin_ui_seat_unsupported;
more than one key on a seat without dsh_slot is a 400
plugin_ui_dsh_slot_required. Closures that need host modules the frame does not
provide, ES module syntax, or invalid JSON stay plugin_ui_unsupported. The
frame loads one Engine-generated CommonJS bootstrap built from the verified
closure bytes; every other path is a 404 plugin_ui_file_not_reviewed. The
response carries dsh_slot and a bridge identity (kind, dsh_version,
compatibility_digest).
Grants are now dropped the moment a plugin is disabled, revoked, restaged,
trusted, enabled, updated or uninstalled, so an open frame's revision is stale
at once rather than at its next read.
/v1/apps/plugins/{selector}/routes/{*path} checks the loopback peer, the single
If-Match revision, the selector and workspace binding, authority and expiry
and that the grant is a DSH client, then refuses with 501 plugin_route_not_served
because no plugin web server exists to forward to yet.
Evidence: codewhale-app-server --lib, filter plugin_ui, 3 passed on the build
farm (native detail-only seat, DSH admit/deny/revoke with consumer-contract
fixture, unprovided-module refusal).
Landing adds the CHANGELOG line.
docs/RUNTIME_ROUTES.md lists the new route (regenerated with
web/scripts/check-runtime-routes.mjs --write) and the agent docs are regenerated from it.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dsh-anchored-standard 1.2.1 ships a .gitignore, which the Native-source
hidden-file rule refused. The existing allow condition now also admits the
package-root .gitignore by exact relative path. Nested .gitignore files, other
hidden files, credential files, .git, symlinks and the MCP-source rule are
unchanged.
Adds one regression: a root .gitignore imports and is retained under source/;
nested .gitignore, .prettierrc, .env, .npmrc, .git/config and a symlinked
.gitignore still refuse.
Task: DSH-IMPORT-GITIGNORE. Landing adds the CHANGELOG line.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
scripts/preflight.sh now runs web/scripts/check-facts.mjs and check-cloud-facts.mjs, the same
commands ci web job runs as check:facts, so a doc change that feeds web/data/agent-docs.generated.json
without regeneration fails preflight and names the regenerate command. Skipped with a printed notice
when node is not on PATH. main-landing.yml sets up Node 22 so the step runs in the landing gate.
Negative control run: appended a line to docs/RUNTIME_API.md, preflight exit 1; regenerated, OK; reverted.
Clean tree: preflight OK. Step adds about 1.1 s warm. actionlint clean.
The same step also runs web/scripts/check-runtime-routes.mjs, so a route added in
crates/app-server/src/runtime_api.rs without regenerating docs/RUNTIME_ROUTES.md fails preflight
and the hint names node web/scripts/check-runtime-routes.mjs --write. All three scripts run even
when one fails. Negative control at landing: one row deleted from docs/RUNTIME_ROUTES.md, preflight
exit 1 with "[runtime-routes] FAIL" and the hint; restored.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(app-server,tui): run read-only builtins over the Runtime API
A remote client can now execute the terminal's read-only diagnostics against a
thread and read the engine's own answer, instead of being told where the
command lives. /system, /tokens and /cost are served; every other name,
including a name no host knows, answers exactly as it did before.
The served set is gated twice — by the terminal host's allowlist and by each
portable handler's declared data needs — so the remote surface cannot widen
without the host widening it first, and nothing is filled in from estimates:
absent telemetry keeps its absence encoding, per-route cost rows stay empty
until they can be attributed, and the cost currency resolves through the same
rule the terminal's display uses.
* fix(app-server): report the effective prompt and keep builtin diagnostics read-only
/system read thread.system_prompt, which is only the client override, so an
ordinary thread printed "(no system prompt)". The route now takes the system
prompt, mode and transcript length from the live engine session snapshot, and
an unreadable snapshot is an error instead of a zero message count.
The settings read used Settings::load(), which can migrate a legacy file
(a write), ran blocking I/O on a Tokio worker and defaulted on a malformed
file. It now runs Settings::load_read_only on the blocking pool and surfaces
failures as 500, as the mobile page does.
The route uses loaded_engine so a diagnostic never boots a cold engine (409
instead), and the name echoed in the 404 is capped at 64 characters.
The route test now starts with a runtime token and asserts 401 without it, 409
for a cold engine, and that the effective prompt is reported. The zh_hans
RUNTIME_API mirror gains the route paragraph, and both languages document the
403 for watch-only tokens.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* chore(web): regenerate agent-docs for the RUNTIME_API route paragraph
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* docs(changelog): credit @gaord's read-only builtins over the Runtime API (#7277)
Adds the CHANGELOG line for POST /v1/threads/{id}/commands/builtin, a
docs/CONTRIBUTORS.md entry under Unreleased and @gaord in
UNRELEASED_CONTRIBUTORS, following the #7261 credit. The agent-docs data
is regenerated with web/scripts/derive-facts.mjs because CONTRIBUTORS.md
is one of its sources. check-contributor-credit passes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* chore(tui): sync the TUI changelog slice after the #7277 credit line
Generated by scripts/sync-changelog.sh.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(runtime): regenerate the route table and agent docs for the builtin commands route (#7277)
docs/RUNTIME_ROUTES.md is generated from the route registrations in
runtime_api.rs; this pull request adds POST /v1/threads/{id}/commands/builtin.
Regenerated with node web/scripts/check-runtime-routes.mjs --write and
node web/scripts/derive-facts.mjs after merging main, which now carries the
Windows compile fix (a37d7fdd27) that this branch's required check needed.
---------
Co-authored-by: CodeWhale Bot <[email protected]>
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
The recorder never starts counting, reading or creating an install id while the browser sends GPC or DNT, and clears earlier queued counts and the id. The forwarder answers Sec-GPC: 1 or DNT: 1 as disabled before reading the body. The notice and privacy text now state what is counted, the random 90-day install id, no URL or referrer, the signals, and the program's off switches; the word anonymous is gone. zh text needs a person's review.
Checks: owned vitest files 56/56 (4 files); tsc, eslint, check:locales, check:facts clean. check:docs fails on origin/main too (RUNTIME_ROUTES.md). Full npm test, build, browser and harness evidence not run yet.
Amended at landing: adds the MEASURE CHANGELOG line under Changed.
Comments removed at landing (no new code comments): the privacy-signals section
divider and the empty-catch note in clearUsageState are gone, and the
usage-counting.ts header keeps its main wording with only "anonymous"
dropped. For readers: clearUsageState removes the queued counters, funnel
counters and install id and keeps the person's own preference; a storage
error there is ignored because there is nothing to clear.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pages and docs that fall back to English inside the right-to-left Arabic shell now carry lang="en" dir="ltr" on the English region, so punctuation and commands no longer render reversed and screen readers read them as English. A zero-specificity [dir="rtl"] rule lets each text block take its direction from its first strong character and keeps code and terminal frames left to right. English dates replace Arabic-locale dates inside English bodies. English, Chinese and translated regions gain no attributes.
Checks (web/): tsc clean, lint clean, check:locales/facts/docs pass, npm test 86 files 1286 tests pass, next build ok, 36 harness screenshots 0 problems.
Amended at landing: adds the RTL CHANGELOG line under Fixed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Modes, vocabulary, tools, account sign-out, sandbox, receipts, hooks and
runtime pages now state what the published release does; source-only
controls sit in the labelled section. The page about bringing settings from
another coding agent names no product in its title, description or headings.
The constitution page drops the avoid-list word. Fleet drops a default
worker count. The Computer Use setup step no longer asks for Operate. zh
mirrored; 16 partial locales carry a machine-drafted computer-use line.
gt catalogs regenerated.
Amended at landing: adds the DOCS-TRUTH CHANGELOG line under Fixed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A docs page can carry capability-keyed source groups and until-gated sections.
resolveDocsPage renders released text in the body, puts groups whose capability
row is above the published release in one labelled in-source section, and moves
a group into the body when a release carries its row, with no hand edit. The
version note now sits at body size and states the published tag. The tools
list, vocabulary controls, search index and reference pages follow the same
split. Tests: 5 in lib/docs-release.test.ts.
Amended at landing: no new code comments. Removed the doc comments on
DocsSection.until, DocsSourceGroup, releaseDescribes and sourceLead
(types.ts), the two ToolRow field comments (tools.ts) and the comments on
RELEASED_CONTROL_MODES and RELEASED_PERMISSION_POSTURES; the vocabulary.ts
header and the CONTROL_MODES and PERMISSION_POSTURES comments are back to
their main wording. For readers: a section's until claim keeps released text
only until that capability row is released; a source group is labelled while
its row is above the published release and joins the body once a release
carries it.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
One converter, scripts/render-released-capture.mjs, turns the released v0.10.2
tmux captures (SGR text) into the cell format TerminalCapture renders; every
character is kept, only SGR codes are dropped, any other escape fails, and
--check joins check:docs. The palette helper moves to scripts/terminal-palette.mjs
and render-terminal-capture.mjs now uses it. Development frames gain
provenance: "development". TerminalCapture takes an optional data prop and
renders its root dir="ltr" lang="en". lib/capture-caption.ts picks the caption
key from provenance: the released caption only when a test run is shown and no
pre-task frame is; none for mixed origins; per-frame flags for the posture note.
Checks: tsc, eslint, check:locales, check:facts clean; new and updated tests 14
passed; npm test 85 files, 1282 tests passed plus 7 node tests; next build ok.
Amended at landing: adds the CAP-WEB CHANGELOG line under Fixed.
Comments removed at landing (no new code comments): the converter's file
header and its inline stale-check note are gone, and the TerminalCapture doc
comment keeps its main wording. For readers of the converter: input is
lib/terminal-captures/released/provenance.json and the .ansi files it names
(tmux capture-pane -p -e); SGR state carries from row to row as in the tmux
stream; tmux trims trailing blanks, so short rows are padded to the frame
width with default-coloured spaces; a crop is a whole-row slice rendered from
its raw frame's stream and must equal its own crop file's text; output is
lib/released-capture.generated.ts; run it with no argument to write the
module and with --check to fail when it is stale or missing.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Adds chrome key footerMerch in all 18 locale chrome files and ChromeDict,
drops the merch argument from footerProjectLinks, regenerates GT catalogs.
Reran: tsc 0; vitest 6 files/134 passed; check:locales PASS; lint 0.
Amended at landing: adds the ROUTES CHANGELOG line under Changed.
Comments removed at landing: footerProjectLinks carries no doc comment.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
public-copy, public-surface-contract, page-meta, docs-article-search and the
funnel test asserted on the source of the eight redirected pages; the
assertions that still describe something move to the pages that carry it
(home, docs/constitution) and the rest are removed with the pages.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Intl.DisplayNames disagreed between Node and the browser (Hong Kong), the text
mismatch behind React error 418. Names now render from the static list until
mounted, English always.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Header: Docs, Models, Changelog, Contribute, GitHub with the snapshot star
count, Sign in, Install, and Desktop only when the desktop state is released.
Footer: four columns from links.ts, retired headline, Capabilities, Runtime,
Roadmap and Desktop-and-web removed, Security points at SECURITY.md. Locales
without native review show one machine-draft line. Nine chrome keys removed,
three added in all 18 locales; GT catalogs regenerated.
Amended at landing: the docs-ia sitemap test now expects /docs/claude-code in
the sitemap, matching the sitemap amendment in the previous commit.
Comments removed at landing (house rule: no new code comments): the new doc
comments on translationMachineDraft, navLinks, signInLink and
footerProjectLinks are gone with the false ones they replaced; the footer
Product, Docs and Legal functions keep the comments they had on main; the nav
header comment changes "five links" to "the page links" and adds nothing else.
The header order and footer columns are listed above.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sitemap loses redirected routes, signin, features, docs/claude-code and the
component pages of ratatui, and gains lastModified from the build.
SoftwareApplication lists macOS, Linux and Windows, drops isAccessibleForFree,
takes the locale's home description and links the locale's install page.
Amended at landing: /docs/claude-code stays in the sitemap, as the DOCS-TRUTH
review asks, because DOCS-TRUTH rewrites that page in this same stack.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
One table (lib/route-redirects.ts) feeds the middleware: community, feed,
roadmap, digest, product, runtime, constitution and enterprise answer 308 to
contribute, contribute#milestones, changelog, the home, docs/runtime-api,
docs/constitution and faq#teams, bare and under every locale. download and
desktop stay 307 and go to /app only when the desktop release object parses.
The eight page files and the digest, enterprise and runtime dictionaries are
deleted; the constitution page body moves to docs/constitution, which used
to redirect back to it. The weekly digest schedule and the digest task of
/api/cron are removed; facts-drift and link-check schedules stay.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
heroTitle, heroIntro, metaTitle and metaDescription change in place; heroTitleLead
is the headline's first sentence and every locale's heroTitle starts with it
(new dictionaries test). New keys carry the install note, both capture captions,
the one coordinator-authorised sentence for frames that hide the model name and
posture, the five frame labels, the belief, the six section headings and
bodies, "One engine", the four state words and the six map parts with their
sentences, in all 18 locales. Chinese needs a person's review before deploy;
the other 16 are machine drafts with no native review yet.
lib/content/product-map.ts computes each row's state from surfaceState(),
the capability registry and the desktop release object, never typed; one
session is always "Being built". Catalogs regenerated locally (export, no
paid service).
Checks: dictionaries, gt-site and product-map tests 28 pass; npm test 85 files,
1283 tests pass; check:locales, gt check, tsc, lint, check:facts pass.
Amended at landing: adds the CHANGELOG line under Changed, and removes the two
new doc comments on heroTitleLead and installNote in types.ts (comment-only).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
COPY_STYLE: a headline may be two sentences up to ten words, "we" is allowed in
the belief and in sentences about direction, "Being built" is a fourth state
label, the belief is exempt from the two-sentence limit, and rule 11 now bars a
desktop download offer only until the desktop release object is true.
MESSAGING now holds only the line, subhead, belief, six sections, the labelled
product map with its state rule, the churn rule and a translator note.
AGENTS points reviewers at the churn rule.
Amended at landing: COPY_STYLE rule 13 and the MESSAGING state paragraph now
write the locked state words "In development, no download" instead of
"In development (no download)".
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Moves the inline SubagentsShim and AgentsShim out of root.ts into
src/dsh/agents.ts as two DshAdapter entries registered in DSH_ADAPTERS.
Behaviour is unchanged: startActivation still needs the exact live calling
agent as parent, refuses a delegated caller or a call without its own
subagent ticket (AdapterHost.liveInvocation is not treated as
authorization), seals the agent/created window, and forwards owner, ticket
and child id on subagent/start, result and dispose. The test harness learns
subagent/* requests, and a new host test covers a consumer-shaped agent
team: persona and tool filter reach subagent/start, result settles, dispose
is sent, and delegated, ticketless, forged-parent and invalid-id calls are
refused before reaching the core. dist/ regenerated by npm run build (a
second build is byte-identical).
Checks in crates/engine/extension-host: typecheck exit 0; npm test 424
tests, 417 pass, 0 fail, 7 skipped; npm run test:bun 418 pass, 6 skip,
0 fail across 424 tests.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
AUD-MEMBER gaps G1-G6, built on the job-operations store and lock rather
than a second store. A member start is reserved in the Runtime store under
sha256(owner, thread, operation_key) with a pre-minted agent id before the
Engine is asked; the spawn uses that id, persists the worker before launch,
and the reservation becomes accepted only once the persisted worker proves
it (owner session = thread, worker id, requested profile = member). Exact
replay returns the original receipt after a timeout or a restart; a
different body is 409 member_operation_mismatch; a key under another
thread is 404; expected_store_id pins the store (409
member_operation_store_changed); an optional request_fingerprint must match
the server's; the body is capped at 2 MiB. A timeout answers 202 with an
unknown receipt and settles in the background; an Engine refusal withdraws
the reservation; a reservation from an earlier Engine generation with no
worker is settled as abandoned. GET
/v1/threads/{id}/members/operations/{key} reads the receipt. Delete and
discard refuse a thread whose start is still in flight. Capability
thread_fleet_member_durable_operations_v1; the existing opaque 1-128
character key is kept.
Touches outside the brief's exact list (recorded in the review packet):
tools/subagent/mod.rs (reserved_agent_id on FleetMemberStart and
SubAgentSpawnOptions, durable registration for reserved spawns),
app-server-protocol capability + regenerated schema/TS, route registration,
docs/RUNTIME_API.md section, regenerated RUNTIME_ROUTES.md and
agent-docs.generated.json.
Checks (farm run 20261111T014308Z-dsh05-members-738d66, base f3f09e4421):
cargo test --locked -p codewhale-engine -p codewhale-app-server --lib --
member extension_host discard delete_thread: app-server 3 passed 0 failed,
engine 270 passed 0 failed. Local: cargo test -p
codewhale-app-server-protocol --all-features 22 passed 0 failed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Five helpers in runtime_threads/tests/external_agent.rs (options_fixture,
received, pid_alive, refusal_code, options_thread_request) are called only
from tests that are #[cfg(unix)]. On Windows they were dead code, and the
workspace compiles tests with -D warnings, so 'Check every workspace target
compiles on Windows' has failed since 10ca1deee0 landed in cf4464f72a:
'could not compile codewhale-engine (lib test) due to 5 previous errors'.
It was caught on a contributor's pull request, whose required Windows check
failed for a reason it did not cause.
The helpers now carry the same gate as their callers. On unix the compiled
code is unchanged.
Checks on this commit: rustfmt clean, preflight OK. The Windows compile is
proved only by hosted CI on this commit; no Windows build was run locally.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
docs/RUNTIME_ROUTES.md is generated from the literal route registrations in
crates/app-server/src/runtime_api.rs. The ACP options route landed in
10ca1deee0 without regenerating it, so check-runtime-routes has failed on
main since cf4464f72a. Regenerated with
node web/scripts/check-runtime-routes.mjs --write: one row added
(GET /v1/agents/external/{id}/options), 238 paths.
Checks on this commit: check-runtime-routes PASS (238 paths), check-facts OK,
preflight OK.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
DSH-16-SEAM, the first slice of DSH-16, implementing COORDINATOR-RULINGS
2026-11-11 §1 (option A).
- protocol.rs: one host-to-core row `dsh/broker` with strict params
{owner, ticket, service, method, input}; `DshService` is the closed
service list (workspace, llm, network, settings, catalog, terminal,
remote). `service` is a string on the wire so an unknown name gets a
typed refusal instead of a parse violation that ends the host. The
service list is generated into protocol.generated.ts as DSH_SERVICES.
`tool/call` gains optional `broker_tickets: [{service, ticket}]`.
- core_call.rs: an in-session extension tool invocation mints one ticket
per service (ticket method `dsh/broker:<service>`, target
{call_id, session_id}), revoked with the invocation, owner and host like
every other invocation ticket. `serve_broker` redeems through the same
`redeem_invocation` as core/call (owner, tier, host generation,
lifetime, 50-use cap, current owner, selection), takes the session from
the invocation and never from the host, then dispatches through the
broker table, rechecking owner and selection afterwards.
- dsh_broker.rs: the Rust table (one arm per service), a typed method
decode helper, and typed refusals carried in data.dsh_refusal:
unknown_service, unknown_method, invalid_input, not_available,
ticket_refused, cancelled. Ticket refusals stay one code with the
existing generic message (no field oracle).
- dsh_{workspace,llm,network,settings,catalog,terminal,remote}.rs: stubs
that refuse not_available; each leaf (DSH-06..13) fills only its own.
- mod.rs: module declarations and the one dispatch arm; supervisor.rs:
the not-served fallback arm.
- extension-host: AdapterHost.broker(ctx, service, method, input) sends
dsh/broker with the live invocation's ticket for that service and
refuses locally (NotAvailable, nothing sent) without one. root.ts wires
it and carries broker_tickets on ToolInvocation. dist rebuilt.
Known limits, kept here rather than in a module comment (no new code
comments): every service arm refuses not_available until its leaf
(DSH-06..13) fills its own module and adds the service to
compatibility.json provided_services; BrokerCall carries only owner,
the ticket's session, service, method and input, so a leaf that needs
the invocation's tool context or the nested-call gate asks for it to be
extended in dsh_broker.rs and core_call.rs; tickets are minted for every
service on every in-session extension tool invocation, and per-plugin
service grants are each leaf's to check before it acts.
Nothing new is advertised: compatibility.json provided_services is
unchanged. No route through core/call by tool name.
Outside the brief's exclusive paths, needed to compile and deliver
tickets: tool.rs (one line putting broker_tickets in tool/call),
extension_host/tests.rs (three `broker_tickets: None` literal lines),
root.ts (ToolInvocation field, identity field and population), new
protocol corpus fixtures 67-74, test/adapters.test.mjs (one test).
Evidence (farm run 20261111T011045Z-DSH-16-SEAM-55ba6a, base f3f09e4421):
cargo test --locked -p codewhale-engine --lib -- extension_host:
176 passed; 0 failed; 0 ignored; 6965 filtered out.
extension-host: npm run typecheck clean; npm run build exit 0;
npm test 440 tests, 433 pass, 0 fail, 7 skipped;
npm run test:bun 440 tests, 434 pass, 0 fail, 6 skip.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Regenerated with scripts/sync-changelog.sh after adding the CW-7959 and
CW-8036 lines to the root CHANGELOG.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
origin/main cf4464f72a changed docs/RUNTIME_API.md, docs/CONTRIBUTORS.md and
docs/RELEASE_CHECKLIST.md without regenerating web/data/agent-docs.generated.json,
so web/scripts/check-facts.mjs failed with "generated agent docs are stale".
Regenerated with node web/scripts/derive-facts.mjs; no hand edits. Only those
three docs' entries change.
check-facts: OK after regeneration.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The v0.10.2 archive install.sh takes no options and never edits a profile, and its install.ps1 only tells the user to add the directory. Qualify the archive and Windows claims in INSTALL.md and zh_hans/INSTALL.md, point v0.10.2 readers to adding the three lines by hand, and regenerate agent-docs.generated.json. Checks: check-docs PASS, check:facts OK, git diff --check clean.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The undo sed used N;N, which on BSD sed quits without printing at EOF, so a lone start marker
within two lines of the end dropped the marker and the next line before the profile was
overwritten. Use {$!N;$!N;...} in both installers, INSTALL.md and zh_hans, regenerate
agent-docs, and add three lone-marker cases per installer (71 install-safety tests pass).
Also drop the new code comment lines from the installers.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Founder decision 138.7: the Unix installers (web/public/install.sh and the
archive install.sh) append the three-line "# >>> codewhale PATH >>>" block to the
detected shell's profile when the install directory is not on PATH, print the
file, the lines and a portable sed command that removes exactly that block, and
skip it with --no-modify-path or CODEWHALE_NO_MODIFY_PATH=1. Unknown options now
fail instead of being ignored.
Safety: only the block is appended (newline-safe); nothing is edited when the
directory is already on PATH, the profile is a symlink or not a writable regular
file, its folder is missing, SHELL is empty or unrecognised (fish prints
fish_add_path), or the directory name has shell-special characters. A reinstall
sees the marker and leaves the file as is. zsh honours an absolute ZDOTDIR. The
end-of-output block (export for this shell, then --version) is unchanged, and the
uninstall sentence stays gated on the installed binary reporting an uninstall
command.
install.ps1 adds the install directory to the user PATH (HKCU Environment Path,
REG_EXPAND_SZ preserved) by default, prints how to undo it, skips with
-NoModifyPath, and never duplicates the entry. Not run on Windows.
Evidence: install-safety.test.js 65 pass / 0 fail (adds default add for zsh, bash
and sh, fresh profile, undo command, flag and env skip, unknown option, already
on PATH, reinstall, symlink, unwritable, undetermined shell, for both
installers); public-installer-contract.test.ts 33 pass; assemble-release-assets
7 pass; check:facts and check-docs pass. A clean Ubuntu 24.04 container run with
a fixture release: bash and zsh, web and archive installers, new non-login
interactive shell finds codewhale with no manual step, reinstall keeps one block,
--no-modify-path leaves the profile alone, GNU sed undo verified.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Replaces the inline SessionsShim and SystemPromptShim in root.ts with
src/dsh/session-prompt.ts registered through DSH_ADAPTERS. sessions.flush
now requires a non-empty checkpoint ticket and the invocation session itself
instead of relying on AdapterHost.liveInvocation. Behaviour is otherwise
unchanged: systemPrompt stays child-agent-only.
No dsh_sessions.rs and no `mod dsh_sessions;` line: by coordinator ruling
(CW-7875) the module is dropped from DSH-04, because with only
session/checkpoint available it would be empty dead code. systemPrompt
stays child-agent-only, as reviewed.
extension-host: typecheck clean; npm test 427 tests, 420 pass, 0 fail, 7 skipped;
test:bun 421 pass, 6 skip, 0 fail.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Regenerated with scripts/sync-changelog.sh after the LEG-04, EA-04,
ENG-UNDO and PROV-7252 CHANGELOG edits.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The encoding fix 0d1ffc1cb2 covers tool declarations, replayed tool_use
blocks, string tool_choice and decoding, but an object tool_choice
({"type":"tool","name":"web.run"}) still went out raw and would 400 against
^[a-zA-Z0-9_-]{1,128}$. It now goes through to_api_tool_name (function-shaped
choices are normalised to the Anthropic tool shape). Adds one regression test
covering every outbound name and both decode paths. Refs #7252.
Evidence: farm cargo test -p codewhale-engine --lib client::anthropic: 38
passed, 0 failed; the new test failed (0 passed, 1 failed) before the fix.
Landing: the existing 0.10.3 CHANGELOG entry from 0d1ffc1cb2 now covers the
forced object-form tool_choice, cites #7252 and thanks the reporter
@ychu824; no second entry. @ychu824 is credited in the 0.10.3 CHANGELOG
Contributors block, docs/CONTRIBUTORS.md (v0.10.3 band) and RELEASE_HELPERS
in web/lib/release-credits.ts. AUTHOR_MAP is not needed: the reporter
authored no commit. #7252 stays open.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The work-in-progress paragraph still named beta.2 as a near-term
prerelease and kept App Server v2 in 0.10.3. Decision 96 dropped beta.2
and decision 124 paused v2. The paragraph now says the next release is the
full 0.10.3, v2 is paused, legacy JSON-RPC is not retired, the coarse
split and DSH all-33 stay, and nothing is declared complete.
crates/tui/CHANGELOG.md regenerated with scripts/sync-changelog.sh;
--check passes.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The prerelease checkpoint section named 0.10.3-beta.2 as its example, but
section 96 dropped that release: the next Engine release is the full
0.10.3. Use a neutral 0.11.0-beta.1 example, keep the generic prerelease
path, and say 0.10.3 does not use it. The checklist and runbook still told
agents to avoid worktrees; section 93 has agents build in short-lived
worktrees and land on main, with freeze, tag and publication from the
clean canonical main.
Prose only. Checks: git diff --check clean; scripts/sync-changelog.sh
--check up to date; relative links in both files resolve. No Rust build.
COARSE_SPLIT.md left to REF-01 (patch proposal in the review packet).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The Runtime's patch-undo refused with restore_requires_trust whenever the
thread was not in trust mode or Full Access, so a default (Ask) session could
never undo a turn's files even though the TUI's /undo already does so. The
ownership attribution, preimage and later-edit proofs decide what may be
reverted, so the trust gate is removed from the helper, its public signature
and the error-code table; the TUI caller drops its constant argument. The
doc comment and two test doc comments the change made false are rewritten.
Checks (review rerun, farm 20261110T153702Z-undo-review-11b834, base
9731fbbf1c): cargo test -p codewhale-app-server -p codewhale-tui --lib --
patch_undo undo_that_cannot_restore restore_undo: app-server 10 passed,
0 failed; tui 9 passed, 0 failed. Not run: Windows /undo, a process-restart
journey, macOS native, hosted CI.
Landing: squashes 2cc2da435f and its fixup 8aa3ec56c8, and adds the
CHANGELOG line from the review packet.
Landing: rustfmt reflows the patch_undo_workspace_files call in
debug_operations.rs after the dropped argument (format only).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Review repair: removes the 25 doc/comment lines the first commit added
(module doc in external_agent_options.rs, item docs in fleet/acp.rs and
manager/external_agent.rs) per the no-new-code-comments rule. The fixture
header in acp-options-agent.mjs follows the existing acp-agent.mjs
convention and stays. Adds the CHANGELOG line for the new route.
Reasoning that lived in the removed comments:
- GET /v1/agents/external/{id}/options?cwd= answers with the agent's own
session/new offer, read on a throwaway process stopped before return.
Nothing comes from the Codewhale provider catalog.
- Known limitations: probing starts a real agent session in cwd, which an
agent may keep in its own history. The route is gated like the rest of
the Runtime API and does not tie cwd to a thread owner.
- ControlWrite is the one ACP method that writes a control, so a choice is
never sent through a different door than the agent offered it on.
- Choices are the only values a caller may write; names and descriptions
are display text only. Over-limit or control-character values are
dropped, never truncated into authorization input.
- A config option that names a model or mode owns that control; the legacy
models/modes blocks fill only what no config option claimed.
- Usage is what the agent reported on the prompt answer; absent fields are
unknown, cost is always unknown.
Shared-file touches runtime_api.rs (+5: mod and one route) and
runtime_api/tests.rs (+2: mod) are the minimum to register the route;
the integrator should accept them explicitly.
Known follow-up: apply_choices still sends a blind set_config_option
("model") when no controls parsed, including when every configOptions
entry was malformed; the agent validates and a refusal is typed
choice_rejected. A follow-up can require malformed_controls == 0.
Checks: clippy -D warnings on engine and app-server, run after this edit.
Landing: rustfmt drops the blank first line the comment removal left in
external_agent_options.rs (format only).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
GET /v1/agents/external/{id}/options?cwd= answers with the agent's own
session/new offer (models, modes, config options, capabilities) read on a
throwaway process that is stopped on every path, plus which Codewhale
actions it carries. A saved model choice is validated against the complete
offer at setup and written through the method it was offered on; a choice
the agent does not offer or no longer offers, and per-turn controls an
agent cannot honor, are stable typed refusals instead of strings.
Agent-reported usage is recorded as turn.agent_usage events: context
window, a per-turn token total, cost unknown, and unknown (not zero) when
the agent reports nothing. A started agent process is now owned by a guard
that stops it on error or panic and logs a failed stop.
Checks (Boat farm, CARGO_INCREMENTAL=0): engine --lib external_agent
fleet::acp 20 passed 0 failed; app-server --lib external_agent 3 passed
0 failed; clippy -D warnings engine+app-server --all-targets clean.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Refused rows now require the Engine message to carry the DSH-00 pinned
peerDependencies refusal; the validator recomputes the match. --engine-kind is
required, source_sha is removed, and dev builds cannot yield an accepted
verdict. Selftest 15 passed (wrong-reason and dev-build cases added); pins 33 ok.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
One stdlib driver runs a pinned row's lifecycle L through the installed
Engine's /v1/apps/plugins routes (no second loader), writes a receipt, and a
validator recomputes the verdict from the pins: a missing required step,
zero-count PASS, evidence-less PASS, unnamed UNAVAILABLE, source-build engine
or hash mismatch is INVALID; provider/desktop gaps are UNAVAILABLE, never PASS.
Checks: selftest 13/13, pins 33/33 hashes, row 1 real run on macOS.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Recovers the scan-effort guarantees from 51f321 on the current owner:
directory enumeration stops after 10,000 entries (listing marked
truncated, "more exist" only when entries remain), and a ranged mention
past the byte prefix stops after 64 MiB of scanning, reported truncated
with no text instead of beyond-eof. Real EOF still reads beyond-eof.
Multi-range and CJK mention fixes untouched.
Checks: cargo test -p codewhale-tui --lib -- file_mention:: 46 passed, 0 failed.
Landing: CHANGELOG line added from the review packet; the new doc comments
on the two scan constants and the new test are dropped (no new code comments).
The rewritten directory_listing, read_file_content and
read_line_range_past_prefix docs stay: they replace comments the change
made false.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds a test-only account-epoch seam to profile_constitution.rs (like
TEST_ACCOUNT_LOAD_FAILURE) and one regression for admit_desktop_browser_call:
no fence refused, a changed epoch refused (A to B, B back to A), an unreadable
store refused, an unchanged epoch admitted, a newly admitted turn under the new
account admitted, and non-browser tools unaffected throughout.
Checks: codewhale-engine --lib desktop_browser_call_is_fenced 1 passed, 0 failed
(7142 filtered). Negative control with the fence check short-circuited to Ok:
the same test fails ("call must be refused"). No user-visible change, so no
CHANGELOG line. The live A-to-B-to-A proof (cw7841-fence-proof run.sh) was not
run here.
Landing: the two-line doc comment on pin_account_epoch_for_test is dropped
(no new code comments).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Every capability the site lists as available is now keyed to a docs/features.toml row and gated by the published Engine release.
- scripts/release-baseline-lib.mjs reads the registry, provider descriptors, model catalog and tool implementations at the published tag with git show and commits the result as data/release-baseline.json. derive-facts refreshes it when the tag is in the checkout and keeps the committed copy otherwise; check-facts fails when the baseline is for another tag or differs from the tagged source. Tag v0.10.2: 78 registry rows, 50 provider routes, 91 catalog models, 79 tool implementations.
- lib/capabilities.ts decides released, in_source or hidden per row: since above the release (or unreleased) is in_source, a row with no recorded since is judged by whether it is in the tagged registry, an unknown release hides everything. Released rows show the wording the tag had. A claim that describes the current row (Accept Edits as the default) is held back when the row's wording changed since the tag.
- Founder switches added, off by default: cloudComputersBeyondInvite (registry rows for cloud computers) and namedVendorAgentsOverAcp (rows naming other vendors' agents over ACP); plansPublished now also hides rows that describe Codewhale plans or pricing.
- New typed surface state in_source with copy in 18 locales (own drafts, need native review). /features and /features.json list only released and in-source rows, /product keys its work patterns, modes and permissions to rows, and structured-data featureList uses the same gate.
- Counts print the tag's values with what they count: provider routes, catalog models, tool implementations. Home, /models, /runtime, llms.txt and the FAQ read them; main-only models are no longer listed; a count that cannot be computed omits its sentence. /api/facts gains releaseCounts without changing existing fields.
- lib/capabilities.test.ts (402 tests incl. a per-file scan that no dictionary or content module carries a literal count).
Checks from web/: npm run check exit 0; check:locales PASS; tsc 0; lint 0; npm test 84 files, 1266 tests passed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Review repairs: web/data/agent-docs.generated.json regenerated so check:facts passes; the zh_hans INSTALL PATH note is translated and mirrors the release-archive "last output" clause; install-safety.test.js now asserts the last three lines of both installers and that the macOS Computer Use notice precedes the PATH block (37 pass).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
When PATH does not select the install, the last lines the installer prints
are now the copy-paste `export PATH=...; hash -r` line and
`codewhale --version`, after the persistent profile line. When PATH already
selects it, the last line is `Next: codewhale --version`. The Computer Use
notice moves above the PATH block and is printed on macOS only. Both
installer copies change; the installer still never edits shell profiles.
Checks: web installer contract + static installer + public surface tests 45
passed; scripts/release/install-safety.test.js 33 passed. Clean Ubuntu 24.04
container, non-root user, v0.10.2 download: output ends with the export line
and `codewhale --version`; pasting the line then `codewhale --version`
prints `codewhale 0.10.2 (ff466b2ed0)`.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Both installers printed `codewhale uninstall --yes`, the PATH-block marker
sentence and the `update.mode` sentence unconditionally. Releases before
0.10.3 have neither command nor key, and an unknown first word starts a chat
that sends it to the model. The installer now asks the binary it just
installed (`--help` lists an `uninstall` command) and otherwise prints the
manual removal. `update.mode` shares the same gate: both shipped together.
INSTALL.md and its zh_hans copy say "0.10.3 and later" and give the manual route.
Proof: install-safety.test.js 37/37 (4 new, website and archive, stub help
with and without uninstall); web vitest installer contract files 39/39;
Docker ubuntu:24.04 arm64: real v0.10.2 asset prints the manual removal and
no uninstall or update.mode text, a main-source build prints the uninstall line.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Each surface's status is now a typed state (released, available, beta, dev,
open, invite_only, not_open, not_public) looked up from the chrome dictionary
in all 18 locales instead of free text. Cloud invitations and plan publication
are committed values in lib/founder-switches.ts, default off, with the decision
beside each and no environment lookup. The desktop row shows a download only
when data/desktop-published-release.json holds a signed, notarized macOS asset
with a checksum (the object is null today). The partner strip expiry uses
lib/expiry.ts at render time. Snapshot numbers carry an "as of" title and
accessible label.
Checks (web/): check 0, check:locales PASS, tsc 0, lint 0, npm test 83 files
864 tests pass, offline build 0. Not deployed; not pushed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Review repairs for 064b1a38b6.
Comments removed per the no-new-comments rule; their reasoning lives here:
- withBuildProvenance: next.config.ts stamps revision, commit time and
generatedAt in one step. They apply together or not at all, so a fresh
revision never sits beside the tracked file's older date, and a partial stamp
leaves the tracked snapshot's own consistent trio untouched.
- RepoStats: a number that could not be read is null, never 0 and never a
floor. source says whether values were read live or come from the committed
snapshot; fetchedAt is when they were read (null when nothing was).
- releaseAssets: the checked-in release record also carries each asset's name,
size and sha256 digest plus the read time. Only the four core fields are
mirrored to docs/ and the cloud facts.
llms.txt now prints "read YYYY-MM-DD" beside the latest published release, so
the offline build says when the release was read.
check:latest-release no longer exits 1 when only the asset list differs and
the tag, version and URL match: it warns (and emits a GitHub Actions warning)
and exits 0. Verified by perturbing one asset size locally: warning, exit 0.
Checks: vitest facts-invariants, github, sync-latest-release,
public-surface-contract 4 files 62 tests pass; tsc --noEmit exit 0;
sync-latest-release --check exit 0; check:facts OK.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Published release: data/latest-published-release.json now also carries the
asset list (name, size, sha256) and the time it was read, written by
sync-latest-release.mjs; docs/ and cloud-facts mirrors still get the four
core fields only. Unknown renders nothing: RepoStats numbers are null when
unreadable, MIN_KNOWN_CONTRIBUTORS is removed, the production-build path
reads the committed repo-stats-snapshot.json (sync-repo-stats.mjs) instead
of returning zeros, and the browser fallback rejects a missing count rather
than defaulting it to 0. Provenance: next.config.ts stamps the generation
time with the revision, and withBuildProvenance applies revision, commit
time and generatedAt together or not at all. The desktop app is no longer
called native (availability row and app page, en and zh). The
SoftwareApplication structured data drops its price offer.
Invariant tests (lib/facts-invariants.test.ts, 18): installed-version line
equals the published-release object; no price, currency amount or plan name
on 19 pages in en and zh, in structured data or in the sitemap; home and
/contribute render with every GitHub call failing; a failed read is never 0.
Checks from web/: check 0, check:locales 0, tsc 0, lint 0, npm test 82 files
805 tests pass, offline build passes.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The ACP server advertises image=false but silently dropped image/audio/blob
prompt blocks while running the rest of the prompt; it now answers invalid
params naming the block type. session/new and session/load accept only an
absolute cwd (ACP requires it; session/list already did). Permission requests
and tool-call cards for the real tool names write/edit/read carried kind
"other"; they now carry edit/read so editors render and gate them correctly.
capture_frontend and CapturedAcpFrontend::serve become pub so a protocol-level
journey test can drive the real server loop over line-framed duplex streams:
initialize, refusals, new, permission allow, cancel, list, load with replay,
and a prompt in the loaded session whose model request carries the saved history.
Checks (Boat farm): cargo test --locked -p codewhale-app-server --test acp_journey: 1 passed; 0 failed.
--lib acp_server with RUST_MIN_STACK=16777216: 38 passed; 0 failed (790 filtered out).
codewhale-core --test single_turn_loop: 14 passed; 0 failed. Journey runs in-process over duplex
streams with a mock model on a current-thread runtime; no real editor was run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
HostRoot now installs every service from a single table and refuses to
construct when compatibility.json's provided_services and the installed
services disagree in either direction, so a service can never be advertised
without a shim or shipped without being declared. Reviewed adapters plug in
through DshAdapter (src/dsh/adapters.ts, DSH_ADAPTERS, empty today) and an
AdapterHost facade (tier, rpc, ownerOf, liveInvocation, log); HostRoot takes
the adapter list as an optional third argument so tests can supply one.
liveInvocation applies the owner/active/abort/disposing checks the inline
shims repeat. An adapter that duplicates an inline service or names an
undeclared one is refused.
An inject of a core-owned service the host does not provide (llm, credentials,
fs, ...) now says the Codewhale core owns it and no reviewed bridge exposes it
yet. No service, protocol method or compatibility entry is added, so
compatibility.json, the protocol table and generated parity are unchanged.
Checks: tsc clean; npm run build reproducible; npm test 416 pass / 0 fail / 7
skipped; bun test 417 pass / 0 fail (6 skipped counted in baseline); cargo
extension_host::protocol 4/4; extension_host 173 pass, 1 timing flake under
load that passes alone.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Review repairs for 4619f6d030: require/import whose quoted literal is followed by anything but ) or , is now a computed specifier (concat and concat-import cases added to the regression loop); removed the two new doc comments; the sibling-package refusal no longer says it leaves the package; CHANGELOG line for the client review outcome and client_closures.
Checks: codewhale-plugins --lib install:: 38 passed, 0 failed.
new URL(import.meta.url), import.meta.resolve and new Worker are still unscanned; CW-7704 must serve only listed, hash-matching files.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The importer no longer drops `dsh.client` with a blanket "not converted"
note. For the root package and every selected nested package that
declares a client, it resolves the client entry (exports["./client"]),
walks the static require/import closure over the bytes already copied
under source/, and records path plus SHA-256 for every file in
CONVERSION.json (`client_closures`), DshConversion and a `client`
outcome. Host-provided modules (react, react-dom, @deepseek-ai/*) are
listed, not resolved. Ambient npm packages, Node built-ins, URL or
absolute specifiers, escapes from the package and computed
require/import make only the client half skipped, with the exact
reason; the server half still imports. Serving stays refused by the
App Server route until its bootstrap admits the closure.
Evidence: codewhale-plugins --lib 208 passed, 0 failed, 1 ignored
(new regression dsh_client_closure_is_retained_hashed_and_refuses_ambient_dependencies
included); the `dsh` filter selects 7; clippy -D warnings clean.
Probe over the 36 survey packages (temporary test, removed): 19
retained, 5 skipped with reasons, the rest refused earlier by existing
server-side gates.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The grace-expiry note advised --prompt-file -, which clap rejects next to a
prompt argument. It now suggests piping the prompt together with the slow
producer, which does work. The resolve_exec_prompt doc comment no longer
claims a pipe always reaches the prompt. The reader thread left behind after
a timeout is unchanged: nothing else in the exec path reads stdin.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
With a prompt argument, exec waits 2 seconds for the first byte of piped stdin. If none arrives it runs on the argument alone and says so on stderr; any data is read to EOF and appended as before. A prompt-less exec still reads stdin to EOF.
The flag-after-prompt check now folds full-width ASCII, typographic dashes and a single em or en dash to the ASCII long flag, so an IME or autocorrected "--auto" is rejected instead of sent to the model as text. The exec help no longer says plain exec has no tools.
Checks on the Boat farm at base 733c34fb32: new exec_prompt_input subprocess tests 5/5; integration exec_ filter 23/23; cli+tui lib filter "exec stdin" 14 + 79 passed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
pet/public/shared.html is the source of crates/tui/src/tui/pet_watch/shared.html;
15456465ff edited only the generated copy, so 'npm --prefix pet run check' failed
on a byte difference and the next sync would have reverted the browser half of
the pairing change. Mirrors the one-line connect() change into the source.
Checked: cmp of source and copy identical; npm --prefix pet run check exit 0
(76 node tests passed).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The pet owner now mints a 30 s single-use pairing code over an authorized
POST /v1/pair; the launcher opens the browser at that code, and shared.html
redeems it once at /v1/attach for the existing HttpOnly SameSite=Strict
cookie. The long-lived owner token no longer appears in argv or the URL.
Adds regression tests for authorized() (wrong, missing, cookie, Host and
Origin pinning) and for single-use and expiry of the pairing code.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Windows crossterm 0.29 reports Ctrl+E with Caps Lock on as Char('E') with
CONTROL and no SHIFT, so plain Ctrl+E (composer end-of-line) opened the file
tree. The toggle now needs SHIFT in every form (Ctrl+Shift+E, Cmd+Shift+E).
The pinned test asserts the Caps Lock event shape no longer toggles and that
Ctrl+Shift+E still does.
Proof: file_tree_shortcut_does_not_steal_plain_ctrl_e fails on old code
(farm run, 0 passed 1 failed) and `cargo test -p codewhale-tui --lib shortcut`
passes with the fix.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The Main landing gate has failed on main since 0481377c4c: the root
CHANGELOG gained the "Website maintainer" line and crates/tui/CHANGELOG.md
was not regenerated. This runs scripts/sync-changelog.sh, which adds that
one line.
Checked: bash scripts/preflight.sh --check -> preflight: OK (rustfmt, TUI
changelog slice, README translations 18 in sync, locale link symmetry,
dead-code budget 121 at budget, blocking-calls budget 658 sites within
budget, release-note receipts).
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Explicit file allowlists for copy lanes plus a deny list for legal, constitution,
enterprise, partner, merch, availability, pricing, roadmap and project copy; PR
bodies list every changed file. Fail-closed bubblewrap probe before the model
runs; scope check now fails on ignored files and changed .git config or hooks;
web checks run in a fresh checkout with no token; publish mints the App token
only when a patch exists; shared action pins with version comments; every job
repeats the WEB_MAINTAINER_ENABLED gate. Adds publish-prs.sh, which the previous
commit left out because *.sh is gitignored. Test fixtures renamed from the
retired dispatch key; adds an overlapping digest generation test.
Checks: actionlint clean, tsc and eslint clean, vitest 81 files / 787 tests, next build ok. Workflow never run live.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds .github/workflows/web-maintainer.yml (disabled unless WEB_MAINTAINER_ENABLED=true): a deterministic derived-files lane and three codewhale exec lanes on deepseek-flash with a post-run path allowlist, web checks, and a separate App-token publish job opening one PR per lane. Removes the Worker triage, pr-review, stale, dupes and curate cron tasks, their prompts, dispatch KV plumbing and tests; digest, facts-drift and content watchers stay.
Checks: actionlint clean, tsc clean, eslint clean, vitest 81 files / 786 tests passed, next build ok. Workflow never run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The vendored design copy holds only what the site and scripts read: tokens.json,
its generated tokens.css/.ts/.rs/.dtcg.json, generate.py and the four inputs
generate.py --check needs (whale/source/spec.json, figma/sync.template.js,
figma/variables.json, figma/sync.js). docs/design/DESIGN.md now says
so, and warns against vendoring the design system's documentation here.
Checks: vendor generate.py --check verified; scripts/export-design-tokens.py
--check up to date (158 tokens); grep for file keys and local paths in
vendor/: none; web vitest 793 passed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review fixes. Verified: tsc, eslint, check:locales, check:tokens, check-facts, vitest 793/793 in 81 files, build ok. Reviewed the plugins and 390px dark screenshots.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Syncs vendor/codewhale-design to 5.3.0 with codewhale-design sync-to.sh, exports the water gradients from export-design-tokens.py, maps the site roles to the 5.3 light and dark tokens, self-hosts Geist and Geist Mono in place of Shannon Sans, and retires the warm-paper kit values. Radii follow the tokens (10/18/pill).
Checks: tsc, eslint, check:locales, check:tokens, check-facts clean; vitest 793/793 (81 files); next build ok; screenshots of /en, /en/models, /en/plugins, /en/app at 1440 and 390 in both themes reviewed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The main landing preflight has failed on every landing since acc0e30f65
(CW-7704 V2-01) because two ratchets counted new sites that are correct
where they are:
- blocking-calls: crates/app-server-protocol/src/bin/contract.rs makes 5
std::fs calls. It is a synchronous command-line codegen binary
(`--write` / `--check`) with no async runtime, so tokio::fs or
spawn_blocking would add nothing. Its sibling bin/export.rs is already
budgeted the same way.
- dead-code: the 2 #[allow(dead_code)] in
crates/app-server-protocol/tests/pinned/codex/common.rs are in the
unmodified pinned upstream Codex export that V2-01's frozen contract
reads byte for byte; editing it would break the pin. App Server v2 is
paused (§124) and its scaffolding is kept.
Raised with each script's --update. `scripts/preflight.sh --check`: OK
(dead-code 121 at budget, blocking calls 658 sites within budget).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Founder, 2026-11-10: new Codewhale accounts are open; cloud computers stay
invite-only. The sign-in entry page, its metadata and llms.txt no longer
say "new accounts are not open"; they name GitHub, Google and email-code
sign-in and say cloud computers are invite-only. /docs/account and the
docs-auth account note (en, zh) drop the "people who already have an
account" framing, and /app's cloud row says invite-only.
Checks in web/: tsc pass; check:locales pass; check-facts pass; vitest 793
passed. Not deployed (deploy waits for the Worker GITHUB_TOKEN, §132.2).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A lessons review of the site history (codewhale-design vintage) flagged
that the offer strip above the hero repeated a reverted choice: on
2026-10-27 (35c5a62b7c) the offer left the top because the page opened on
a sponsored banner, and moved beside the models section it relates to.
The strip now sits inside the models section, still dated and labelled.
CURRENT_DECISIONS §46: English surfaces only, no featuring in zh-Hans, and
the disclosure scoped to the campaign link (founder amendment 2026-10-28:
the general "referral fee" line "isn't totally accurate"). The promo is
gated to locale en, and the disclosure now reads "This link is part of a
referral campaign; Codewhale may earn a referral reward on top-ups made
through it."
The home maker chips were a hand-typed list; they are now the distinct
makers in facts.models (gateways excluded), alphabetical, so new makers
appear without an edit and no provider is ranked (§46 neutral order).
Also removes components/whale-live.tsx, lib/content/release-highlights.ts
and the whale-live CSS, which nothing imported.
Checks in web/: tsc pass; eslint pass; vitest 793 passed; next build pass.
Browser: /en shows the offer card in the models section with the
disclosure; /zh shows no OrcaRouter text.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Adds a translated footerApps chrome key (all 18 locales) and the /app link in
the footer Product column; hides the promo artwork under 640px. CHANGELOG
lines cover the home, models, plugins, catalog, brand, /app and sign-in work.
Checks: tsc pass; check:locales pass; vitest lib/i18n + docs-ia 75 passed;
next build pass; browser: /en, /en/models, /en/plugins, /en/app,
/en/docs/account render, no horizontal overflow at 375px.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- FAQ, getting started, docs hub and tasks, vocabulary, enterprise, docs-auth, docs-trust and roadmap copy now lead with DeepSeek, Anthropic, OpenAI, Google and the OpenAI-compatible endpoint; Ollama and OpenRouter stay in long-tail lists. The OpenRouter how-to FAQ becomes a generic gateway/custom endpoint FAQ; docs-auth shows an Anthropic example first and a vllm example for self-hosted (en and zh).
- Footer: Product (6), Docs (6), Project (7), Legal (4) columns; removes the Product link under the Product heading and moves legal/security/usage-data links into the Legal column. New chrome key footerLegal in all 18 locales.
- Version claims in reference-guides, tools, product and contributing now derive from facts.generated; the Computer Use bundled-plugin caveat shows only while the published release is the one it was verified against.
- roadmap-feed: deleted the FALLBACK_SHIPPED v0.8.45 entry so a GitHub outage keeps the curated Shipped list, and an all-empty feed is no longer cached. /feed shows one unavailable state instead of two empty columns.
- Han seals and bilingual title asides on faq, runtime, constitution and feed render only on the zh locale.
- gt-catalog re-exported. Checks: tsc clean, eslint clean, check:locales PASS, vitest 791/791 (80 files), next build OK.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Verified: tsc clean, eslint clean, check:locales PASS, vitest 80 files / 791 tests pass, next build OK.
JSON-LD logo stays /icon-512.png (square tile, valid for Google logo guidance); no test expects icon.svg there.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Header and footer draw public/brand/mark-mono.svg as a CSS mask inked with
--mark-ink (forced-colors fallback included), replacing the WhalePose rest
mark. app/icon.svg is the mono mark with a prefers-color-scheme fill; favicon.ico
(16/32) regenerated from it; apple-icon and PWA icons are the desktop app icon
tile rendered from codewhale-design icon-light.svg; OG card uses mark-reversed;
JSON-LD logo points at icon-512.png. CANONICAL_MARK_SRC (auth tile) unchanged.
ocean-contract test encodes the split.
Checks: tsc clean, eslint clean, check:locales PASS, vitest 790/791 on the full
run with one load-induced timeout in public-installer-contract (passes alone,
4 files 42/42), next build ok. Screenshots in artifacts/site-overhaul-20261110/logo.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Removed the connectorsDesktop sentence (desktop Connectors UI is only in an unpublished 0.10.3-beta.1 candidate) and its docs-search reference. Rewrote connectorsNote as one sentence, checked against docs/MCP.md (plugin MCP cannot use OAuth; direct servers can). derive-connectors.mjs now emits the committed header exactly and rerunning it leaves no diff; dropped the unused CONNECTORS_CHECKED_AT export. The example connector command is a constant. Connector grid is two columns at 390px.
Checks: tsc clean; eslint clean; check:locales PASS; vitest 80 files / 791 tests pass; build OK; en+zh at 1440/390 no horizontal overflow.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Lead with the plugin philosophy credited to DeepSeek Harness, with an
experimental-status note matching docs/DSH_PLUGIN_COMPAT.md. Render
documented MCP connectors (auth badge) from a committed snapshot generated
by web/scripts/derive-connectors.mjs from the marketplace catalog; no Gmail
connector is claimed. Catalog cards lead with Computer Use (version from
marketplace data), drop the stale v0.10.2 note, and collapse trust, sources
and commands into one Safe by default block plus a collapsed reference.
Dead copy keys removed; docs search sections and its test updated.
Checks: tsc clean; eslint clean on changed dirs; check:locales PASS;
vitest 80 files / 791 tests passed; next build OK; screenshots at 1440 and 390.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
- Latest becomes Recently added: dates are catalog-addition dates, stated on the page; within a maker ties prefer flagship over mini/flash/haiku tiers (claude-sonnet-5-5 still wins over haiku; fixed maker order, no id-sort ordering between makers)
- Providers section: drop lead paragraph, summary reads Show all N providers
- Compat card: drop Ollama/OpenRouter (Ollama is a built-in provider), name [providers.<name>] in config.toml, commands render as code
- Reasoning badge moved out of the row header cell; redundant sr-only captions removed
- Copy no longer claims a Codewhale version; search index updated
- Checks: tsc clean, eslint clean, check:locales PASS, vitest 791/791 (80 files), next build OK; screenshots 1440/390 in CW/artifacts/site-overhaul-20261110/models
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Latest first-party models derived from facts, three-step connect with DeepSeek, first-party and OpenAI-compatible cards, catalog grouped by maker in disclosures with a separate reasoning badge and an honest 'In Codewhale since' column, providers collapsed and first-party first, one setup-guides link, no Ollama in meta, fixed unfilled version caption. Replaces models-table (client sort) with server models-catalog; removes dead data-table-sort CSS.
Checks: tsc clean; eslint clean; check:locales PASS; vitest 791 passed (80 files); next build ok.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
gh pr edit never changed draft state, so a failing run left a ready PR whose body said draft; the edit branch now runs gh pr ready or --undo from the check result. Checkout no longer persists the App token (it is passed only to the push), the push is --force-with-lease, the PR body and doc say merging publishes unreviewed rows and that discovery only looks 45 days back. Source receipts cite models.dev/catalog.json, the endpoint the pipeline reads; bundled seed re-rendered.
Checks: seed render --check ok; catalog_models_dev_test.py 20/20; check-facts ok; tsc clean; check:locales ok; vitest 791/791 (80 files); next build ok. Rust tests not rerun (only source strings changed). Workflow YAML parses; not run in Actions.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The seed now lists claude-fable-5-2 and gemini-4-carbon, so the registry
export gains their rows. The farm run of cargo test -p codewhale-config showed
this golden as the only failure (833 passed, 1 failed).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Both ids and their metadata come from models.dev (fetched 2026-11-10):
anthropic/claude-fable-5-2 (released 2026-11-09, 1M context, 128K output, $8/$40)
and google/gemini-4-carbon (released 2026-11-10, 1,048,576 context, 32,768
output, $2/$10). Added through the documented slice: seed spec entries, seed
lock and render, then reviewed.intrinsic and reviewed.public_models rows with
added_at set to the models.dev release_date. The re-lock also pulled the
upstream drift the dry-run listed (Kimi K3.1 open_weights and descriptions, a
few OpenRouter prices). No Rust pricing rows: the Anthropic hand_pricing
allowlist entry for claude-fable-5-1 prices nothing, and the catalog price now
comes from the seed. models.dev lists a 16/80 fast-mode rate for Fable 5.2 that
needs a billing-policy decision and is not recorded.
New `seed discover` in scripts/catalog_models_dev.py lists, and with --write
adds, text models from an explicit vendor allowlist (anthropic, openai, google,
deepseek, xai, moonshotai, zai, minimax, alibaba/Qwen ids, mistral ids, meta,
xiaomi, stepfun) that models.dev released inside the [discover].since_days
window and the public list lacks. .github/workflows/catalog-refresh.yml runs it
daily with the seed lock/render and the web facts derive, runs the checks, and
opens or updates one PR with the codewhale GitHub App token so CI runs on it.
It never pushes main and uses no pull_request_target. docs/CATALOG_REFRESH.md
(en, zh_hans) now describe it and its limits.
The hard-coded 92-row public-model counts in scripts/catalog_models_dev_test.py
and web/lib/model-catalog.test.ts would break on every automatic addition, so
they assert uniqueness and agreement with the source instead.
Checks run: seed render --check ok; seed age ok; python3 -m unittest
scripts/catalog_models_dev_test.py 20 passed (2 new for discover); web prebuild
and check:facts ok; tsc --noEmit clean; eslint lib clean; check:locales PASS;
vitest 791 passed (80 files); next build ok; actionlint clean. Rust tests run
on the build farm separately.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The home terminal frames were captured from a dirty 0.10.2 build on
2026-10-08 and showed four "Note Workbar ..." slash-command lines. The
capture sequence now leaves the default bottom Tasks workbar in place for
the home frame and drops /workbar off and /workbar bottom, so the home
frame shows the finished task list instead of command notes.
All six frames, the capture manifest (binary, test binary and receipt
hashes, dirty-source diff hash, workflow evidence: 10 root and 2 reviewer
requests, review confirmed, 3 local tests passed), the generated renderer
module, the README image (same path, now 1872x956) and
docs/public-surface-facts.json are regenerated. TERMINAL_SCREENSHOT now
says 0.10.3.
Evidence: codewhale built with codewhale-tui/long-running-tests via
scripts/dev-cargo.sh; launch_card_pty::website_current_terminal_capture
1 passed (an earlier run failed once on harness timing and passed on
rerun). web vitest public-surface-contract and terminal-capture: 29 passed.
Local scripted demo with a loopback provider; not a release or provider
qualification.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign-in wording now says when sign-in is available; cloud computers no longer
named invite-only; desktop row matches sessions.md and review.md; capture note
says they come from a test fixture. tsc clean, eslint clean, check:locales
PASS, vitest 81 files / 793 tests passed, build ok, screenshots en/zh 1440 and
390 light/dark, no horizontal scroll.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
New /[locale]/app: status line, three development-build captures from
docs.codewhale.net (conversation and approval cropped above the composer,
model picker), what works without an account versus what one adds, and the
where-it-runs table. The availability rows move to lib/content/availability.ts
so /product and /app read one source (hosted web now reads Not open yet,
local browser reads Released). Registered in sitemap and llms.txt.
Checks: tsc clean; eslint clean on changed files; check:locales PASS;
vitest 793 passed (81 files) before the final crop tweak; next build ok;
screenshots at 1440/390, light and dark, no horizontal overflow.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds /[locale]/docs/account (en, zh) as a docs-map topic with a docs-tasks
entry and article search, linked from Connect a provider. Replaces the retired
app.codewhale.net/settings and /session?run= references in PROVIDERS.md and
GUIDE.md (en, zh_hans) and the stale GPUI note.
Checks: tsc clean; eslint clean; check:locales PASS; vitest 789 passed, 0
failed (80 files); next build OK; check-facts OK; page renders 200 en and zh.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
- OrcaRouter offer moves from a 16:9 card at the bottom to a dated strip
above the hero with a days-left count, a visible "Referral link" tag and
rel="sponsored noopener"; it disappears after endsAt.
- One proof strip under the terminal: latest published release and date,
live star count (hidden when unknown), contributors, licence, roadmap.
The separate Latest releases band is gone.
- The empty "Use the models you choose" header becomes a models section:
first-party makers, any OpenAI-compatible endpoint, provider/model counts
and the six newest first-party models from facts. Ollama is no longer
named on the home page.
- Computer Use folds into the capabilities section as a fourth item; the
roadmap goals band leaves the home page (it lives at /roadmap).
- New "Everything is a plugin" teaser linking /plugins.
- Community keeps stats, merges and faces; the duplicate star button is
removed and empty stat rows are hidden instead of showing a dash.
- The terminal gallery loses its unexplained "Build with these components"
link; the Work card loses its 0.10.3 Operate-alias note.
- 25 unused home dictionary keys removed from all 18 locales and HomeDict
(gain, modelsFacts, availability, surfaces and their headings); tests that
only pinned them are removed; GT catalogs re-exported.
Checks in web/: tsc pass; eslint pass; check:locales pass; vitest 789
passed, 0 failed; next build pass. Browser: /en renders the promo strip,
models section and community panel; GitHub returned 403 to this machine so
live stats were correctly hidden. Not deployed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
SamhandsomeLee's Responses dialect profiles (#7261) are under Unreleased in
CHANGELOG.md, but docs/CONTRIBUTORS.md listed them in the v0.10.3 band, so
the website credits-parity test failed on main. That test gates the Web
Frontend deploy job (deploy needs lint, lint runs npm test). The entry moves
to a new "Unreleased — landed after v0.10.3" band; the credit is unchanged.
Agent docs data regenerated.
Checks in web/: npm test 791 passed, 0 failed; npm run check pass (facts,
latest-release, docs, tokens, lint, tsc, next build).
scripts/check-contributor-credit.py: every contributor credited on all
three surfaces.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The docs layout drew a second band (reading whale + "Codewhale
documentation") directly under the site navigation, which already marks
Docs as active. Remove the band, its CSS and the now-unused portalMark
dictionary key; the release-truth line moves under the breadcrumb with
spacing before the search field.
Founder feedback, 2026-11-10: "kinda weird that we have double headers".
Checks in web/: tsc pass; eslint pass; check:locales pass; next build pass;
vitest 790 passed, 1 failed (credits parity, same failure as main).
Browser: /en/docs desktop and /en/docs/sandbox at 375px show one header,
no console errors. Not deployed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The roadmap and contribute page dictionaries existed only in English and
Chinese, so 16 locales rendered those page titles, intros and actions in
English. Adds both dictionaries for ja, vi, ko, ru, uk, es, pt-BR, fr, de,
ca, id, hi, tr, it, pl and ar, with terms matched to each locale's existing
project/home/chrome dictionaries. The dictionaries test now requires these
two pages to be translated instead of pinning English for ja/fr/ar.
Translations drafted by four Claude Sonnet subagents and spot-checked; no
native review yet.
Checks in web/: tsc pass; eslint pass; check:locales pass; next build pass
(1068 pages); vitest 790 passed, 1 failed (credits parity, same failure as
main). Browser: /de/contribute and /ar/roadmap render translated (Arabic
RTL), /en/install self-check and /en/docs search work, no console errors.
Not deployed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Finishes the stranded Codex lane from worktree codex-docs-measure-CW-7933
(last touched 04:21, never committed), carried onto current main.
- Install and quickstart pages gain a reader self-check button that records
install_confirmed / quickstart_confirmed; copy says it is a self-report.
- Docs search records search, empty-result and result-selection counters
without sending query text or URLs.
- funnelPath/funnelExit map pages and GitHub exits to the fixed counter set
accepted by the website-funnel v5 ingest schema.
- The usage-counting toggle reports "unsaved" when storage refuses the
preference instead of claiming it was saved; empty stored values are no
longer read as the default.
- TELEMETRY.md (en, zh) and the privacy copy name the contributing-guide
exit correctly.
- docs/RUNTIME_ROUTES.md regenerated for routes main added since the lane
was cut; one zh docs-review code block lost a stray newline.
Checks in web/: tsc pass; eslint pass; check:locales pass; check:docs pass
(237 runtime paths); check-facts pass; vitest 790 passed, 1 failed. The
failure is lib/public-copy.test.ts credits parity, failing identically on
main (docs/CONTRIBUTORS.md lists @SamhandsomeLee in the v0.10.3 band while
the changelog has #7261 under Unreleased). telemetry-ingest: 215 passed.
Not deployed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Reuse the existing strict Markdown/copy renderer for canonical Runtime API, Fleet tutorial and plugin authoring references, with visible English fallback and source-candidate boundaries. Add a checked 232-path HTTP inventory and derive sitemap docs destinations from docs-map. Put installation and first task ahead of advanced tasks.
Replace the incomplete force-push substring gate with an explicitly scoped unconditional bash deny; separate copied execution contexts; preserve state in operations guidance; correct the Fleet route example, Docker image and Runtime API token/server lifecycle. Copying documentation examples never counts as install intent.
Scoped ESLint and web tsc --noEmit passed. Focused Vitest: 6 files, 36 tests passed. check:docs passed 28 topics and 232 literal routes; check:facts passed. Corrected Fleet spec validated offline with 2 tasks, no workers/model calls launched. Public Docker v0.10.0 manifest returned 200 with linux amd64/arm64; no image pull or runtime qualification. Parent owns merged full gate and browser review. Depends on the sibling search change adding guide.searchSections/preamble in install-guide-lib; those two sibling-owned files are deliberately excluded here.
Index installation, model/provider tables, plugin sections and constitution layers from their rendering sources, with real heading destinations. Reuse the Markdown renderer for section text and preambles, and share existing model formatters and constitution illustration data rather than duplicating them.
Keep body construction on the server and deliver the active locale through GET /api/docs/search/[locale], with a bounded locale cache, weak ETag/304 and explicit failure responses. Keep only the small matcher in the browser. Integrates the companion canonical-reference adapter and lazy-search client slices.
Verification against the integrated candidate: TypeScript and focused ESLint passed; 4 test files, 20 tests passed. New regression failed against the prior index (1 failed, 2 passed). Static component graph: 378512 to 129705 raw bytes, 104830 to 36287 gzip; full deferred index ~163-168KB gzip. Browser/production gates remain with the coordinator.
Reuse the existing Analytics Engine query transport and fixed columns for bounded aggregate website counts and separate native technical-completion cohorts. Report self-confirmations separately; withhold native fractions on incomplete, sampled or immature cohorts. Add offline synthetic receipts and a public GitHub history exporter that verifies visibility and pagination before producing aggregate-only first-issue and first-merge cohorts, using stable public author IDs only in memory. No website/runtime/GitHub attribution join, credential writes or new collection framework.
Verified: ingest/report suite 215 passed across 8 files; typecheck passed; both offline synthetic report CLIs and query-plan CLI ran; git diff --check passed. No production query, live GitHub export, provider call or deploy was run.
Add website schema 5/notice 7 with fourteen fixed u32 funnel counters through the existing first-party ingest. Preserve runtime v4, browser v3, legacy v1/v2, the old product counters and all Analytics Engine column positions. New traffic remains first-party only; the PostHog v2/v3 boundary is unchanged. Publish the generated browser contract and English/Chinese disclosure with self-report and rollout limits.
Verified: ingest suite 202 passed across 7 files; added documentation parity assertion rerun 28 passed; TypeScript typecheck passed; git diff --check passed. Local fixtures only, no deploy or accepted production traffic claimed.
Delete items nothing calls: engine _assert_var_handle_shape,
merge_contexts and its test, FleetManager::record_task_timeout;
runtime resource_telemetry except estimate_output_tokens_from_text,
SessionJournal::children_of; app-server run_daemon_socket, run_stdio
and every daemon_client::forward_stdio variant with its re-export.
trailing_start_marker_prefix_len duplicated trailing_marker_prefix_len
over the same 30 start markers (kept index-paired by the existing
marker-table test), so its caller now uses the generic helper.
Drop dead_code allows on items with live callers (SubAgentManager.
workspace, App::cell_at_virtual_index, AppAction::LoadSession,
ActiveCell::revision, McpPool::all_resource_templates) and on items
that are pub-reachable, where the lint never fires (UserInputProvenance,
the McpConnection/McpPool accessors, TokenEstimateCache methods,
ProjectContext.project_root, termination.rs, auto_review.rs). Some of
the latter are still only read by tests; they are listed with the other
maintainer-decision items in #7270.
The tests module's Path import is cfg(unix) because every test using
it is unix-only.
Dead-code budget 137 -> 119 (allow 105 -> 87). Refs #4785.
Checks: clippy -p codewhale-runtime -p codewhale-app-server
-p codewhale-engine -p codewhale-tui --all-targets --all-features clean;
runtime 26 passed, app-server 20 passed, engine focused 385 passed;
check-dead-code-budget PASS at 119.
Several TUI surfaces sized text by chars or bytes where the terminal
counts columns, so CJK labels pushed later columns right or were cut
to about a third of the space they had:
- `{:<N}` padding (chars) in the command palette label column, the
hotbar setup action rows, the config detail fact labels and the
workflow history lane labels now pads by display width through a new
tui-kit `pad_to_display_width`; fit_config_column uses it too.
- The approval card intent summary and params line, and the elevation
prompt command, used util `truncate_with_ellipsis`, which budgets in
bytes, with a column width. They now use `truncate_line_to_width`.
ASCII output is unchanged for budgets of 4 columns or more.
Not covered here: push_detail_line's `{label:<7}` on the same card
(the frozen legacy approval-band parity fixture pins it), hotbar
"recommended" labels wider than their 3-column slot in ja/ko/hi/id/vi,
and similar char-count widths in skills_manager, theme_picker,
subagent_routing and the agent nickname view.
Tests: 7 new regression tests fail on the old call sites (0 passed,
7 failed) and pass with the fix. RUST_MIN_STACK=16777216 cargo test
-p codewhale-tui --lib -- tui::widgets tui::approval tui::hotbar
tui::command_palette tui::views: 906 passed; 0 failed. tui-kit
ui_text: 26 passed. clippy -p codewhale-tui -p codewhale-tui-kit
--all-targets clean.
Co-authored-by: CodeWhale Bot <[email protected]>
matches_simple_glob retried the rest of the pattern at every remaining
position for each `*`, with no memoization and a fresh String per
attempt. A model-supplied include pattern such as
`*a*a*a*a*a*a*a*a*a*a*b` against a file named 40 `a`s plus `.rs` took on
the order of C(40,10) recursive calls for that one file, and again for
every similar file in the walk. Brace expansion and repeated globstars
were already bounded (#7254); this was the remaining unbounded path.
It is now the iterative two-pointer wildcard match: remember the last
`*` and backtrack to it once, O(text x pattern). Semantics are
unchanged: `*` matches zero or more chars (including `/`, as before),
`?` exactly one, literals by char, so multi-byte names stay safe.
Tests: the existing pathological_globs_stay_bounded 2 s bound now also
covers the many-stars case; cargo test -p codewhale-engine --lib
tools::search: 31 passed. clippy -p codewhale-engine --all-targets clean.
`[retry] jitter = false` with `max_delay = -1` (or a negative
initial_delay) reached Duration::from_secs_f64(-1.0) in
RetryConfig::delay_for_attempt and panicked on the first transport
retry; with jitter on, the clamp hid it. Config::validate now rejects a
negative or non-finite initial_delay, max_delay or exponential_base,
the way [retry.rate_limit] already rejects bad values.
delay_for_attempt and TokenBucket::delay_until_available keep a
no-panic backstop for the public builders and env input: a negative
delay is zero, and a delay too large for Duration saturates at
Duration::MAX (every consumer already caps or saturates it). A tiny
CODEWHALE_RATE_LIMIT_RPS such as 1e-30 therefore waits indefinitely
instead of panicking.
Tests: runtime negative_retry_delay_is_invalid 1 passed; engine
delay_for_attempt / token_bucket 7 passed. clippy -p codewhale-runtime
-p codewhale-engine --all-targets clean.
/config split its key and value, and detected --save, on ASCII space
only, so a tab or a full-width space (U+3000) from a CJK input method
turned `/config approval_mode<U+3000>auto` into a lookup of one unknown
key. The key/value split now reuses the command's whitespace-aware first
word, and split_save_flag finds a trailing --save/-s after any
whitespace; /config notifications shares it.
apply_template expanded $ARGUMENTS and $N in several passes, so text in
the user's own argument (`awk '{print $1}'`) was expanded again, and $10
read as $1 followed by 0. It now scans the template once. $N for a
missing argument, $0 and $0N stay literal; before, $12 with one
argument became <arg1>2.
/goal took the first `budget:` and only its first word, so
"Keep the budget: 5 engineers" set a budget of 5 and dropped the rest.
Only the last `budget:` counts now, and only when everything after it
is a u32. `budget: 5000 tokens` therefore stays in the objective.
/anchor treated any text starting with remove/rm/delete as a removal.
It is a removal now only when exactly one word follows the verb
(matched in any case, after any whitespace); longer text is pinned.
Tests: RUST_MIN_STACK=8388608 cargo test -p codewhale-tui --lib --locked
-- goal anchor config user_command apply_template notification:
715 passed; 0 failed. clippy -p codewhale-tui --all-targets clean.
CONTRIBUTING gains "What a planned-work issue gives you". Every
maintainer-opened planned-work or release tracking issue carries:
- its status;
- ways to help, with files, a local test command and a done-when, and
report-only pieces first;
- what maintainers already hold;
- how to claim a piece.
AGENTS.md makes that a rule for whoever opens such an issue.
The six 0.10.3 work items (#7278-#7283) and the tracking issue #7284 were
rewritten to that shape and labelled help wanted; #7279 and #7283 also
carry good first issue.
The ROADMAP's 0.10.3 row now links #7284. It names ACP in both directions
and says Codex App Server v2 moved out, per the scope change recorded
the same day.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
contract/codex-v2.json is now the one source for the Codex App Server v2
contract: every pinned rust-v0.166.0 stable method with its tier, status,
basis, upstream variant and params/result types, the dispatch rule per
tier, the error codes and the Codewhale extensions. src/codegen reads it
with the unmodified pinned schema member and generates src/methods.rs,
src/v2.rs (319 typed definitions: params, results, notification payloads,
server-request answers, the full ThreadItem/UserInput/Thread/Turn unions
and their item unions, plus typed ClientRequestParams, ClientResponse,
ServerRequestParams, ServerRequestResponse and ServerNotification
envelopes), METHODS.md, schema/json/codex-v2.schema.json and
schema/typescript/codex-v2.d.ts. This replaces the hand-kept methods.rs
macro and METHODS.md table, whose agreement tests/methods.rs only checked
after the fact.
Selection is unchanged: 44 A/B client requests, 1 client notification,
5 server requests, 33 server notifications. Tier C and undecided rows
(124) answer -32601 and are never emitted; the 29 undecided rows with an
A/B recommendation stay undecided pending the founder.
Shapes the earlier hand types dropped are now typed: ThreadItem's shared
startedAtMs/completedAtMs beside its tagged kind, and the url/fileId
alternatives inside image inputs and function-call output items.
Codewhale extensions: Thread.executor (ThreadExecutor) and
Thread.environment (ThreadPlacement, named apart from Codex
ThreadEnvironment) stay optional and credential-free; rpc re-exports them
under their old names. CAPABILITY_UNSUPPORTED (-32020) with
CapabilityRefusalData carries the executor/placement refusal codes.
Pinned: tests/pinned/codex/codex_app_server_protocol.schemas.json
(stable export member, sha256 9b6d5364...) and common.rs (git blob
2936c018, read only by the result-type check).
Checks (local, dev-cargo.sh, macOS): cargo test --locked -p
codewhale-app-server-protocol: lib 10, contract 7, wire 3 passed;
with --features schema also schema 2 passed. The contract test
round-trips 1064 schema-derived instances over 303 pinned definitions
and validates input and output with jsonschema; removing one Thread
field from src/v2.rs made it fail. clippy -D warnings (both feature
sets), cargo fmt --check, codewhale-codex-contract --check and
codewhale-app-server-schema --check clean; Cargo.lock unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Applies the reviewed two-hunk wiring patch: mod automations plus the five
/v1/automations/*missed routes. Checked in the stack-wide app-server clippy
and tests.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Decision 77: automations run only while Codewhale is open, and a run
missed while it was closed waits for the person instead of silently
catching up.
The scheduler loop now records a continuous presence in
<root>/scheduler-presence.json. A slot that came due before the current
presence began (Codewhale closed, or the scheduler suspended past its
grace window of max(120 s, 4 ticks)) is missed. Per-automation policy
lives in <root>/missed/<id>.json beside the definition so the
AutomationRecord shape and its many struct-literal consumers stay
unchanged:
- ask (default): the slot becomes an owed occurrence. Nothing runs for
that automation until resolve_missed_run_shared answers RunNow or Skip.
- run_once: the previous coalesced catch-up (oldest owed slot, once).
- skip: a durable skipped outcome, then the next future slot.
The Operate keep-alive (mode operate) defaults to run_once, its old
behavior.
RunNow admits the owed slot through the existing claim path: dispatch
lock, schedule admission binding, durable run record before enqueue,
recovery by recover_schedule_advance, and it refuses while an earlier
run is queued or running (no overlap). Each answer appends a bounded
outcome receipt (ran/skipped, person/policy, run id). An owed slot is
only valid while the definition is active and still points at it, so
pause/resume, an rrule edit or delete withdraws it. Without a presence
file (direct ticks in tests, older stores before the first loop pass)
collection behaves as before. Delete removes the missed-state file.
App Server adapter crates/app-server/src/runtime_api/automations.rs
(list_missed, get_missed, set_missed_policy, run_missed, skip_missed).
Its mod declaration and routes in runtime_api.rs belong to the
integration owner and are delivered as a patch request; the adapter
compiled with that patch applied (cargo check -p codewhale-app-server
-p codewhale-tui --lib: 0 errors, 0 warnings; rerun after the module
docs moved here, -p codewhale-app-server --lib: 0 errors, 0 warnings)
and the patch was then reverted.
Known limits of this design: there is no background or cloud runner, so
automations run only while a Codewhale scheduler is running; a scheduler
suspended for longer than its grace window (sleep, a large clock jump)
counts as closed; delayed triggers (fire_due_triggers) keep their own
catch-up behavior.
The App Server routes read and answer the Engine store only: they run no
scheduler and start no work without the person's Run now, and they do
not notify. Clients poll GET /v1/automations/missed on open and react to
the automation.missed thread event; the card, tray and settings copy
belong to the client.
CHANGELOG: Unreleased line for automations missed while closed.
Checks: scripts/dev-cargo.sh test --locked -p codewhale-engine --lib --
automation_manager: 60 passed, 0 failed, 1 ignored (fixture entry
point), including the new
recovery::missed_occurrences_wait_for_an_answer_across_reopen journey
(close, reopen, ask/skip/run_once, no run before an answer, durable
across a second reopen, pause/resume withdrawal, overlap refusal, Run
now, Skip, double-answer refusal, exact executed task set).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
EA-02. External-agent discovery picked the extensionless npm sh shim on
Windows, never tried PATHEXT suffixes, trusted relative PATH entries and
ignored USERPROFILE/APPDATA\npm. Lookup now searches only absolute
directories, tries only the PATHEXT suffixes Rust can spawn
(.com/.exe/.bat/.cmd, in PATHEXT order), refuses binary names that are
not a single path component, and adds the Windows per-user npm folder.
Rust std quotes .bat/.cmd arguments through cmd.exe and refuses unsafe
ones, so no command string is formed. A batch-shim CLI is not handed to
an adapter through CLAUDE_CODE_EXECUTABLE/CODEX_PATH, which a non-shell
child spawn cannot run.
The --version probe now runs through
process_tree::contained_output_with_input_bounded (process group or Job,
8 KiB stdout cap, 5 s stop), so a hung CLI and anything it started are
killed. The initialize probe is contained the same way and keeps
"answered", "answered with an unsupported protocol/without JSON-RPC 2.0/
with an error" and "silent" distinct in the no_acp reason text; the
reason_code set is unchanged. The row's version prefers the CLI's own
--version over agentInfo, since min_version is a CLI floor. A missing CLI
whose only upgrade command is a self-update (agy update) now points at
the website instead of telling the person to install it with itself.
Registry facts verified from installed --help on macOS 2026-11-10:
kimi upgrade, qodercli login/update, grok login/update.
Checks: macOS local engine fleet::agents + runtime_threads::tests::
external_agent 14 passed 0 failed (second run, after a case-insensitive
APFS fixture fix); app-server runtime_api::external_agents 2 passed 0
failed (macOS and Boat farm Linux). Not run: native Windows, hosted CI,
real adapters.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
POST /v1/git/push could run bare `git push` or `git push -- <remote>`,
letting push.default=matching and remote.<name>.push publish unrelated
branches and tags; even the set_upstream path pushed annotated tags under
push.followTags. Every push now runs
`git push --no-follow-tags --no-recurse-submodules -- <remote> <commit>:refs/heads/<branch>`.
Additive contract: `branch` plus `expect.head` (the head_oid read from
GET /v1/git) binds the push to that branch and commit. A different
checked-out branch, a detached HEAD or a moved tip answers 409
git_state_changed with stale [branch|head] and pushes nothing. GET /v1/git
advertises named_push: true; an older Engine rejects the unknown `branch`
field with 400, so new clients fail closed. Requests without `branch` push
the checked-out branch at its current tip (detached/unborn -> 400). Remote
resolution: remote, branch.<b>.pushRemote, remote.pushDefault,
branch.<b>.remote, origin; it must be configured. An upstream tracking a
differently named branch is refused like git's `simple`. set_upstream now
writes branch.<b>.remote/merge after success, since --set-upstream does not
record an upstream for a commit-id source. The old push_args test moved to
tests/git_push.rs and was rewritten for the new arguments.
Checks (worktree, hermetic home, dev-cargo + sccache):
- test -p codewhale-app-server --lib -- push_args git_push: 3 passed, 0 failed
- test -p codewhale-app-server --lib -- runtime_api::git::: 28 passed, 0 failed
- clippy -p codewhale-app-server --lib --tests -D warnings: clean
- cargo fmt --all --check: clean
- CLI negative control: old bare/remote-only push sent main+unrelated+v1,
old set_upstream sent main+v1, new args sent only main.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
git_status, git_diff, git_commit_plan, git_show, git_blame, git_log,
git_merge_tree, the remote check before git_fetch and resolve_commit_ref
ran `std::process::Command::output()` inside `spawn_blocking`. Dropping the
tool call (cancel, timeout) left git, and anything it started, running to
completion on the blocking pool (residual R5, D03-m3; recovered from the
contained async helper in 524f0c7abd on opus/0101-audit-tools).
One runner now serves them all: `run_git_command_async` prepares the
read-only command on the blocking pool (Git::review_command still disables
fsmonitor, hooks and repository filters; its config probe is the stated
uncontained limit) and runs it through
codewhale_runtime::process_tree::contained_output, so dropping the future
kills git's own process group only. Path resolution, untracked-file reads
and commit planning stay on the blocking pool. The duplicate
spawn_blocking wrapper in git_history.rs is deleted. git_fetch's
deadline-bounded contained runner is unchanged.
Regression: tools::git::tests::cancelled_read_only_git_run_kills_only_its_own_tree
drops a run whose alias starts `sleep 300`, reads the grandchild PID back,
asserts it exits within 5 s, and asserts an unrelated `sleep` bystander
survives. Negative control with contained_output swapped for a bare
tokio `output()`: 0 passed; 1 failed ("a process git started outlived the
cancelled call").
Checks (macOS arm64, native, dev-cargo.sh + sccache, hermetic HOME):
- test --locked -p codewhale-engine --lib -- tools:
78 passed; 0 failed; 0 ignored; 7054 filtered out
- clippy --locked -p codewhale-engine --lib --tests -- -D warnings: clean
- rustfmt --check on both files: clean
- scripts/check-blocking-calls-budget.py: 653 sites, within budget
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Refs CW-7875
160480b985 limited the launch resume popup to a plain or Shift-only
press and refused Ctrl/Alt/Super/Meta/Hyper chords. The current guard
only masks Ctrl/Alt/Super, and normalize_macos_modifiers folds Super
into Ctrl but passes Meta and Hyper through untouched, so Meta+Enter,
Meta+y or Hyper+Enter still emitted LaunchResumeConfirmed and replaced
the whole session. The chord mask now includes META and HYPER again.
Plain Enter/y and Shift+Y still confirm, non-Press kinds are still
refused, and Esc with any modifier still backs out.
The existing modified-chord regression now builds keys through the
dispatcher's normalize_macos_modifiers and covers Meta/Hyper (alone and
with Shift), a Repeat, the still-confirming plain and Shift answers,
and modified Esc.
Checks: dev-cargo.sh test --locked -p codewhale-tui --lib -- launch_resume_confirm on macOS: 2 passed, 0 failed, 5719 filtered out. With META/HYPER dropped from the mask the extended test fails on Meta+Enter (1 passed, 1 failed). rustfmt --check clean.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stage B was still the superseded engine-tools plan (lower ~220k into a new
crate). The founder chose variant U on 2026-11-08: crates/engine stays the
lower crate and the turn loop and thread manager (core/engine/*,
runtime_threads/manager/*, tools/subagent/engine, exec_events) move up into
codewhale-engine-loop. The map now says that and assigns all 496 current
units, including the 11 the old maps left unassigned; the test-only bridge
pattern is dropped.
crate_map.py check gains --baseline/--update/--baseline-ref, the same
ratchet contract as module_graph.py: each upward unit pair across crates is
keyed lower->upper|src->dst, and the check fails on a new pair, a raised
count, a drop the baseline did not record, or a baseline raised against the
base ref. Structural problems (unassigned, doubly claimed, unmatched,
oversize) still fail outright. Without --baseline the exit code is unchanged.
Measured at origin/main 3c8eb5540c: engine 271,507, engine-loop 57,577,
app-server 55,918, tui 225,033 production lines; loop -> engine 1,279 refs;
engine -> loop 52 refs over 23 pairs, recorded in
scripts/split/engine-band-baseline.json as the Stage B work list. No
production boundary moved. The COARSE_SPLIT sentence claiming Engine still
links ratatui/crossterm/Palette was false and is corrected.
Checks: crate_map.py check (variant U, --baseline, --baseline-ref
origin/main) PASS; test_crate_map.py 8 passed; existing
test_check_command_crate_boundaries.py 30 passed. Negative controls on real
source: Engine->TUI edge, missing unit, stale baseline, removed pair and an
--update that would raise all fail with exit 1. Not wired into CI yet.
Refs CW-7764
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#7261 merged as its author (b3fa8d6d3f), carrying two maintainer review
fixes pushed to the branch (c48cdfe3c3): the dialect table is checked
only in tests, and provider_wire_dialect's doc comment is back on its
function. This adds the CHANGELOG line, a docs/CONTRIBUTORS.md entry and
@SamhandsomeLee in UNRELEASED_CONTRIBUTORS, and regenerates the website
feature and agent-docs data for the new custom-responses-profile row.
Evidence: review farm runs on the PR head 736510f3b3: codewhale-config
lib 832 passed, codewhale-runtime lib 934 passed, codewhale-engine
client:: 645 passed, codewhale-tui conformance:: 36 passed. After the
review fixes: responses_dialect 6 passed, and clippy -D warnings for
codewhale-config and codewhale-runtime is clean. Hosted PR checks on
c48cdfe3c3 passed (Lint, Test windows-latest). check-contributor-credit,
check-facts and sync-changelog pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(config): load Responses dialect profiles from a checked JSON table
Responses behavior is still chosen by ProviderKind. The table gives routing a validated profile id before the engine starts consuming it.
* feat(config): stamp a Responses profile id onto the resolved route
The dialect table existed without a route to carry it. Selection now happens after the protocol is known, so Chat routes stay empty and Responses routes cannot be minted without a profile.
* feat(config): let a custom provider name its Responses profile
A custom table could set wire to responses but not name the dialect, so every such route inherited standard. The field keeps that default, and rejects a built-in table, a non-Responses wire, or any profile outside the custom allowlist.
* feat(engine): carry the resolved Responses profile into the client
Fixed-wire clients were built from parts and dropped the profile the route had already chosen. The id now stays on the client and the prepared request; a catalog probe does not invent one, and a Responses client without an id fails before the body is built.
* feat(engine): build Responses requests from the admitted profile
The client already carried a profile id, but the body and replay still branched on the provider. Those choices now come from the dialect profile, while encrypted scope stays with the caller.
* feat(engine): drive Responses URLs and stream termination from the profile
The request body already followed the dialect profile, but the endpoint and SSE completion still branched on the provider. Path policy now chooses the URL, including DeepSeek search, and strict completion stays on the profile while account-sharing errors stay on the Codex identity.
* fix(engine): keep the standard profile helper out of the library build
The helper is only called from tests, and the library build denies unused code.
* feat(tui): record Responses route, request, and stream fixtures
Profile selection, the prepared body, and termination now have a compare-only family. It uses the existing resolver, prepare path, and loopback instead of a second parser.
* fix(tui): give the Codex request fixture its own test grant
The official route will not build a client from a process token. Isolate CODEWHALE_HOME and install the existing test registration so the request golden is compared.
conformance::responses and conformance::sse: 6 passed; 0 failed.
* fix(config): check the dialect table only in tests; restore the wire dialect doc
Review follow-up for this PR. The embedded dialect JSON was re-parsed
and re-generated at runtime by ensure_asset() on every lookup, which put
the parser in the library build although build.rs already validates the
same file at compile time. The parser module is now test-only and the
check is the embedded_asset_matches_the_generated_table test.
provider_responses_profile was inserted between provider_wire_dialect
and its doc comment; the doc is back on its function.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: CodeWhale Bot <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Founder decision §124 pauses App Server v2 so 0.10.3 can focus on ACP,
the DSH plugins and the rest of the release. The work-in-progress item
becomes a moved-out item: the typed Tier A/B inventory stays, dispatch,
subscriptions, the WebSocket transport and the SDK are not in 0.10.3,
and the legacy JSON-RPC dialect is not retired in this release.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Moved: all 8 [Unreleased] entries into 0.10.3 (6 to Fixed, Website and
Chinese docs to Added, Permissions merged into the Accept Edits line);
[Unreleased] is an empty heading. Merged: #7254 into the #7215 grep_files
line, the Windows ledger and sidecar fix into one Windows line.
Added 5 lines: provider timeout "after 0ns" (#7253), failed-send draft
(#7255), Windows plugin runtime-root ACL plus ledger compaction and
checkpoint inspection (b3e874b8fb, 06a05bd75d, 34f8219bb1, 13858fa8d0),
pet owner token constant-time compare (Security, fdff4514a9), and the
website roadmap, locale and guide changes folded into the Website line.
Contributors gains zhuowp #7251, gaord #7253, luochen211 #7263-#7266 and
Lstarsky0 #7254/#7255. Internal tracker ids removed from the section.
Work in progress: Codex item notes the typed Tier A/B inventory
(42ebdb9ea4); DSH item notes the 0.2.2-rc.1 uplift with no receipts yet;
Windows item says the suite is not shown green; new External agents and
One permission dial items; internal tracker names removed.
Checks run: sync-changelog.sh, check-feature-release-notes.sh v0.10.2 HEAD
(32 references OK), check-contributor-credit.py (all credited),
derive-facts + check-facts (OK, no regenerated change).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The six PRs merged as their author: #7256 (footer context meter after a
restore), #7257 (fuzzy edit keeps the rest of the line), #7258 (@ after
CJK text), #7259 (two ranges of one file), #7260 (custom command
arguments after any whitespace) and #7267 (/config column). This adds
their CHANGELOG lines, extends the docs/CONTRIBUTORS.md entry and adds
@Lstarsky0 to UNRELEASED_CONTRIBUTORS.
It also moves #7258's new test above the "#101 regression" doc comment,
which the PR had separated from cwd_pass_resolves_when_workspace_pass_misses
(review finding; the comment was false otherwise).
Evidence: each PR's tests ran on the Boat farm at its reviewed head
(7256 3/3, 7257 11/11 with fix and 10/1 failing without each commit,
7258 60/60, 7259 60/60, 7260 52/52, 7267 1/1; 7256 negative control
2 passed 1 failed). After all six merged, the combined tree with this
commit: farm run 20261110T115913Z-coordland-0a2f80, cargo test -p
codewhale-tui --lib -- file_mention context_usage_cache commands::user
setting_detail_values: 117 passed, 0 failed. check-contributor-credit,
check-facts, check-versions and cargo fmt --check pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(tui): attach every line range when one file is mentioned twice
Mentions are deduplicated before they are attached, and a resolved file
is keyed by its path alone. `compare @notes.txt:1-1 with @notes.txt:4-4`
therefore attached lines 1-1 and dropped 4-4, while the composer showed a
"resolved on send" chip for each and the model got no hint that the
second range was missing.
Key a ranged mention by path and range. Repeating the same range, or the
whole file, still deduplicates as before.
Signed-off-by: Lstarsky0 <[email protected]>
* fix(tui): keep a range key from matching a literal file name
The range was joined to the path with `:`, which a file name can contain.
With files `notes.txt` and `notes.txt:1-1`, `@notes.txt:01-01
@notes.txt:1-1` gave both the key `notes.txt:1-1`, and the literal file
was dropped. Join them with a NUL, which no path contains.
Signed-off-by: Lstarsky0 <[email protected]>
---------
Signed-off-by: Lstarsky0 <[email protected]>
* fix(tui): recognize an @ mention that follows Chinese or Japanese text
An `@` only started a file mention after whitespace or an ASCII bracket
or quote, so that `[email protected]` stays text. Chinese and Japanese don't put
spaces between words, so `请看@src/main.rs 讲了什么` attached nothing:
the message went out without the file, Tab didn't complete `请看@src/ma`,
and the composer showed no mention chip. Nothing said the mention was
ignored.
Also accept a mention start after a CJK character, using the tui-kit
`is_cjk` predicate (ideographs, kana, CJK punctuation and full-width
forms, so `(@a.rs)` and `「@a.rs」` work too). Attachment, completion and
screenshot-path stabilization all go through `is_file_mention_start`, so
they change together. ASCII text before `@` is handled as before.
Signed-off-by: Lstarsky0 <[email protected]>
* fix(tui): drop full-width closing punctuation from a mention's end
A bare mention runs to the next whitespace, and only ASCII closing
punctuation was trimmed from its end. Now that an `@` after CJK text
starts a mention, `(@a.rs)` read as `a.rs)` and `看@c.rs。` as `c.rs。`,
so the file came back as missing.
Trim the full-width forms of the same punctuation, plus CJK closing
brackets, `。` and `、`.
Signed-off-by: Lstarsky0 <[email protected]>
---------
Signed-off-by: Lstarsky0 <[email protected]>
The detail pane's fact labels were padded with `{:<10}`, which counts
chars, and the value was truncated to the width left after 10 columns.
That only holds for English. A Chinese, Japanese or Korean label is two
columns per char, so `当前` and `已保存` padded to 12 and 13 columns, the
values started in different columns and could run past the pane. A
label of 10 columns or more got no padding at all, so German showed
`gespeichertde` and Ukrainian `під час запускуuk` and
`застосуваннязастосовується…`.
Size the label column from the widest fact label in the locale, plus a
space, and pad by display width with `fit_config_column`. English labels
are at most 9 columns, so English keeps its 10-column layout.
Signed-off-by: Lstarsky0 <[email protected]>
User commands were split from their arguments on an ASCII space only,
while built-in commands split on any whitespace. A full-width space,
which Chinese and Japanese input methods type in full-width mode, or a
newline before multi-line arguments left the whole line as the command
name. `/ask 闭包` or `/ask` + newline + text then fell through to the
built-in lookup and failed with "Unknown command: /ask. Did you mean:
/ask, ...", naming the command it had just failed to find.
Split on any whitespace, as the built-in dispatch already does.
Signed-off-by: Lstarsky0 <[email protected]>
* fix(engine): keep the rest of the line when edit falls back to fuzzy matching
When an edit's oldText only matches after normalization (NFKC, trailing
whitespace, typographic quotes/dashes/spaces), the lines it touched were
rebuilt from the normalized text. Everything else on those lines went
through NFKC too, so full-width characters in Chinese, Japanese or Korean
text were rewritten: in `PRICE = "价格:100元(含税)" # “促销”`,
replacing the comment `# "促销"` turned the string literal into
`"价格:100元(含税)"`. The model only gets "Successfully replaced 1
block(s)" back.
Build the fuzzy view of the touched lines one character at a time,
recording where each original character lands, and replace only the
matched range of the original text. Trailing whitespace stays invisible
to the fuzzy view as before: a match that reaches the end of a line takes
it along. When the per-character view differs from the normalized text
(a base letter composing with a following combining mark, for example),
the group falls back to the previous behavior.
Signed-off-by: Lstarsky0 <[email protected]>
* fix(engine): keep adjacent fuzzy edits from overlapping after remapping
Trailing whitespace is invisible in the fuzzy view, so a normalized
boundary at the end of a line maps to several original offsets. An
edit's end took the last of them and the next edit's start the first,
so two edits meeting at that boundary both claimed the whitespace. On
`“A” \nB\n`, replacing `"A"` with `X` and `\nB` with `\nY` wrote `X\n`,
and an empty second replacement panicked on an out-of-range slice.
Clamp each remapped end to the next edit's start. The whitespace goes to
the later edit, which matches what the normalized path did.
Signed-off-by: Lstarsky0 <[email protected]>
---------
Signed-off-by: Lstarsky0 <[email protected]>
The footer's context meter caches one estimate per message and recomputes
only the last entry each frame. Restoring a session assigned api_messages
directly and left the cache alone, so after /load of a smaller session the
meter kept the previous conversation's numbers for every index both share:
a session that reads 2% showed 5% after /load from a larger one, and 6%
after the next turn.
Clear the cache where a restored conversation is installed. set_api_messages
now trims the cache to the exactly-equal prefix it keeps, which the engine
projection did at its one call site with the looser same-turn prefix; its
other callers append to or rewind the current history, so nothing changes
for them.
Signed-off-by: Lstarsky0 <[email protected]>
Co-authored-by: Hunter Bown <[email protected]>
Replace the old stacked footer slogan with each locale positioning headline written as one sentence. Synchronize EN/ZH catalogs after the task-guide control corrections and correct the hero reduced-motion description.
Integrated gates: tsc, lint, locale parity, docs mapping, facts, production build and preflight passed. Vitest: 76 files and 773 tests passed. Browser: 28 routes in 224 EN/ZH desktop/phone light/dark combinations, no page errors or page overflow; docs captures now wait for search hydration and wordmark assets. Native translation review remains outstanding. No deployment.
Fleet and subagent guides still attributed independent delegation to retired Operate, hooks still named it as a mode-change event, and progress/quickstart/navigation summaries repeated the old controls. Describe Work delegation, Plan/Work mode changes and version-aware approval guidance instead. Include Accept Edits in the hook ask explanation, following the existing approval gate; keep published v0.10.2 history in the versioned mode guide.
Verified: dictionary, docs-task, vocabulary and public-surface suites: 49 passed across 4 files; focused ESLint passed; git diff --check passed. Coordinator owns final full gates and refreshed browser receipts.
Verify v0.10.2 and current source preserve an unsent keyless message and open provider setup. Replace the stale v0.10.0 diagnosis with conditional recovery, selected-provider auth status and explicit hosted/local connection probes. Tag evidence: session_state.rs:960 and keyless recovery tests; pet style/language also exist in the tagged config command handlers.
Scoped ESLint and locale checker passed; vitest: 2 files, 7 tests passed. No live provider call or credential mutation. Parent owns browser and full site gates.
Move 100 project strings into dictionary files and resolve exact locale keys through the existing content picker, retaining English fallback for unmigrated content. Keep links and project structure shared. Register the project dictionary in existing locale and GT catalog checks; no translation API was used. New translations are model-authored drafts; native review remains outstanding.
Verification: TypeScript and focused ESLint passed; locale parity passed (100/100 project keys in every locale); runtime dictionary tests: 2 files, 22 passed; GT local export and check passed. Parent owns full web gates and browser acceptance.
Add published install choices, version verification and a quickstart route before the complete guide. Count only successful installer copies; diagnostic copies no longer inflate install intent. Add star actions with fetched counts, keep ideas in issues, update Indonesian contribution guidance and remove unused home strings. Fix repeated intro storage writes found in review.
Local browser evidence: 32 EN/ZH width/theme captures returned 200 without horizontal overflow or page errors; clipboard choices, diagnostic counter exclusion and article search exercised. Full integrated gates run before landing; no deployment.
Shared vocabulary and docs search still exposed Operate, called Ask the default and claimed Auto-Review never asks user questions. Use the current Plan/Work and four permission postures, correct the authority descriptions, scope the English/Chinese vocabulary page to unpublished 0.10.3, and link the versioned mode guide. Update the existing onboarding contract for answer verification and assert Fleet remains optional after the required first task.
Verified: vocabulary and public-surface suites: 23 passed; focused ESLint passed; git diff --check passed. Full gates and browser review remain with coordinator.
Reuse DocsSearch and the existing human-facing dictionaries for 17 task articles plus quickstart, tools and vocabulary. Return anchored sections and excerpts; keep catalogue destinations searchable by metadata. Align guide, auth, hooks, Fleet, subagents and troubleshooting source registrations with their authoritative docs.
Scoped ESLint and tsc --noEmit passed. Vitest: 4 files, 41 tests passed; check:docs parsed 25 topics and passed. Full gates and browser review are coordinated separately. Install, model, plugin and constitution custom bodies remain outside the article index; their topics still match existing metadata.
Keep v0.10.2 published shortcuts in an explicit version section and describe Plan/Work, the single Shift+Tab dial, Accept Edits, and the Operate compatibility alias as source-candidate behavior. Correct Ask versus Accept Edits and Full Access boundaries. Explain approval shortcuts only when offered, including exact repository rules and child-card Escape behavior, verified against approval/view.rs.
Verified: dictionary and docs-task suites: 26 passed; focused ESLint passed; locale check PASS; git diff --check passed. Coordinator owns full web and browser verification.
Replace the hero character/SVG bead renderer with the generated launch lattice, a 650ms mark hold and 960ms reveal. Use equal drone radius, density selected by physical pixel pitch, still drones for reduced motion and visibility/focus suspension. Keep the existing whale geometry and hero layout.
Verification: npx tsc --noEmit and focused ESLint passed; existing design/ocean tests: 2 files, 14 passed. Browser acceptance and reference-Mac CPU measurement remain outstanding.
Add a read-only project-file prompt and a reader check against cited lines, version verification, prerequisites and troubleshooting. Keep optional Fleet guidance after the four required steps. Focused ESLint passes; vitest: 2 files, 16 tests passed. Full site gates and browser review remain with the coordinating agent.
The runtime retired Operate and now exposes a permission dial and fourteen eager tools. Update the owning public facts and shared English/Chinese copy without attributing source-candidate behavior to v0.10.2. Keep native schema, policy, concurrency and cache checks; replace obsolete mode and page-layout assertions with current runtime authority checks.
Verified: public-surface-contract.test.ts: 12 passed; git diff --check passed. Full web and browser gates remain with the coordinator before landing.
closed_diagnostics_channel_is_an_error_not_an_empty_result failed on
both nextest attempts of hosted CI 42788433126 (macOS, 59b1664105):
'LSP diagnostics timed out before a current publication'. With a 10 ms
wait, open_or_change on a loaded macOS runner used the whole deadline,
so diagnostics_for returned at its remaining.is_zero() check before
reading the closed channel. A closed channel returns at once, so a 5 s
deadline keeps the test fast and makes it check what it names. The
product behavior and the silence-is-timeout test stay as they were.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Follow-up promised in the review of #7264: the audit-log paragraph
pointed at the English docs/RECEIPTS.md now that zh_hans/RECEIPTS.md
exists.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Founder rule, 2026-11-10: public copy uses whole sentences, never choppy
fragments. The roadmap's "Open:" lines, release table and starting points,
and the contributing guide's standing projects and commitments list, are
rewritten as sentences. Goal 2 is renamed "Every model is a peer" to match
the website, and the roadmap intro carries the current position. "What to
expect from us" becomes "How we work with you".
Checks: scripts/preflight.sh --check OK; README translation check 18 in
sync (README unchanged).
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The four zh_hans docs PRs merged as their author. This adds their
CHANGELOG line and the docs/CONTRIBUTORS.md entry, and adds @luochen211
to UNRELEASED_CONTRIBUTORS. The squash merges carry GitHub's profile
name 落尘, so .github/AUTHOR_MAP now maps [email protected] to
luochen211 for the credit checker.
The translations added Chinese links to docs/RECEIPTS.md and
docs/CLAUDE_PLUGIN_COMPAT.md, which left web/data/agent-docs.generated.json
stale; it is regenerated here.
Checks: check-contributor-credit.py "Every contributor in the window
is credited on all three surfaces"; derive-facts + check-facts OK;
sync-changelog wrote crates/tui/CHANGELOG.md; check-versions OK.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(zh_hans): add a short provider setup page for beginners
Add a Chinese-only page that walks a first-time user from an installed
codewhale to a first answer: what a provider, model and API key are,
the /provider picker flow, local Ollama, the built-in China-based
providers, custom OpenAI-compatible endpoints, where keys are stored
and the most common setup errors. It links the full references.
* docs(zh_hans): add Windows env var commands and correct doctor probe output in provider quickstart
Show PowerShell $env: and setx alongside the POSIX export, describe the
second line codewhale doctor --probe-api prints after a failed probe, and
list the page under the already-installed section of the zh index.
---------
Co-authored-by: CodeWhale Bot <[email protected]>
Founder direction, 2026-11-10: the headline "An open-source agent for your
computer" undersold the product; the site should feature contributors
rather than state promises; copy is written in whole sentences.
Headline: "Use any model through an agent you can read". The lede says
what is inspectable: Codewhale, the open-source agent between the reader
and the model, which can also be asked to read its own code. Set in all 18
locale dictionaries, MESSAGING.md updated, and COPY_STYLE.md gains rule 12
(whole sentences, no fragment slogans). Fragment copy from the previous
slice is rewritten as sentences.
Live project, from the GitHub layer the site already had (loadFeed,
fetchRepoStats) plus three small list calls in lib/github.ts
(fetchFaces, fetchMilestones, fetchLabelled):
- Home: stars, contributors, open pull requests and issues; the latest
merged pull requests with author avatar, localized age and GitHub's
first-time-contributor flag; a wall of contributor faces. This replaces
the static four-step loop.
- Contribute: live help-wanted issues and recently merged pull requests
with the contributor wall replace the "What you get back" list.
- Roadmap: each release shows its milestone's closed/total count.
Every live block is omitted when GitHub does not answer or during static
generation; the pages still render.
The seven goals carry the existing whale poses (run, connect, needs, pod,
read, talk, think), and the pod pose sits beside the project numbers.
Checks: tsc clean; eslint clean; check-locales PASS; gt-site catalogs
re-exported; next build OK; vitest 768 passed, 2 failed (the same two
public-surface-contract failures as origin/main). Rendered /en, /zh,
/en/contribute and /en/roadmap against live unauthenticated GitHub data at
1280 and 375 wide. Not deployed.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The site did not say what the project is aiming at, the roadmap page was
unreachable from the navigation, and the contribute page listed four
generic paths with no routing. This brings the site in line with the new
CONTRIBUTING.md and docs/ROADMAP.md.
Home: a deep-sea band lists the seven long-term goals and links the
roadmap. The community section becomes the improvement loop (use it, say
what happened, shape the change, see it ship) with a feedback action and
the contributor count. The proof row links contributors and the roadmap,
and the closing band adds the community link.
Roadmap: the seven goals with what is still open and their issues, then
what each release milestone is for. The shipped/underway/ruled-out tracks
and their live feed are kept below.
Contribute: a routing list (what you have, where it goes), the
issue-first path for features, the standing projects that need no issue,
and what a contributor gets back.
The copy lives in lib/content/project.ts as en/zh pairs, the pattern the
home page already uses; other locales read English there. A footerRoadmap
chrome key is added in all 18 locale dictionaries and the roadmap joins
the footer and the compact menu; docs-ia.test.ts expects it.
Checks: tsc --noEmit clean; eslint clean; check-locales PASS; gt-site
check OK; next build OK; vitest 768 passed, 2 failed. The two failures
are in lib/public-surface-contract.test.ts (MODES.md wording and the
native tool list) and fail identically on origin/main without this
change. Rendered and inspected /en, /zh, /en/roadmap and /en/contribute
at 1280 and 375 wide, light and dark. Not deployed: web.yml is a manual
dispatch.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
docs/features.toml changed on main without its derived web data, so
web/data/features.generated.json and agent-docs.generated.json were stale.
Regenerated with web/scripts/derive-facts.mjs; no hand edits.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Add a Simplified Chinese translation of the Claude plugin compatibility
page, link it from the English page and the zh_hans reading guide, and
point the four zh_hans pages that linked the English version at it.
Add a Simplified Chinese translation of the receipts page, link it from
the English page and the zh_hans reading guide, and point the zh_hans
GUIDE and RUNTIME_API links at it.
The contributor guide opened with Rust prerequisites and never said where
a report, an idea or a pull request should go. A feature that arrives as a
pull request costs more review time than one that starts as an issue, and
nothing told contributors that.
CONTRIBUTING.md now opens with a routing table: issues for bugs, ideas and
feedback; pull requests for fixes, translations, providers, docs and
planned work; an issue first for a new feature, with the reason. A feature
PR without an issue is still read and never closed for that. The build,
check, landing, harvest, layering, label and structure sections are kept
as they were. Removed: the stale v0.8 exemplar list and the duplicated
submit/guidelines sections. Corrected: "a push to main starts no GitHub
build" has been false since main-landing.yml began dispatching ci.yml on
2026-11-08. The agent section no longer points at a private repository.
docs/ROADMAP.md is new: seven long-term goals with their open questions,
the release milestones, and where to start. It carries no dates.
A Feedback issue form lowers the bar for "this looked or felt wrong". The
feature form and PR template say that an issue is the faster start for a
feature. The README Community section links the guide and the roadmap; all
18 locale READMEs carry the same paragraph and the new stamp.
Checks: scripts/preflight.sh --check OK (README translation check: 18
translations in sync, sha256:a9ec5fb3f3dd; locale link symmetry PASS);
0 broken relative links or anchors across the five edited Markdown files;
five issue-form YAML files parse. No CHANGELOG line: no product behavior
changes.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Hosted CI 42771647964 on e173b159b1 was the first nextest, macOS and
Windows run of the external-agent code. Lint (clippy) and MSRV passed.
Test failed on all three OSes: ubuntu 19178 run / 1 failed, macOS 19184
run / 1 failed, Windows 18426 run / 4 failed. No product behavior changes
here.
1. a_fixture_agent_turn_runs_end_to_end_and_never_auto_approves (ubuntu
"missing turn.completed", macOS "Thread already has an active turn").
Turn settlement saves the terminal status, publishes turn.completed,
then frees the thread, the same order the native path uses. The test
helper polled only the stored status and went on at once. wait_for_turn
now also waits for the thread to be free, through a cfg(test) probe
external_turn_is_active.
2. Windows, three external-agent tests: "The fixture agent needs `node` on
PATH." The tests' own guard ran `node --version`, which Windows
resolves to node.exe, while the product resolver looks for a file named
exactly `node`. The guard now asks the product resolver, so the tests
skip where the product cannot launch the fixture.
3. Windows, typed_methods_inventory_and_pinned_export_agree: "inventory has
no section Client requests". METHODS.md is read with include_str! and
matched on "## <section>\n"; a Windows checkout made it CRLF. It is now
pinned to LF in .gitattributes beside the protocol schema.
Left open, found here and not fixed: fleet::agents::resolve ignores
PATHEXT, so no external agent resolves on Windows and these tests skip
there. And a client that starts the next turn the instant it reads
turn.completed can be refused with "Thread already has an active turn",
on external and native sessions alike.
Checks: Boat farm run 20261110T084916Z-wfmainred-ea01fix2-f022b5 (Linux
x86_64, CARGO_INCREMENTAL=0), cargo test --locked -p codewhale-engine
--lib -- external_agent fleet::acp
approval_required_awaits_external_decision_allow
external_approval_is_never_acknowledged_when_its_decision_append_fails:
14 passed, 0 failed. rustfmt --check clean. The two races and both
Windows failures were not reproduced locally; hosted CI on this commit is
the proof.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The EA-01 review asked for this sentence on the "Sessions on your own
installed agents" entry, to carry the canceled-startup fix that precedes
this commit. crates/tui/CHANGELOG.md regenerated with
scripts/sync-changelog.sh; --check reports it up to date.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
external_session held engine_load as a guard borrowed by the caller's
future while a blocking worker started the agent process and ran ACP
setup. Aborting the caller released the guard with the worker still
running, so shutdown could pass the loading barrier, snapshot no session
and return while the Fleet process lived until its setup timeout.
The guard is now owned, travels into the blocking setup and comes back
beside its result, result first: an abandoned SessionStart drops and
stops its process before the barrier unlocks, and a live caller keeps the
same guard through the post-setup admission check, record persistence,
active insertion and executor.started as before. Setup also watches a
child of the Runtime cancel token that the caller cancels on drop, so an
abandoned or shut-down startup fails its pending ACP request within the
25 ms poll instead of holding the barrier for up to three 60 s deadlines.
AcpClient gains abort_requests_on for that; it is cleared before the
session thread takes the client, so prompt reads keep their existing
cancel and forced-stop path. start_session_blocking takes the executor
record in place of two of its fields. No second process owner, startup
store or turn runtime.
Regression: a fixture variant records its pid and never answers
initialize; the test aborts the caller, shuts down and requires the
process to be gone when shutdown returns.
Evidence (Boat farm, Linux x86_64, base 2e90ca43f8 plus 17420280cf and
8d4645f2c6):
- pre-fix source with the new test: 0 passed, 1 failed ("shutdown
returned while the abandoned agent process ... was still running").
- cargo test --locked -p codewhale-engine --lib -- external_agent
fleet::acp approval_required_awaits_external_decision_allow
external_approval_is_never_acknowledged_when_its_decision_append_fails:
14 passed, 0 failed, 7114 filtered out, with CARGO_INCREMENTAL=0. The
first attempt with incremental on died in rustc with exit 137 before
any test ran.
- sh scripts/external-agent-e2e.sh: PASS.
- scripts/preflight.sh --check: OK.
Not run: hosted CI, macOS, Windows, any real agent.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Recover CW-7879 executor work and CW-7704 approval metadata into one Fleet ACP client and Runtime journal. Preserve installed agents own credentials and sessions, durable decisions, bounded untrusted streams, cancel/shutdown ownership, and explicit unsupported-placement refusals. Refuse ineffective external model/tool/instruction overrides.
Local checkpoint for integration review, not qualified for landing yet. Protocol gate passed 10 lib + 2 inventory + 3 wire tests. Combined Engine and HTTP fixture checks remain in progress against the previous source; final integrated checks will run after rebasing. Formatting, diff check and static preflight passed.
Co-authored-by: Claude Opus 5.5 <[email protected]>
App Server v2 had no committed method inventory, so Tier C "method not
found" had nothing to stand on and no check tied Codewhale's method set to
the pinned Codex contract.
- src/methods.rs: ClientRequestMethod (44: 19 Tier A, 25 Tier B),
ClientNotificationMethod (1), ServerRequestMethod (5) and
ServerNotificationMethod (33: 27 A, 6 B), each with wire name, tier,
lookup and string serde. Variant names follow upstream common.rs.
- METHODS.md: every method in the rust-v0.166.0 stable wire enums (109
client requests, 1 client notification, 10 server requests, 87 server
notifications) with tier A/B/C/undecided, status on main and basis.
Undecided rows carry a recommended tier for the founder.
- tests/pinned/codex/*.ts: unmodified TypeScript members of the frozen
stable export (hashes in UPSTREAM.md). TypeScript, not the JSON schemas,
because only it carries the full wire enums (the JSON omits three
deprecated v1 requests and two internal-only notifications).
- tests/methods.rs fails when the pinned export names a method the
inventory lacks or vice versa, when a typed variant is missing from the
export or disagrees with its inventory tier, or when an A/B row has no
typed variant.
Params/response types per method are not in this slice; dispatch is
unchanged.
Checks: cargo test -p codewhale-app-server-protocol --locked: lib 10
passed, methods 2 passed, wire 3 passed, 0 failed. Removing the fs/copy
row made typed_methods_inventory_and_pinned_export_agree fail
(left: ["fs/copy"]). Dead-code budget 137, at budget.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Hosted CI 42750482912 on 2e90ca43f8 failed all three Test jobs. Eight of
the failures are stale expectations or a notices wrap; none is a code
regression.
Notices (from 7d6779d346): THIRD_PARTY_NOTICES.md already named
@deepseek-ai/dsh-llm 0.2.2-rc.1, but a line wrap fell between the package
and its version, so the licence test's "`name` version" lookup missed it.
Rewrapped; no entry added or removed.
Accept Edits default (from 29665bfa61, founder D2). Updated to the new
default, matching what that commit already did for the sibling
mode_only_override_preserves_legacy_full_access_posture:
- app-server start_turn_accepts_dynamic_tools_and_environment_id: a thread
created with no posture reads accept_edits.
- engine start_turn_can_override_thread_auto_approve_to_false: legacy
auto_approve=false with no posture clears Full Access and lands on the
default posture, now accept_edits / AcceptEdits.
- tui leaving_yolo_after_startup_restores_baseline_policies: the unset
baseline is AcceptEdits.
- tui status baseline fixture: "approvals accept edits".
- tui config panel goldens at 80x24 and 120x32: the preview is the single
dial chip "accept edits"; the separate "work" chip left with the dial
(D1). 40x12 has no preview row and is unchanged.
Windows only (same commit): two tideline tests read the footer on a host
with no sandbox backend, where the chip carries the unenforced file scope.
The wider "accept edits" word leaves a 31-column notice budget at 80
columns, one short of "Files not changed. Use /restore.". session_app now
pins sandbox_backend = None, as one_owner_tests already does, so every
host asserts the same row: Plan reads "plan · files: read-only
(unenforced)", and the restore pointer joins at 96 columns.
Checks: Boat farm run 20261110 wfmainred-six (Linux x86_64,
CARGO_INCREMENTAL=0), cargo test --locked -p codewhale-engine
-p codewhale-app-server -p codewhale-tui --lib with the six test names
plus tideline_tests: app-server 1 passed, engine 2 passed, tui 39 passed,
0 failed. rustfmt --check clean on the four Rust files.
Not addressed, not caused by the above: Windows
task_manager::tests::human_wait_is_excluded_from_the_task_wall_clock (a
400 ms wall budget against a loaded runner; passed on fd9ca79d70) and
macOS dsh_package_preview_then_exact_install_over_http ("composition
reviewer deadline elapsed"; passed on Linux and Windows).
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Validate workflow result availability in execution order, isolating parallel
siblings and conditional branches. Refuse implicit relative StateStore
locations when no absolute home resolves. Treat provider catalog timestamps
more than five minutes ahead as stale while retaining explicit fallback rows.
Recovers current-owner guarantees from 737eb3d5847, f1176667c4c and
77ed7363c74 without restoring retired runtime or cache implementations.
macOS hermetic cached checks: workflow lib 238 passed, 0 failed;
config catalog focused 3 passed, 0 failed; state focused 2 passed, 0 failed.
Each new behavioral regression failed 0 passed/1 failed without its guard;
restored final tree selected 1/0 per crate (3 total). First workflow negative
control was rejected by -D warnings for its unused helper, then corrected
by removing that helper and rerun to the expected assertion failure.
Preflight 8 steps passed; version drift and ordinary whitespace passed.
No hosted CI, provider, installed-artifact or release qualification claimed.
Root review found the removed Ask-to-Accept-Edits migration still declared a setting with no runtime owner. Remove its final schema consumer rather than advertise a dead setting.
Root cached macOS checks: 4 settings-schema tests passed, 0 failed; explicit saved-choice Runtime regression 1 passed, 0 failed. Worker proof on the same mode behavior: macOS TUI 8/0, Runtime 1/0; Linux TUI 7/0, Runtime 1/0; rebased preflight 8 checks passed. Ordinary source whitespace passes; four fixed-width footer goldens retain validated trailing cell padding. No installed or release acceptance claimed.
Remove the unlanded Ask-to-Accept-Edits migration: explicit saved permissions
remain the authority, including Ask from existing installs. Keep unmanaged
startup at Accept Edits when no choice exists. A requirements-only locked
policy keeps the strict Ask fallback instead of inheriting the new default.
Settings shows the same absent-choice default. Update the existing permission
and footer expectations, and keep fixed-width golden padding intact.
Verified on macOS: Runtime settings regression 1 passed/0 failed; focused TUI
cases 8 passed/0 failed (six prior failures, ConfigView source/default, full
dial walk). Hermetic dev-cargo with shared sccache, TUI -j6. Rustfmt passed;
locale parity 15 packs at 2821 keys; 18 README translations in sync;
blocking-call budget 653 sites; dead-code budget 137 suppressions. Lexicon is
warn-only (232 findings). Four padded goldens have exact row widths;
ordinary source diff whitespace check passed with those snapshots excluded.
Rebased scripts/preflight.sh --check passed all eight steps.
Linux farm final bundle: Runtime settings regression 1 passed/0 failed and
focused TUI 7 passed/0 failed; compile 2m53s, exit 0. The extra macOS case
walks the full dial. Rebase over the separately checked DSH host uplift
changes only the two changelog files in this slice. Hosted CI, installed TUI,
publication and release qualification remain separate.
The docs, README (and its 18 translations), zh-Hans mirrors, the features
registry and the lexicon check still described Tab cycling Plan -> Work
(-> Operate), Ask as the default posture and a folder-fenced Full Access.
They now describe the Shift+Tab dial (Ask -> Accept Edits -> Plan ->
Auto-Review -> Full Access), Tab as completion only, Accept Edits as the
default with the one-time ask migration, Operate parsing as Work with a
notice, and Full Access as "commands can reach anything on this computer".
docs/zh_hans/MODES.md was still on the pre-retirement three-mode text and
the old "mode locked while a turn runs" rule; it now mirrors English
(Fleet rendered 舰队). New copy follows the ADE-PLAN-20261109 section 1.3
vocabulary (agent, conversation).
Checks: check-readme-translations OK (18 in sync, sha256:fb068568394f);
features.toml summaries 111 and 103 chars; check-lexicon warn-only.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Founder D1/D2/D4 (modes-postures FOUNDER-DECISIONS, 2026-11-07): Shift+Tab
steps one dial, Ask -> Accept Edits -> Plan -> Auto-Review -> Full Access
-> Ask. Plan is AppMode::Plan with the posture kept dormant; every other
stop is Work with that posture. App::permission_dial_next/step_permission_dial
compute the stop from (mode, posture) and reuse select_mode and the existing
posture persist paths (select_approval_posture / select_root_approval_posture,
formerly cycle_*). When config or managed policy owns the posture, the dial
still alternates between that posture and Plan instead of going dead.
Tab only completes (slash menu, mention menu, commands, file mentions,
prompt suggestion): ShellBindingId::ModeCycle, is_mode_cycle_shortcut,
App::cycle_mode, TabDispatch::ModeCycled, the MODE_CYCLE footer hint and
AppMode::next/previous are gone; AppMode::CYCLE is renamed ALL (tests only
enumerate it). The footer carries one dial chip ("plan" in Plan, else the
posture word) with "Shift+Tab to change"; the separate mode chip and its
Tab key left TidelineFooter and the config preview.
Mid-turn: select_mode never refused while busy, and the engine already
applies a queued ChangeMode at the next step from the live authority
snapshot, so crossing Plan mid-turn needs no new path. Prompt and tool
catalog stay mode-agnostic; no KV-cache effect.
D2 default Accept Edits: unset TUI posture (init, root-policy release),
the settings schema default, the config preview, the "Normal agent" setup
preset, and new Runtime requests with no permission_posture
(RuntimePolicyProjection::from_request). ApprovalMode's #[default] stays
Suggest on purpose: engine/automation code uses it as the conservative
fallback, and a persisted thread with no posture still fails closed to Ask.
A saved permission_posture = "ask" migrates once to accept-edits at settings
load; ask_to_accept_edits_migrated records it so a later explicit Ask is
kept (one new test pins that data-migration contract). ACP still folds
Accept Edits to Ask.
D3 Operate (finishing c50612d6ab): CommandMode::Operate is removed and
operate spellings parse as Work; /mode operate says Operate was retired.
A Runtime request with mode "operate" runs as agent + default posture and
logs one notice per process (never an error). Forked child context drops
legacy Operate contract messages, so a child of a restored pre-0.10.3
Operate conversation never inherits it.
Full Access copy (D4): ConfigChoiceDetailFullAccess now reads "Tools run
without asking first, and commands can reach anything on this computer."
in all 15 packs (14 from codewhale-app fdb7898d, zh-Hans new).
KbCyclePermissions / HomeAgentModeYoloTip name Plan on the dial;
KbCompleteCycleModes is renamed KbTabComplete.
Wire values unchanged: permission_posture ask / accept_edits / auto_review /
full_access, mode plan / agent (checked against
codewhale-ops/design/ADE-PLAN-20261109.md section 1.3 and section 5).
Checks: see the follow-up docs commit and the hand-off report for counts.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Decision 91 makes DSH 0.2.2-rc.1 the compatibility target and requires the
host uplift before any plugin receipt. Upstream tag dsh-v0.2.2-rc.1 resolves
to 66491b05e3f05ad35bcdd4f6cd7ed26041d9e3db locally and on GitHub.
Cordis 4.1.0, cosmokit 1.9.0, schemastery 3.19.0 and dsh-util-values
0.2.2-rc.1 are pinned exactly; lockfile integrities equal the npm registry
and the fetched tarballs. Loader, include and group 1.1.0 are rebased with a
three-way merge from the recorded rc.5 sources; every prior source/candidate
hash verified before the merge. Local adaptations kept: reviewed-closure
imports, the disabled native-internals probe, the erased FiberState values
(still a const enum in 4.1.0) and strict-mode casts. Two adaptations follow
upstream API changes: tree persistence moved from write() to
commit(change), so the reviewed tree's no-op moved with it, and service
providers moved from impl.fiber to impl.ctx.fiber in the leaked-service
audit. One new delta: bare specifiers keep the native import fallback
instead of upstream createResolve(), which writes a .cordis/resolve.mjs
helper into the package scope and imports it. Include's new journal.ts and
patch.ts are vendored with type-only casts; resolve.ts is not vendored.
The dsh-tools definition excerpt is re-taken from rc.1's split build: the
JSON-schema region and error tail are byte-identical (now in the shared
chunk) and the schema region differs by one doc line and an added pure
projectToolSchema helper, which is not exported. The six pure message
excerpts and deepFreeze are byte-identical at their new line numbers.
Hooks and persona sources are byte-identical; dsh-system-prompt's prompt
presentation split is recorded but not imported.
Injectable and refused services are unchanged; no new service is bridged.
The compatibility digest changes, so earlier Native review and trust must be
renewed; no live trust state was edited. The host stays experimental and off
by default.
Checks: typecheck and build pass. Node 413 pass, 7 skip; Bun 1.4.0 414 pass,
6 skip (420 each, matching the rc.5 receipt). Four deferLoading/when
definition probes match rc.5 on both runtimes. Rust crates are unchanged
here; plugin and extension-host Rust tests are recorded separately. No
hosted CI, packaged install, live plugin or provider call is claimed.
Receipts: artifacts/dsh-plugins-20261109/uplift/.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The plugin runtime root verifier required exactly one ACE, but the sandboxed
plugin host adds its own read/execute AppContainer (S-1-15-2-*) grant, so
every extension-host test failed on Windows. The new verifier still requires a
protected DACL, the current user as owner with exactly read+execute, and no
grant above read+execute; it tolerates only AppContainer SIDs besides the user.
save_agent_profile_document re-opened its write lock with the deny-write
bundle opener while holding a write handle, which is ERROR_SHARING_VIOLATION
on Windows; use the read-only fully-shared opener for the identity check.
exec_stream_drop_acceptance counted runtime handoff records, which are
user-role, as synthetic continuation turns; filter them the same way the
engine test already does. The stopship workflow fixture still expected the
scout to activate a deferred grep_files through tool_search; grep_files is
now on the surface directly.
Verified: mac nextest of both tests pass (tui_next_manual_turn... 52.9s).
`fd3d407bb1` dropped the `owner_work` re-export from `daemon_socket` but
left `daemon_windows.rs` importing it through that re-export, so the
Windows app-server library failed to compile (E0603 "function import
`owner_work` is private") and took the whole Windows Test job with it.
The later `process.start()` errors were that cascade: with the import
unresolved the closure's return type was `!`.
`daemon_windows.rs` now names `codewhale_runtime::runtime_control::owner_work`
directly, which is where `daemon_socket` imports it from too, so the
Windows path keeps its single source and no re-export comes back.
Checks: this is a Windows-only file (`#[cfg(windows)]` module), so a
local macOS check cannot cover it; the Windows Test job on this commit is
the verification.
`34ea11e0a3` ended a deleted conversation's terminal sessions by calling
`tools::terminal_session::kill_owner_sessions` from `delete_thread`, but
that module is `#[cfg(not(target_env = "ohos"))]` (there is no PTY
terminal surface there). The OpenHarmony check therefore failed with
E0433 on manager.rs:7372, so no OHOS build was possible on any commit
since.
The call is now behind the same gate as the module it names. Behavior on
every other target is unchanged; on OpenHarmony a deleted thread has no
terminal sessions to end.
Checks: cargo check -p codewhale-engine --all-targets --locked exit 0 on
macOS; the OpenHarmony job needs the SDK, so its verification is the CI
run on this commit.
`scripts/sync-changelog.sh` keeps crates/tui/CHANGELOG.md in step with the
root changelog; the 0.10.3 Added entries for the DSH market parser and the
deleted-conversation jobs fix had not been mirrored, so the landing
preflight's TUI changelog slice check failed on 80b3c62930.
Checks: `scripts/sync-changelog.sh` clean after this commit (no diff on
re-run).
The file-revert trust review (SECREVIEW-0a626fdab8, L3) found that the
route test covered only the 400 and 409 shape cases: nothing at route
level proved that a default-posture thread restores a file it owns, or
that a second thread on the same workspace naming that receipt is
refused with the working tree unchanged.
This adds that pair. One thread records a `pre-turn:` restore point on
its own turn and, still in the default (untrusted) posture, restores the
file through POST /v1/threads/{id}/file-revert: 200, `modified`, bytes
back to the pre-turn content. A second thread on the same workspace
posts the same snapshot id and hash: 409, and the file is untouched. The
trust mode is not involved either way, which is the point — the route
only writes one regular file inside the thread's own workspace.
Checks: hermetic app-server file_revert_route filter 3 passed, 0 failed
(incl. the new route test); clippy -D warnings clean on
codewhale-app-server --all-targets; rustfmt applied.
Adds a fifth marketplace parser for the DSH plugin market index, format 1
(reference/dsh packages/util/plugin-market-index, the format's own
executable rule set). Detection uses the format's discriminator, a
top-level numeric `format`; version 1 is the only one defined, and any
other value is reported by the parser instead of being called ambiguous.
Parsed as documented: npm-valid `name` is the entry identity, `title`
and `description` are a string or a language-keyed object whose `en`
member is authoritative (a missing `en` falls back with a warning),
`icon` resolves against the index URL, and entry metadata keeps keywords
plus category and label ids only when the index declares them. Versions
sort by SemVer precedence and the highest one decides the row.
Install plans stay truthful. npm sources are listed as unsupported
because Codewhale does not execute npm. A commit-pinned GitHub source
without a package subdirectory installs from the pinned commit; a
subdirectory or non-GitHub host stays unsupported with the reason. A
version whose `engines.dsh` range admits none of the host's DSH versions
(0.2.1-rc.5 / 0.2.2-rc.1) is refused before any download, validated by
the same npm-range parser the installer uses so the catalog and the
installer cannot disagree.
Checks: hermetic codewhale-plugins marketplace filter 41 passed, 0 failed
(5 new DSH tests); clippy -D warnings clean on codewhale-plugins
--all-targets; cargo check -p codewhale-app-server -p codewhale-tui
--all-targets exit 0; rustfmt applied.
Deleting a Runtime thread ended its terminal sessions but left the
thread's background shells running: `delete_thread` dropped the thread's
`ShellManager` without killing anything, so a `persist:true` or plain
background job outlived the record and its output went nowhere. The
thread's shell manager is now taken out of the active map and every job
it still owns and that is still running is ended before the record is
dropped. Released jobs stay untouched (their ownership moved to the
user) and already-finished jobs are not recounted, so a second call is a
no-op.
Checks: hermetic engine lib test kill_all_owned_running 1 passed;
hermetic engine cleanup tests 24 passed earlier on the same tree;
rustfmt applied.
When sub-agent cleanup could not reach its durable state because
the path does not exist or is a symlink loop, the session sent a
"Failed to clean up sub-agents" transient error every cleanup cycle
and skipped the agent-list refresh. Cleanup now classifies those
two cases: the session warns once, naming the state path, serves
the list from memory, and logs later skips. A state directory that
exists but is invalid (linked, wrong owner, not a directory) still
refuses exactly as before. The credentials-directory open error
now says which case it hit ("does not exist" / "is a symlink loop")
instead of only "without following links"; the loop test is a raw
errno check shared by both sites because ErrorKind::FilesystemLoop
is still unstable.
Checks: dev-cargo check and clippy -D warnings clean on codewhale-config + codewhale-engine (all targets, locked); hermetic config private_directory tests 19 passed; hermetic engine cleanup tests 24 passed.
`codewhale resume` from a pipe or cron job refused to start with the
interactive-terminal error, even though `exec --resume` could have
done the work. Now, when stdin or stdout is not a terminal, resume
is rewritten to the equivalent exec invocation before dispatch
(--last and a bare resume map to --continue; an explicit id maps to
--resume; trailing prompt words keep their exec meaning, and a
piped stdin stays the prompt source exec already defines). Fork
runs its normal mounted-session preparation first, then continues
the new session through exec the same way. An --operation-key
recovery keeps the exact interactive intent and is never rewritten.
In a real terminal nothing changes.
Checks: dev-cargo check and clippy -D warnings clean on
codewhale-tui (all targets, locked). Behavioral smoke on the built
binary with a hermetic home: `resume --last` and `resume <id>`
piped reach exec's session errors instead of the TTY refusal;
`fork --last` piped reaches fork's session resolution; a plain
no-TTY launch still refuses with the terminal message.
The engine's only palette use was a test-only chrome_ink() returning
a constant; delete it and its test, and drop codewhale-palette from
the engine's dependencies (it remains reachable in dev-dependencies
through other crates). App Server callers now import owner_work from
codewhale_runtime::runtime_control directly; daemon_socket keeps a
private use instead of re-exporting it. No behavior change.
Patch carried over from the 2026-11-09 engine lane handoff
(artifacts/handoff-20261109/grok/E2-1-uncommitted.patch), applied
and verified here.
Checks: dev-cargo check and clippy -D warnings clean on engine + app-server (all targets, locked); Cargo.lock loses only the codewhale-palette edge; cargo tree confirms palette remains only under dev-dependencies.
rmcp 3.8.0 buffers Streamable HTTP response bodies without a bound;
3.9.0 is the fixed release. The lock delta is rmcp alone (plus a
sha2 edge); the windows-sys and getrandom lines are dependency
edges re-pointing among copies already locked, reviewed line by
line. Manifest floors rise to 3.9.0 in engine and app-server so a
future lock regeneration cannot fall back into the advisory range.
Checks: cargo audit no longer reports RUSTSEC-2026-0462;
cargo deny check advisories ok; dev-cargo check on engine and
app-server (all targets, locked) clean.
The lean harness (§94) shrank the prompt core and tool surfaces, so
every representative-stage identity moved and the Lint budget gate
was red on identity alone. Re-measured provider-free through
scripts/dev-cargo.sh and locked with --update --allow-increase:
base stage 9063 -> 5749 bytes, total 11707 -> 7181, ceilings down
with the smaller surfaces. A plain check now passes: all 55 metrics
exactly at budget. Attribution recorded in the budget history note.
The companion server's Bearer and cookie checks used plain byte
equality. Swap both for a length-folded XOR compare with no early
exit. The rest of the surface already holds: loopback-only bind,
Host and Origin pinning, 0700 owner-checked directory, HttpOnly
SameSite=Strict cookie, CSP headers, presentation-only routes.
Adds GET /v1/apps/plugins/catalog plus enable/disable and settings
routes. Built-in rows are Subagent, Web search, Shell and the
always-on Agent loop; experimental rows are the experimental-stage
features; the remaining features ride in More. Toggles persist the
[features] leaf through the existing locked config writer and apply
through the same reload path as POST /v1/config/reload, so the
desktop Plugins page now reads live Engine state instead of a
client-side fallback. Disabling the agent loop refuses with 409;
Extension host is marked restart-required because toggling it
re-reviews every plugin. The one editable setting is the subagents
concurrency cap, validated against MAX_SUBAGENTS.
One HTTP contract test covers the row shape, a persisted toggle,
the agent-loop refusal, unknown-id 404s and the settings write, and
proves the static catalog segment does not shadow {selector}.
Checks: hermetic app-server plugin filter 7 passed, 0 failed (incl. the new catalog contract test); clippy -D warnings clean; rustfmt applied.
* feat(http): 允许宿主向实际模型客户端注入内存传输约束
Signed-off-by: zhuowp <[email protected]>
* fix(transport): preserve vision HTTP policy error causes
Keep the original anyhow construction error and include its full cause chain
in the vision ToolError. Clarify runtime policy inheritance without changing
provider credential inheritance.
Add one public-API integration test for configured-base refusal and builder
mutation through primary and vision transports, the HTTP/1.1 client, and
successful requests without a policy.
Validation on reviewed tree 046d0f4acf9142d03e6abaa411c63b1bd39810d9:
- Focused integration target: 1 passed; 0 failed.
- Production diagnostic-erasure control: 0 passed; 1 failed at the vision
cause assertion, as expected; exact source restored and rerun: 1 passed;
0 failed.
- cargo fmt --all -- --check and source/index diff checks passed.
- Rebased runtime/engine locked library check passed; final delta adds only
test coverage and corrects existing documentation beyond that production
check.
Signed-off-by: zhuowp <[email protected]>
* style(engine): format project instruction recording
Apply rustfmt wrapping to the if-let expression in
record_current_project_instructions. The exact CI merge commit
2c130984f2acf9b1dd35a98e6e1275c11fcd91df reproduces the formatting failure.
This changes only whitespace, without changing calls or control flow.
Validation on candidate tree 6b987fe029:
- cargo fmt --all -- --check passed.
- Focused model_http_policy integration target: 1 passed; 0 failed.
- Source and staged diff checks passed.
Signed-off-by: zhuowp <[email protected]>
---------
Signed-off-by: zhuowp <[email protected]>
Co-authored-by: zhuowp <[email protected]>
Co-authored-by: Hunter Bown <[email protected]>
Tarball staging now detects a dsh.bundle package.json, converts the
bundle during staging, and validates the converted result. Bare-module
resolution admits CommonJS packages (type commonjs or unset, require
exports) next to ESM and refuses native .node addons. Host admission
parses engines.dsh as an npm semver range (caret, tilde, comparators,
x-ranges, unions) matched against the supported DSH versions, and
refuses bundles whose dsh.bundle.platforms excludes this computer,
carrying the mapped OS list into the converted manifest.
Checks: codewhale-plugins hermetic tests 202 passed, 0 failed,
1 ignored; clippy -D warnings clean; rustfmt applied to the four
touched files. Fixed a borrow error (admission result now owns the
declared range) and dropped an unused range helper from the
handed-over slice.
Ambient fish, bubbles and whale cameo render only in the empty state.
Streaming reasoning shows a single Thinking… header; hidden reasoning
and the window title say thinking. Tool hint reads ⌥V details, dimmed.
The instruction-history record is now a user-role runtime event, so
last_user_text skips <codewhale:runtime_event records and returns the
person's own message. The base-prompt pin follows the slim core text
from 2282c33f0c ("an agent with real tools and a real workspace").
Verification: scripts/dev-test.sh crates/tui/tests/integration/exec_turn_usage.rs
exec_turn_usage (hermetic, nextest): 11/11 passed.
Workspace and configured instructions now arrive as a history record
rather than in the system prompt, and the eager tool surface is
leaner. Existing pins that still asserted the old shape are updated:
- acp_server core prompt: the instruction marker is absent from the
system prompt and present in the request messages.
- app_system_prompt_includes_configured_instructions: the configured
marker and its <instructions> tag are no longer in the system prompt.
- /context diagnostics fixtures and their Windows variants: constitution
2414 -> 1474 tokens, total 2818 -> 1878.
- TUI conformance prompt/event goldens re-recorded
(CODEWHALE_CONFORMANCE_UPDATE=1, nextest); only tool descriptions,
tools_sha256, catalog and "frozen:" hashes move.
Verification (hermetic scripts/dev-test.sh, nextest):
- tui conformance:: + debug_diagnostics +
app_system_prompt_includes_configured_instructions: 121/121 passed.
- app-server core_prompt_is_stable_after_instruction_write...: 1/1 passed.
Note: libtest runs of these goldens in one process produce different
catalog hashes than nextest's process-per-test; CI uses nextest.
f7dd49b took the worker's stdin/stdout through a binding that is only
`mut` on unix, so the Windows build failed with E0596 at
fleet/host.rs:345 (CI run 42612454217, Test windows-latest and npm
wrapper smoke windows-latest). This unbreaks the Windows build from
f7dd49b; the unix path is unchanged.
Unbreaks CI from f7dd49b275: every Rust job (Lint, Test x3, MSRV,
deny, audit, OHOS, smoke) failed on clippy -D warnings, unused import
`PathBuf` at crates/engine/src/fleet/acp.rs:15. PathBuf is used only by
the test fixture helper, so the import moves into the tests module.
Verified: scripts/dev-cargo.sh clippy -p codewhale-engine --all-targets
--all-features --locked -- -D warnings -> exit 0 (macOS).
Credential-rejection retraction of the unanswered question had stopped
working: turn preparation now records runtime messages (workspace
instructions, constitution, MCP rosters) after the user message, so the
"nothing after the question" check never held. The mark is now
UnansweredUserMessage { index, content_sha256 } (core/turn.rs); a
retraction requires the same message at the same index with the same
content hash, and only internal runtime-handoff messages after it, which
go with the question. project_instructions is now an internal runtime
handoff kind. The mark is no longer Copy (it carries a String); callers
borrow or clone it.
Existing tests re-pinned to the lean surface and history delivery:
eager-set pins, the hooks category table, shell guidance phrases, the
representative runtime-context fixture (instructions and skills absent
from every system stage), the skill-discovery baseline (no discovery at
prompt assembly), repin, edit-last-turn, interactive counts, preflight
(+1 workspace-instructions record), SSE replay count, the scout surface
and the stopship workflow brief (grep_files eager), and the MCP
no-servers test (disabling MCP after a roster withdraws it once).
Verified on macOS, hermetic HOME (scripts/dev-test.sh engine):
- targeted lean batch incl. credential_rejection, an_answered_question,
a_rewritten_session, edit_last_turn, preflight_guard,
representative_runtime, sse_turn_recovery, fresh_forked,
no_configured_mcp_servers, preview::: 237 passed, 2 failed before the
preflight re-pin (host-only record) ; lazy_boot_* passes alone.
- full engine lib: 7095 passed, 13 failed (wait timeouts under a
concurrent TUI build; re-run recorded on CW-7923), 13 ignored.
The earlier engine-full-c5 failures for edit_last_turn, fresh_forked and
preflight came from this Mac's real ~/.codewhale and ~/.agents/skills
leaking into a non-hermetic run, not from the product.
Not claimed: hosted CI verdict.
Lean-harness Commit 5. Lines are now the only model-facing paging
unit for the lowercase read primitive:
- limit defaults to and is capped at 2000 lines; a larger limit
errors with the cap named.
- The 50*1024 output cap is internal: lines are counted as UTF-8
bytes plus one joiner, and the first line that would cross the
cap stops the window. The operator [workshop]
read_result_max_bytes override is the only widening layer.
- A line over 2000 chars is truncated with a marker suffix,
replacing the first-line-too-large bash fallback.
- max_bytes is removed from the schema and refused as an unknown
parameter (breaking). Footers speak only in lines:
"[Showing lines A-B of N; output cap reached. Continue with
offset=B+1.]" / "[Showing lines A-B of N. Continue with
offset=B+1.]".
Kept from the prior contract: raw unnumbered text, exact whole-file
output when nothing truncates, size/line_count/truncated metadata,
read_budget_bytes for the compactor, and the hidden compatibility
reader's separate contract.
Schema delta verified against the conformance tool goldens: read
loses exactly the max_bytes property, nothing else changes shape.
Conformance goldens regenerated; both CHANGELOGs updated here.
Verified on macOS (Apple Silicon), hermetic HOME via scripts/dev-test.sh,
working tree = this commit plus the follow-on commit after it:
- scripts/dev-test.sh engine -- <lean batch> tools::file:: preview:: ...
237 passed, 2 failed; tools::file:: 159/159 passed. The two reds:
a preflight pin carried a host-only instruction-notice record (fixed in
the follow-on commit) and lazy_boot_leaves_unselected_servers_unspawned,
which passes when run alone (libtest in-process interference).
- scripts/dev-test.sh engine (full lib): 7095 passed, 13 failed,
13 ignored. All 13 are approval / auto-review / runtime-thread /
sub-agent wait timeouts (Elapsed) that ran while a TUI test build
held the CPU; none touch read or file_tool. tools::file:: 159 and
tools::file_tool:: 21 passed in that run.
Not claimed: this commit built alone, hosted CI verdict.
The fleet host already supervises a program with process groups, a
parent-death pipe, stop/restart, memory and logs. It wired stdio for logging,
so an agent that speaks the Agent Client Protocol had no channel. This adds a
transport to the worker start request:
- `FleetWorkerTransport::Logs` is today's behaviour, unchanged.
- `FleetWorkerTransport::JsonRpc` gives the child piped stdin/stdout and hands
the caller a `FleetWorkerIo` exactly once. Parent death still works: the
adapter holds the write end of stdin, so its death reaches the child as EOF.
The SSH adapter refuses this transport rather than pretending to support it.
- stderr goes to the worker log in both shapes.
`fleet::acp` speaks the protocol over that channel: newline-framed JSON-RPC,
initialize, session/new, session/prompt, session/cancel, and
session/request_permission answered by policy (allow once, reject once,
cancel). Filesystem and terminal client methods are advertised false, no
credential is read, and no update is mapped onto a journal item yet.
Verified with a dependency-free fixture agent through the real host, under the
repo's own test environment (RUST_MIN_STACK=16 MiB, which every fleet test
needs): streamed text, an edit tool call writes a file only when allowed and
nothing when rejected, an unlisted environment variable never reaches the
child, a cancel settles a prompt that never answers, and a Logs worker still
writes its log and has no duplex io. 3 tests, all passing.
Adds the CW-7922/7924/7925/7926 entries owed from their commits (slim
prompt core, instructions as a conversation message, eager grep_files,
MCP capture fix) and lands the peer's pending wording update for
conversation deletion ending background jobs. Both mirrored changelogs.
Lean-harness Commit 4: grep_files joins the eager default set;
workflow, context_archive_get and context_archive_search load via
tool_search instead. Compaction guidance names tool_search as the way
to reach the archive tools. Tool descriptions trimmed to first-relevant
detail across codemode, goal, subagent, shell guidance and file tools;
no schema shape, required field or enum changed (verified by a
description-stripped diff of the conformance tool goldens: only
grep_files added, context_archive_* removed from the eager surface).
Also mirrors the CW-7925 project-instructions history message in the
request-preview planner (production records it after the user message
and before the instruction notice), so preview hashes match the wire
body again; the shared planned_project_instructions_message helper now
backs both paths.
Conformance goldens regenerated (prompt + events); the events goldens
also pick up the CW-7925 instructions message.
Verified: scripts/dev-cargo.sh test -p codewhale-engine --lib --locked
-- tool_catalog codemode goal agent_tool_description shell_guidance
preview small_surface -> 230 passed, 0 failed, 2 ignored.
CODEWHALE_CONFORMANCE_UPDATE=1 test -p codewhale-tui --lib -- conformance
-> 33 passed.
CHANGELOG (owed to peer-edited files): Engine: grep_files is eager by
default; workflow and context_archive tools load on demand via
tool_search; tool descriptions shortened.
Commit 3 of the lean-harness plan
(CW/artifacts/thinking-wire-ab-20261108/HANDOVER-PLAN.md), decision
recorded as CURRENT_DECISIONS section 94 in codewhale-ops.
Discovered workspace instructions (AGENTS.md, CLAUDE.md, overlays,
existing discovery and budget rules unchanged) are no longer spliced
into the pinned system prompt. The engine records the same rendered
assembly block as one user-role runtime message whose body is a
<system-reminder> ("may be relevant... do not override system,
developer, or direct user instructions. Instructions from: <source>"),
capped at 65,536 bytes with an explicit truncation note, delivered
once per baseline and re-delivered when the rendered block changes.
Mid-session instruction edits therefore arrive as history and cannot
bust the prompt prefix. The prompt core gains the pointer sentence.
observe_context_update now returns the freshly composed assembly and
refresh_pinned_header_for_turn installs it, so the recorder always
sees the current block; notice rendering still compares against the
previously installed assembly first. prompt_range is never set now:
the static-prefix guard measures the smaller prompt and its
instruction-trimming lever simply never engages (scope budgets still
trim at assembly time).
Tests updated to the new contract: instructions absent from system
prompts, AGENTS.md edit arrives as a second history snapshot with no
duplicate on re-record, set_model reload proven via the event's
instruction assembly, registry guidance absent from the prompt.
Conformance prompt goldens regenerated on macOS and reviewed.
CHANGELOG line (both files peer-owned mid-edit; add under Unreleased):
"Workspace AGENTS.md instructions now arrive as a conversation
message instead of inside the system prompt, so editing them
mid-session no longer resets the prompt cache."
Verified: farm 20261109T084650Z-lean-c3f-566e61 (base 2282c33f):
cargo test -p codewhale-engine --lib -- prompts:: project_context
instruction: 246 passed, 0 failed. One selected test
(saved_profile_discovery_and_actual_start_share_current_instructions_
route_and_trust) stack-overflows identically on the unmodified base
(farm 20261109T084153Z-lean-c3base) and was excluded from this check;
it is not a C3 regression. Local macOS conformance::prompt
regeneration passed 1/1. Not claimed: hosted CI verdict or live
session behavior.
Commit 2 of the lean-harness plan
(CW/artifacts/thinking-wire-ab-20261108/HANDOVER-PLAN.md).
The eager prompt stacked five voices on the same rules. BASE_PROMPT,
LANGUAGE_PROMPT and OUTPUT_PROMPT are now one slim core (4,293 chars
for the three static layers, down from ~7,600; the plan's 3,800 target
assumed merged segments, while the split-layer guards require separate
Language/Output segments): identity, ground truth, scope and
authorization, truthful completion, a short Tools paragraph, mechanism
guarantees, and ### Whose word wins verbatim. Kernel guard phrases are
preserved exactly. The separate Concise Output Discipline block is
deleted; conciseness is one line of Output Formatting for everyone.
The ## Skills index no longer renders into the system prompt. Skills
are discovered through load_skill (query search or name="list"), whose
description now says so; /skills and other catalogue surfaces are
unchanged. The ## MCP Registry first-turn prompt injection is removed;
its guidance (fallback not first step, refine once, schema-retry) now
lives in the registry_sync description, and tool_search names the
Registry pair.
Tests encoding the old blocks updated in this commit (verbosity,
skills-merge, registry-in-prompt). TUI conformance prompt goldens
regenerated on macOS with CODEWHALE_CONFORMANCE_UPDATE=1 and reviewed:
fixture system prompts are now 5,814-6,189 chars with no Bearing,
Skills, Concise, or MCP Registry sections.
CHANGELOG line (both files currently peer-owned mid-edit; add under
Unreleased): "The system prompt is substantially smaller: one slim
core, skills discovered through load_skill search instead of a prompt
index, and MCP Registry guidance moved into the registry tools."
Verified: farm run 20261109T081538Z-lean-c2c-0d35c8 (base 5c9edf6,
these paths): cargo test -p codewhale-engine --lib -- prompts::
registry_guidance_stays_out: 114 passed, 0 failed. Local macOS
conformance::prompt regeneration exit 0. Prior run lean-c2b failed
5 guards on phrase wrapping; fixed by rewrap/punctuation only.
Not claimed: hosted CI verdict on the landed SHA, live-session
behavior, or prompt size measured through exec receipt (plan
validation runs after Commit 4).
Agent-owned work now lands on main from the agent's own short-lived worktree,
cut from origin/main and removed after the push, instead of every agent sharing
one checkout. A bug fix, or a new contract between components, may add the one
test that would have caught it; there is still no TDD and no coverage for its
own sake. AGENTS.md and the cw-land, cw-orient and cw-slice skills say the
same thing.
Docs only; no build or test run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
reqwest timeout errors carry no elapsed time, so from_reqwest used to
hard-code Duration::ZERO and failed turns surfaced "Request timed out
after 0ns" — it reads like a zero-length budget and hides the real
failure (seen on a production clip order whose render turn died mid-
stream). The retry loops now measure the actual elapsed time per
attempt, and where the duration stays unknown the message says so
plainly instead of printing a fabricated 0ns.
Submitting clears the composer and dispatch runs in the background, so the
composer is usable while a message_submit hook or the engine is still working.
When the send then failed, the message was written over whatever had been typed
since, with nothing to get it back: not in history search, not in the undo
buffer.
Stash a non-empty draft before the message comes back, on the three paths that
write the composer. An empty composer keeps its undo buffer as it was.
Signed-off-by: Lstarsky0 <[email protected]>
* fix(engine): bound nested brace expansion and repeated globstars
Two follow-ups from the #7215 review. The 64-combination cap counted each
brace group up to its first }, so a nested group hid its commas:
{{a},c0,...,c9} ten times counted as 1 and expanded to 11^10 patterns per
file. Expansion now counts the patterns it produces and stops at 64, leaving
the pattern literal as before.
A run of **/ segments multiplied the tail search, so ten of them against a
30-deep path didn't finish. Runs collapse to one **/ before matching, which
doesn't change what they match.
The regression test was asked for in that review. With main's matcher it ran
past 120 s and was killed; with this change it takes 0.024 s.
Signed-off-by: Lstarsky0 <[email protected]>
* fix(engine): collapse only leading globstar runs and cap brace groups
Collapsing **/**/ everywhere changed what some patterns meant. src/**/**/src
became src/**/src, whose single-** branch tests the suffix against the whole
path, so it matched src and src/foo.rs and an exclude pruned all of src. And
src*/**/**/file.rs became src*/**/file.rs, where that branch reads src* as a
literal prefix, so it stopped matching src1/a/b/file.rs. Collapse only a run
that starts the pattern being matched; a run after a plain directory reaches
that position once the directory is consumed, and one after a wildcard keeps
the path it took before.
Expansion also descended into every first alternative before counting a
leaf, one stack frame per level, so 8000 nested groups were reported to
overflow a 2 MiB thread. The tests run with a 16 MiB stack, where that case
didn't overflow but took about a second. More than 64 { now leaves the pattern
literal before expanding: past that many groups with commas the 64
combinations are already gone.
The regression test gains those cases. With the previous commit's matcher it
fails on src/**/**/src; with this one it passes in 0.024 s.
Signed-off-by: Lstarsky0 <[email protected]>
---------
Signed-off-by: Lstarsky0 <[email protected]>
record_mcp_runtime_context returned the same Default for a failed
capture (pool try_lock contention, changed MCP config sources, or a
spawn_blocking join failure) as for a successful capture of zero
servers. A transient failure therefore withdrew the MCP server notes
and the next successful capture re-sent the full payload, oscillating
in history despite the last-payload guard.
The capture is now typed: failure returns None and the function emits
nothing, leaving prior notes and the tool catalog untouched (including
the composition entry). Withdrawals happen only on successful empty
captures. The no-pool / ineligible-surface arm keeps its existing
successful-empty semantics.
One consequence: deleting an MCP config file now withdraws its note on
the first successful capture after the pool reloads, not in the same
turn. The two test assertions encoding the old behavior (held pool
lock withdraws; removed config file withdraws immediately) now assert
that a failed capture changes nothing.
Commit 1 of the lean-harness plan
(CW/artifacts/thinking-wire-ab-20261108/HANDOVER-PLAN.md).
Verified: Boat farm run 20261109T070952Z-lean-c1-e87488 (Linux, base
13858fa8d0 plus only these two paths; no drift in either file since
that base) — cargo test --locked -p codewhale-engine --lib with the
three record_mcp_runtime_context tests selected: 3 passed, 0 failed,
7114 filtered out. Linux farm evidence only; no macOS build, hosted CI
verdict, or live MCP session claimed. Whether a slow lock holder
contributes to contention was not investigated.
A GitHub prerelease such as v0.10.3-beta.2 can now be prepared and tagged,
publishing a GitHub prerelease and npm `next` while leaving the plain X.Y.Z
path unchanged.
- prepare-release, check-versions, verify-remote-tag, the npm tag-checkout
gate, ensure-release-assets-absent and auto-tag accept X.Y.Z-prerelease.
They still reject build metadata, a missing patch, and v-prefixed values
where the convention is bare.
- prepare-release also normalizes internal pins in the workspace-member
test harnesses (tests/portable-*) when either side is a prerelease: Cargo
requires an explicit prerelease requirement, so their stale caret pins
could not resolve a prerelease workspace. Both those manifests and their
normalization are added to the restore transaction.
- release.yml resolves a `prerelease` output. A prerelease tag skips
docker-build, docker, CNB, GitCode, Homebrew and the web release record,
creates the GitHub release with prerelease=true, and publishes npm with
--tag next. There is no crates.io job, so a prerelease publishes no crates.
Stable tags take every existing path with unchanged inputs.
- release-artifacts skips AUR metadata for prerelease versions (AUR pkgver
cannot carry a prerelease hyphen).
- docs/RELEASE_CHECKLIST.md documents the checkpoint flow, including the
workspace restore afterwards.
Verified with a git-archive fixture of 13858fa8d0 plus these scripts
(receipts in CW/artifacts/release-beta-tooling-20261109/):
- prepare-release.sh 0.10.3-beta.2 then check-versions.sh
--require-dated-release: Version state OK workspace/npm/npm-binary/
lockfile at 0.10.3-beta.2 (66 script-level checks, 0 failures;
verify-beta-tooling.log).
- prepare-release.sh 0.10.3 restores the tree; the only residual tracked
diff is the three tests/portable-* pin files, now tracking 0.10.3.
- release.yml resolve step executed against real git tags for push and
dispatch paths: prerelease=true for v0.10.3-beta.2, prerelease=false for
v0.10.3, and invalid versions rejected.
- actionlint clean on the three changed workflows; bash -n/node --check
clean; existing prepare-release, verify-remote-tag,
require-release-tag-checkout, generate-release-body and
ensure-release-assets-absent tests pass.
Not proven here: no live GitHub prerelease, npm publish, or hosted
release.yml run. The fixture has no .git, so check-versions checks 7/12
were skipped there (they pass on canonical before 34ea11e0a3).
The Runtime thread delete path now kills and deregisters every live PTY
owned by that thread and marks its durable record cancelled. The desktop
tab-close half and the jobs half remain open.
Local: cargo check -p codewhale-engine --all-targets (9m06s, exit 0).
- FleetLedger caches only the durable length, not an open ledger handle,
so a live appender no longer blocks compaction's replace on Windows.
- hash_session_record treats a Windows path-not-found under a
non-directory parent as NotADirectory, matching Unix, so a blocked
checkpoint inspection cannot silently clear save health.
- The focused Windows workflow now carries RUST_BACKTRACE for Phase B.
Local: persistence_actor 1/1, ledger compaction 1/1, fleet_setup 1/1;
scripts/preflight.sh --check OK.
The focused job now runs only Rust tests and needs neither runtime. Removing the setup actions avoids the current GitHub action-download 504s; the full ci.yml still installs both for the release suite. actionlint clean.
runtime_store_binding_survives_launch_snapshot_and_resume drives its phases on a deliberately pinned 2 MiB thread. The same frames overflow that budget on Windows (0xc00000fd) while passing on macOS. Keep 2 MiB on non-Windows and pin 4 MiB on Windows, still independent of CI's RUST_MIN_STACK. cargo check -p codewhale-runtime --tests passes.
The first focused run failed before checkout completed: mozilla-actions/sccache-action could not resolve its executable and hit a GitHub 504. Remove sccache and rust-cache from this diagnostic job; the focused job now uses the standard Rust toolchain, nextest, Node and Bun and pays a cold compile instead of depending on the cache service.
The fixture total was 2818 for the 21-token Linux environment, but context_fixture_for_host still hard-coded an older 2207 base and never adjusted the total. Windows/pwsh contributes 22 tokens, so its actual total is 2819 and the fixture remained 2818. Parse the fixture's own text/JSON total and add the host delta; macOS stays 2818, Windows becomes 2819, and the sum is still checked against the individual entries.
cargo nextest -p codewhale-tui --lib -E 'test(context_routing...) | test(normalisers...)' -> 2 passed, 5717 skipped.
The full Windows suite takes about two hours on a cold hosted runner. This workflow_dispatch job checks out the exact expected SHA and runs a caller-supplied nextest expression across the engine, plugins, tui and app-server lib tests, with the CI profile and stack floor. It is diagnostic only; the full ci.yml remains the release gate. actionlint clean.
The Windows log now names the inner verifier error: Codewhale-owned xAI OAuth DACL must grant only one user. The stage root's read/execute ACE carried CONTAINER_INHERIT_ACE and OBJECT_INHERIT_ACE; on a directory Windows stores that generic-rights ACE as more than one explicit ACE, which the protected owner-only verifier refuses. The finalized root uses NO_INHERITANCE because harden_staged_tree_contents has already given every descendant its own explicit read/execute ACL; the temporary staging root and plugin state directories keep inheriting ACEs.
cargo fmt --check and cargo check -p codewhale-plugins --locked pass. A Windows-target check reached ring's C build and failed for the missing Windows SDK assert.h, not this code. The Windows suite remains red until this exact SHA runs.
verify_finalized_stage_root discarded the inner Windows owner-only/read-execute verification error, so every failing extension-host, plugin-MCP and skills test reported only 'Plugin runtime root has not been finalized read-only'. It now appends the real error chain. No product acceptance changes; the Windows CI log is the diagnostic this change is for.
engine owner_origin_tests now drives a bounded write_file claim, which still goes through exact owner re-admission after a2fda9d570 made unbounded shell runs skip the peer/origin branch. TUI exec_turn_usage now passes --no-tools to the one-shot runs and expects piped stdin appended to an argv prompt. Passive: no product behavior changed.
owner_crash_reentry read the receipt from CODEWHALE_HOME/run even when resolve_socket_path shortened an implicit endpoint past macOS's 103-byte limit. It now resolves the same path the child uses, so it covers the shortened form instead of failing on it. Reproduced first with a long TMPDIR: FAIL 0 passed, 1 failed at line 74. After the fix: PASS 1 passed under the same long TMPDIR and PASS 1 passed with the default TMPDIR.
Checks: scripts/dev-test.sh engine owner_origin_tests -> 1 test run: 1 passed, 7114 skipped. cargo nextest -p codewhale-tui --test integration exec_turn_usage -> 11 tests run: 11 passed, 108 skipped. scripts/preflight.sh --check: OK.
Records that the source candidate is not the final tag; App Server v2, the coarse split and DSH all-33 on 0.2.2-rc.1 stay in 0.10.3; beta.2 is the near-term prerelease. Marks the Deepin incident (7095), the original ZAI freeze (6184) and the open CW-6943/CW-6945/CW-6950 rows as moved to 0.10.4; CW-6944 is not moved. States the terminal-lifetime, Windows-suite, App Server Tier A/B and Tier C, DSH, ChatGPT, Windows ACP/undo and install-route work items without claiming them fixed.
Regenerated crates/tui/CHANGELOG.md with scripts/sync-changelog.sh. scripts/preflight.sh --check: OK.
The HTTP runtime marked its tasks answerable but still applied the 30-minute
durable-task human-wait cap, so a question left open while the person was away
was cancelled with "Request cancelled while awaiting user input". Answerable
tasks now keep an unbounded human-wait cap; unanswerable and headless tasks
keep the fail-safe cap.
Verification: cargo test -p codewhale-app-server --lib
runtime_api::tests::server_task_config_opts_tasks_into_answerable_human_waits
1 passed;
runtime_api::tests::server_task_runtime_wires_answerable_human_waits_into_the_executor
1 passed. scripts/preflight.sh --check OK.
The write-claim ledger still coordinates bounded File/Edit/Patch targets, but a
child's shell no longer waits on a peer's claim: the execution envelope,
sandbox and approval posture already govern executable work. The old refusal
told analysis agents to re-run with read_only=true, which burned steps without
protecting a peer's files. The contention refusal now names only bounded write
tools, and Run verifiers remain guarded.
Repairs three stale candidate assertions:
- the app-server settings test expected the retired `operate` spelling to
persist; it now asserts the documented Work (`agent`) normalization.
- the mailbox test expected snapshot.mode == "operate"; it now expects the
Work wire value.
- the parent prompt+catalog ceiling re-measured at 94,121B (was 92,100B) after
the deliberate tool-text expansion; the read-only child envelope still fits.
The shell tests that execute a command now run on the engine's large stack so
they exercise the policy instead of overflowing the test thread.
Verification: cargo test -p codewhale-engine --lib, exact filters:
child_write_tool_fails_closed_outside_registered_scope 1 passed;
lone_shared_writer_keeps_unbounded_shell 1 passed;
contended_shared_writers_keep_proven_readonly_shell 1 passed;
contended_shared_writer_keeps_its_shell 1 passed;
parent_agent_surface_stays_within_measured_ceiling 1 passed;
read_only_child_envelope_stays_within_measured_ceiling 1 passed;
full_mailbox_posture_update_supersedes_queued_change_mode 1 passed.
cargo test -p codewhale-app-server --lib
runtime_api::tests::runtime_tui_settings_normalize_legacy_modes_and_do_not_save_env_overlays
1 passed. scripts/preflight.sh --check OK.
codewhale-util's tests used codewhale_runtime::test_support::{EnvVarGuard,
lock_test_env}, but codewhale-runtime depends on codewhale-util. A versioned
dev-dependency made cargo publish resolve runtime 0.10.3 before util; a
path-only one tripped cargo-deny's wildcard rule. EnvVarGuard, its guarded-key
registry and the state-from-environment flag move down to util (runtime
re-exports the same names), and the two private-log tests move to
codewhale-runtime::private_log, their owner.
util also declares reqwest's stream feature directly: response_body.rs calls
bytes_stream(), which only compiled because runtime happened to enable the
feature for the workspace. The published crate would have failed to build.
Verification: cargo test -p codewhale-util --lib (49 passed);
cargo test -p codewhale-runtime --lib private_log (2 passed);
cargo test -p codewhale-runtime --lib test_support (3 passed);
cargo deny check bans (ok); clippy on both crates;
RUSTDOCFLAGS=-Dwarnings cargo doc -p codewhale-util -p codewhale-runtime;
publish-crates.sh dry-run packages util and stops only at the unpublished
codewhale-ratatui 0.2.0 prerequisite.
The website changelog derivation requires an Unreleased lane first and the
dated candidate immediately behind it (web/lib/changelog.test.ts). Restores
the empty lane used by 0.10.2 and regenerates the TUI slice.
Verification: scripts/sync-changelog.sh; cd web && npm test (770 passed).
codewhale-util's tests use codewhale_runtime::test_support and
codewhale_runtime::private_log, but codewhale-runtime depends on
codewhale-util. A versioned dev-dependency makes cargo publish resolve
codewhale-runtime 0.10.3 before util, which cannot exist because runtime is
published after util. A path-only dev-dependency keeps local tests unchanged
and is stripped from the published manifest.
Verification: cargo check -p codewhale-util --tests; publish-crates.sh dry-run
now passes codewhale-util and proceeds to codewhale-tui-kit.
The release heading is the top CHANGELOG section again, dated 2026-11-08, with
the Runtime API agent discovery and provider-key check entries under Added and
the website polish under Fixed. Regenerates crates/tui/CHANGELOG.md and
web/data/agent-docs.generated.json.
Verification: scripts/sync-changelog.sh; scripts/release/check-versions.sh
--require-dated-release (version state OK); scripts/check-contributor-credit.py
(15 contributors, all credited); cd web && npm run check (lint, tsc, build).
main-landing.yml now triggers on push to main as well as manual dispatch, and
its full-ci job dispatches ci.yml with expected_sha set to the pushed commit.
The preflight job stays narrow so a red landing still shows up in minutes, and
the dispatched run fails loudly when --ref main no longer resolves to the
pushed SHA.
Verification: both workflow files parse as YAML; ci.yml exposes the
expected_sha workflow_dispatch input and checks it against the resolved HEAD.
The README mode row and all 18 localized READMEs now describe Plan and Work
only, the 15 changed locale strings match, and /mode usage, the mode_change
hook text and the command-verb comment no longer offer Operate. Completes
c50612d6ab and 71a8599b54; `operate` still parses as Work, as MODES.md says.
Verification: scripts/preflight.sh --check (rustfmt, README stamps, changelog
slice, budgets) OK; cargo test -p codewhale-localization 55 passed, 0 failed.
Review of 0a626fdab8 (StepFun step-5-preview, read-only, run through codewhale
exec) found three things, all verified here:
- docs/RUNTIME_API.md still listed "thread not in trusted mode" as a 409 for
file-revert, and docs/zh_hans/RUNTIME_API.md kept both the "always requires
trust" table row and the 409 clause. Both now match the route.
- The CHANGELOG line for e7f2844049 said the App Server clamps a zero page
size; that commit changed only the stdio thread/loaded/list handler, and
GET /v1/threads still truncates to an empty page. The line now says so.
- The cycle_mode doc comment still named Operate.
README.md goes back to its c50612d6ab text: editing the English README stales the
stamp on all 18 docs/i18n/README.*.md files (preflight --check), so the mode-row
change returns together with its translations.
Verification: preflight --check run before this commit showed only the README
stamps and one rustfmt hunk (fixed in 28ff652d9a); rustfmt --check on app.rs clean;
sync-changelog --check clean. Comment and documentation changes only.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
README, KEYBINDINGS and CONFIGURATION still described Operate as a live mode, a
Tab stop and hotbar slot 6, and goals as Operate-mode goals. Matches c50612d6ab
and crates/config/src/hotbar.rs DEFAULT_HOTBAR_ACTIONS. Localized docs and
locale strings are not covered by this commit.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Plugins: the load path discarded the reason staged_bundle_rejection already
computes, so enabling a plugin whose snapshot failed verification said only "no
staged snapshot". The reason now rides in the runtime-snapshot-unavailable
diagnostic and in the enable error. On hosted Windows 119 of the 135 failures at
c50612d6ab are that one message; this makes the next run say which check
rejects the stage root. It is a diagnostic, not a fix for those failures.
TUI (CW-6950 U02-06): apply_provider_picker_custom_provider set config.provider
to the new custom provider before resolving its identity, so a failed
resolution or a refused switch left config on the target while the footer showed
the old provider. config.provider is now re-synced from the app on those
returns.
Runtime: the unsupported-mode error named the retired operate mode.
CHANGELOG: lines for the three changes above, the external-agents lookup
(9becbc824a) and the zero-limit thread page (e7f2844049, which had none).
Verification (focused, macOS, existing tests only): codewhale-plugins lib 201
passed, 1 skipped; codewhale-runtime runtime_policy 4 passed; codewhale-tui
provider_picker 152 passed and custom_provider 26 passed; cargo clippy -D
warnings clean for plugins, runtime, app-server and tui libs; blocking-calls
budget within budget; sync-changelog --check clean. Not run: Windows, hosted CI,
a live provider switch failure.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Each agent row now also reports min_version, upgrade (the install command to
run again), login (the command the person runs in a terminal), adapter (the
ACP adapter package when the CLI has no native ACP mode), and needs_update,
true when the installed version is below the minimum. State values are
unchanged, so existing readers keep working. The table is Paradev's
(MIT, packages/host-service/src/chat-v3/acpCatalogue.ts) for Claude, Codex,
Gemini CLI and OpenCode; Kimi has only a login command, and every other agent
has none. DeepSeek Harness (dsh, ACP over `dsh acp`) is the third entry, after
Codex, per decision 88. The minimums for Codex and Gemini CLI are Paradev's
conservative floors, not ours verified.
Versions compare as semver on the leading digits (a pre-release suffix is
ignored); a version that does not parse as major.minor.patch never reports
needs_update. Lookup stays on tokio::fs; nothing here blocks a worker.
Checks: cargo check -p codewhale-app-server --lib --locked passes (8m27s, on a
tree that also holds another session's uncommitted edits in other crates). NOT
run: the route itself (no binary built), so the new fields are unobserved on a
live response.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
resolve and is_executable in runtime_api/external_agents.rs become async and use
tokio::fs::metadata. c4a7d694a0 left one unprotected std::fs site there, which
fails scripts/check-blocking-calls-budget.py (std_fs sites 1 > budget 0).
Verification: check-blocking-calls-budget.py within budget (653 sites);
cargo clippy -p codewhale-app-server --lib --locked -D warnings clean. No existing
test covers this file; GET /v1/agents/external not exercised after the change.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Cost chip (founder 2026-11-08, CURRENT_DECISIONS 86): the metrics-line chip prints
the confirmed amount and nothing else. A partly priced session shows exactly its
confirmed amount; a session with nothing priced shows no cost segment. The
"subtotal {amount} + unknown ({reasons})", "saved subtotal" and "cost: unknown
({reasons})" templates and format_unpriced_reasons are removed from the engine
and every locale. The price view still says "unknown" for a session with no
confirmed price, and /cost remains the one place that lists each unpriced turn;
its reasons are reworded in en and zh-Hans ("the provider never confirmed the
request finished", "this session predates cost tracking", "still running").
Accounting is unchanged: unpriced turns stay excluded, never guessed.
File revert: 74713ec7cd added a trust gate to POST /v1/threads/{id}/file-revert,
undoing 6a6d9bf298 ("revert follows the user's own choices"). The route's
checks are the restore point the thread owns and the reviewed hash, as before.
The gate, its CHANGELOG line and the docs/RUNTIME_API.md "trust always" row are
removed, and the route test is renamed to validate body then ownership.
Verification (focused, macOS, existing tests only): codewhale-localization lib
55 passed; codewhale-engine route_billing 52 passed; codewhale-app-server
file_revert 2 passed; codewhale-tui cost 36 passed, statusline 4 passed,
provider_switch_keeps 1 passed; cargo fmt --check clean; sync-changelog --check
clean. Not run: Windows, hosted CI, a real TUI or desktop journey.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The first hosted run on the mode-retirement candidate (c50612d6ab) failed in
four jobs, none of them flaky.
Lint and Parity: clippy's `single_element_loop` on two `for mode in
[AppMode::Agent]` loops left behind by the Operate removal (plan_handoff.rs:151,
codemode.rs:1604). Both are straight-line assertions now.
Test (all three platforms): three app-server tests.
- `create_thread_..._named_permission_posture` sent `mode: "operate"` and
expected it echoed back. The runtime normalizes the retired spelling to Work,
so it now asserts `agent`, which is the alias contract the release notes
promise.
- `file_revert_route_validates_body_then_trust_then_ownership` expected a
`409` naming trusted mode for an untrusted thread and got the
"restore point is unavailable" conflict instead, because the route had **no
trust gate at all** — an untrusted thread that named a restore point it owned
could rewrite workspace files. The gate now sits after body validation and
before ownership, exactly the order its name describes, and refuses with the
same wording the TUI's undo gate uses.
- `memory_workspace_scope_without_a_remote_is_a_client_error` encoded the old
"workspace scope needs an origin remote" contract that 3c7a43252c
deliberately replaced with a work-tree-path identity. It now asserts the
scope resolves and is empty, which is what the code and the handler's own
comment describe.
Release candidate: "Verify exact candidate web surface" failed on
`[check-facts] FAIL — generated agent docs are stale`. Re-ran `npm run
prebuild`; `agent-docs.generated.json` and `features.generated.json` now match
docs/features.toml and the current agent list.
Verification: nextest codewhale-app-server -E on the three names → 3 passed;
cargo fmt clean; sync-changelog --check clean. The trust gate is exercised only
by the route test; no TUI or desktop journey touched it yet.
Work and Plan are the two modes. Tab cycles between them, `/mode`, the mode
picker, the config editor and the default Hotbar offer two choices (the freed
Hotbar slot becomes the reasoning cycle), and `Alt+A`/`Alt+P` still land on
Work and Plan.
`operate`, `operation`, `ops` and `3` still parse, as Work, so an old setting,
script or client never errors; `AppMode::is_retired_operate_alias` is the one
predicate callers ask when they want to say so. A saved
`default_mode = "operate"` is normalized to `agent` at the settings edge, and
the TUI explains the change once per install (a persisted
`operate_deprecation_shown` flag and a startup toast, mirroring the YOLO
migration) instead of silently dropping the mode someone chose. The permission
receipt no longer reads "applies in Work and Operate".
Operate-only behavior goes with it: the launch-concurrency floor of 4, the
top-level child auto-accept posture, the mode's contract message and its
"leaving Operate" replay rule, and the engine/settings/ACP wire spellings.
Fan-out is untouched — sub-agents and workflows keep their 64/128 ceilings, and
`/fleet` is unchanged.
Stale expectations updated in the same slice, including two that were already
red on main before this work: the config-view permission choice lists now
include Accept Edits (`7e6000aa43a` added the posture but left those two
assertions behind).
Verification: `cargo check --workspace --all-targets` clean; nextest
codewhale-config + codewhale-tui + codewhale-runtime → 7465 passed, 2 failed
(both the /context fixtures, re-frozen in the previous commit and re-run
green: 87/87); cargo fmt clean; sync-changelog --check clean; locale parity,
product vocabulary, web locales, dead-code budget, contributor credit and
runtime-contract budget all pass. The retired-mode UI is unverified on a real
Windows or macOS device; hosted CI on the next candidate is the proof.
Two release-candidate artifact jobs failed on 0d1ffc1cb2, and neither was a
product defect.
ohos-arm64: the SDK mirror publishes the C++ `toolchains/lib/binary-sign-tool`
and no `binary-sign-tool.jar`, so the signer probe failed after the binary had
already cross-compiled in 27m33s ("binary-sign-tool.jar is missing from the SDK
toolchains component"). I extracted the pinned SDK archive and confirmed the
layout and the tool's own usage text: the native binary takes the same
`sign` / `display-sign` subcommands and the same `-inFile` / `-outFile` /
`-selfSign` flags. The step now uses the jar when a DevEco SDK provides one and
the native tool otherwise, and lists `toolchains/lib` when neither exists.
windows-x64: the build, the import check and the version banner all passed
(`codewhale 0.10.3 (0d1ffc1cb2)`); the clipboard acceptance harness died with
`TimeoutError` before it ever spawned a terminal, so the receipt carried no
desktop state. The retry loop added earlier only retried the response headers —
`fetch`'s 60s signal also covers the body read, so a slow 200 timed out on
`arrayBuffer()`. The retry now covers fetch + body + digest as one unit with a
180s budget per attempt.
Verification: node --test .github/scripts/release-workflows.test.js → pass
(the OHOS step contract still asserts the jar path, -selfSign 1, display-sign
ordering and scripts/ohos-env.sh); node --check on the harness. Neither fix has
run on a hosted runner yet; that is what the next RC dispatch is for.
`/context report`, `json`, `prompt-json` and `summary` all froze the bundled
constitution at 1803 tokens and the source-entry total at 2207. The base prompt
intentionally grew to 2414 / 2818 (`5ba1aa63bc`, `080554911c`), and commit
69505829ab recorded that growth only in the runtime-contract budget — the four
captured fixtures were left stale, so `context_routing_and_report_branches_
match_baseline` and `context_expectations_preserve_counts_for_each_host` failed
on main before any of today's work.
Fixtures are re-frozen from the measured output, and the host-difference guard
now perturbs 2414 rather than the retired 1803 so a real regression in another
source still fails on every fixture.
Verification: nextest codewhale-tui --lib -E 'test(debug_diagnostics)' → 87
passed, 0 failed (5632 filtered); isolated HOME, CODEWHALE_HOME unset.
GET /v1/agents/external reports Claude, Codex, Gemini CLI, OpenCode, Kimi
Code, Mistral Vibe, Qoder, CodeBuddy and Grok. Each binary is resolved on
PATH and in the usual per-user bin directories, then probed concurrently
with `--version` and, where the CLI speaks Agent Client Protocol itself,
one `initialize` over stdio. The response carries the version, whether ACP
is native or needs a separate adapter, the agent's own auth methods, and
its website. `runnable` stays false: this route reports, it does not start
an agent.
The probes are child processes that never receive Codewhale or provider
credentials: CODEWHALE_*, *_API_KEY, *_AUTH_TOKEN, *_ACCESS_KEY and
*_SECRET_KEY are removed from the child environment. Nothing is stored.
Timeouts are 8 s for the handshake and 5 s for --version, so a missing or
hanging CLI cannot stall the route.
Validation:
- Farm build on Boat (farm-722523): `cargo build --bin codewhale`, exit 0,
with the exact working-tree copies of the two source files.
- Live route against that binary on the same VM:
- Real CLIs on the image: claude 2.1.319 (adapter), codex 0.167.0
(adapter), opencode 1.19.5 (native, agent auth), kimi 2.5.2 (native,
terminal auth), mistral-vibe 3.1.0 (native, agent auth); gemini,
qoder, codebuddy and grok reported not_installed.
- Fixture binaries for all nine: each reported the expected
adapter/native kind, version and both auth method types.
- Environment: 32 fixture invocations, zero leaked names (no
CODEWHALE_HOME, provider *_API_KEY or CODEWHALE_SECRET_SENTINEL).
- Nothing written under CODEWHALE_HOME.
- `node web/scripts/check-facts.mjs` exit 0 after regenerating the derived
docs from a clean HEAD export.
- Not run here: clippy in this session (it passed on these exact file
contents earlier today); hosted CI.
Reproduced from a founder session on a 0.10.3 dev build (artifacts
subagent-failures-20261107): sub-agents died with "Child usage settlement
failed", a turn failed on "native usage journal capacity reached before
dispatch", and two workers returned Anthropic 400 "tools.17.custom.name".
- Anthropic client: tool names were sent raw, so a name such as web.run (from
tool_search) invalidated the whole request. Outbound tool declarations,
replayed tool_use blocks, tool_choice and the omitted-tool list now use
to_api_tool_name, and streamed and non-streamed tool_use names are decoded
with from_api_tool_name, matching the chat and responses clients.
- Usage journal: MAX_RUNTIME_USAGE_RECORDS_PER_OWNER 64 -> 256. Children share
the session's journal owner and the workflow default is 64 live agents with a
ceiling of 128, so a full fan-out plus the parent exceeded 64. be43ea93fd
already makes the overflow non-fatal; this keeps the receipts.
- Turn start (CW-6633, #6184): the heartbeat stayed Idle until TurnStarted, so
a wait before it produced no log, no stall record and no cancel. The
Preparing bound (180 s) now starts at the top of handle_admitted_operation
with an IdleOnDrop guard, and owner_work warns when all four slots are held.
The root cause of #6184 is still unproven.
- Session list (CW-7553, #7095): list_sessions failed whole on one unreadable
record and two callers turned that Err into an empty list. New
SessionManager::list_sessions_lenient returns the readable records, newest
first, and logs the rest; the launch card and ACP session/list use it. The
strict list_sessions stays for retention and prune.
- NSIS uninstall (CW-7542, #7091): also removes .codewhale-install.sha256 and
the codewhale.old-*/codew.old-* files a self-update leaves. makensis compiled
the script with placeholder inputs.
Verified on macOS with an isolated HOME: codewhale-engine lib client::,
cost_status, turn_heartbeat, session_manager: 829 passed/0 failed; cargo check
of codewhale-tui, codewhale-app-server and codewhale-runtime clean. Not run: hosted
CI, Windows, a live Anthropic request, or a real >64-child fan-out.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
PUT /v1/providers/{id}/key checks only a key's shape and stores it, so a wrong
key was saved and the desktop showed 'Couldn't load models'. POST
/v1/providers/{id}/key/check asks the provider's own /models once through the
existing client::verify_provider_api_key (the call the TUI's guided setup
already uses) and never stores anything. 401/403 from the provider answers 422;
any other failure to ask answers 502, so a client can still save the key. A new
route, not a new field on PUT: that request denies unknown fields, so an older
Engine would 422 it, and session-key handoff to a Boat Engine must not start
calling the provider. checked:false means the provider has no /models to ask.
Replaces nothing; ApiError gains bad_gateway beside unprocessable.
Validation: cargo build -p codewhale-cli on the Boat farm, exit 0, 0 warnings.
Live, on a Linux VM with the built Engine: loopback provider good key 200,
401 -> 422, HTTP 500 -> 502, closed port -> 502; the real OpenAI endpoint with an
invalid key -> 422; empty key 400; unknown field 422; no auth header 401; no key
from any check on disk; the existing PUT still stores (200). Existing tests
(-p codewhale-app-server --lib provider_key, RUST_MIN_STACK=16777216): 3 passed,
0 failed; at the default 2 MiB stack one of the three aborted with a stack
overflow and was not investigated. cargo fmt --check clean; sync-changelog and
check-facts clean. Not run: the full app-server suite, hosted CI, Windows.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
workspace_relative_path used a case-sensitive Path::strip_prefix, so on Windows
a recorded write whose drive letter, directory case or verbatim (\\?\) prefix
differed from the workspace root was dropped and /undo could refuse or leave
files and conversation out of step (CW-7418, #6970). The strict match still
runs first; only on Windows a component-wise ASCII case-insensitive match,
treating Disk and VerbatimDisk as the same drive, is tried next. The result is
still confined to the workspace and still goes through is_safe_relative_path.
Verified on macOS: codewhale-snapshot 91 passed/0 failed. The Windows-only
branch is not exercised locally; real Windows /undo remains unqualified.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Hosted Lint on e59a119643 failed check-runtime-contract-budget on the base
stage identity digest. The base system prompt grew from 7231 to 9063 bytes from
5ba1aa63bc (eager sub-agents and goals by default, clearer tool text) and
080554911c (ask the user with options); both are intended product changes.
Re-measured provider-free on macOS aarch64; the budget carries the reason.
Verified: check-runtime-contract-budget.py passes (55 metrics exactly at
budget). Hosted Linux measurement not yet compared beyond the identity digest.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Hosted Lint failed check-blocking-calls-budget on three new path_canonicalize
sites, all in synchronous helpers: the workspace-id fallback for a folder
without a git origin (runtime native_memory), validation of a thread's add_dirs
at creation (engine runtime_threads manager), and the exec --add-dir path (tui
lib). Each is a one-shot metadata call, not a loop or a hot path. The same
update tightens three budgets that shrank. Refs #6149.
Verified: check-blocking-calls-budget.py passes.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The release-candidate web surface job failed check-facts: generated agent docs
were stale after the 0.10.3 feature and docs edits. Re-ran npm run prebuild in
web/; npm run check:facts now reports facts and cloud facts OK.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Full engine lib run under nextest (7104 tests, isolated HOME) found these
existing tests asserting behavior the batch deliberately changed:
- persistent_service_requires_explicit_headless_exec_authority matched the old
persist:true refusal wording; it now matches "Full Access shell authority".
- Two sub-agent coordination tests expected an unheld flock to be an error; the
manager now falls back to session-private state, so they assert that.
- Compatible-default breadth is 16 with Wafer; Wafer's GLM-5.3 route is recorded
as a reviewed Unknown-billing omission (no price source was reviewed).
- A cancelled pending child request projects "request in progress" in-process
(reserved by 5d8e4427a7); the test accepts it as well as "outcome unknown".
Not changed: four engine tests fail only on a machine whose home has an empty
global instructions.md, and parent_agent_surface_stays_within_measured_ceiling
only on hosts with pandoc and tesseract (documented in that test); both pass or
do not apply on hosted runners.
Verified: each listed test passes under nextest with a clean HOME/CODEWHALE_HOME;
full engine lib before these edits: 7095 passed, 9 failed, 13 skipped.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Hosted Lint and the release-candidate Parity job failed on 072468aff2 with
clippy::collapsible_if in crates/config/src/catalog.rs and, behind it,
crates/tui/src/tui/provider_picker.rs. Both are flattened with let chains,
same order and short-circuit. The web credits-parity test failed because
@cmdcorp6534 and @Asteria1028 are in the 0.10.3 changelog ledger but not in the
website release helpers or the docs/CONTRIBUTORS.md v0.10.3 band; both added.
Verified locally: cargo clippy --workspace --all-targets --all-features --locked
-D warnings (CI flags) exit 0; cargo fmt clean; web vitest public-copy 6 passed;
check-contributor-credit passes. Hosted CI not rerun yet.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Hosted Windows run 42381817495 (549ff01) failed 221 cases. Fleet ledger replay
and state rebuild, and the CLI metrics diagnostic reader, opened files through
the protected reader that denies concurrent writers, so a live writer caused
OS error 32. They now use WorkspaceFile::open_file_shared, which keeps the
regular-file, link and identity checks. Plugin enablement now reports which
snapshot check refused (stage root, content identity, final root check) so the
118 snapshot rejections can be diagnosed from the next hosted run; the refusal
itself is unchanged. .gitattributes keeps LF on the generated protocol schema
and SDK exports.
Verified on macOS only: plugins lib 201 passed/0 failed; engine fleet:: 435
passed/0 failed; cli metrics:: 42 passed/0 failed; cargo fmt clean. The Windows
effect is unproven until hosted CI runs. The 2 MiB stack-overflow case is not
addressed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Removes one blank line that cargo fmt --all --check flagged; the hosted Lint job would fail on it. Verified: cargo fmt --all -- --check clean.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Moves the 13 engine fixes from [Unreleased] into [0.10.3], adds the #5185/#5186
Accept Edits receipt so check-versions passes, and regenerates the embedded
changelog. Completes the add_dirs rollout in test fixtures and the Runtime Chat
relay (cargo check --tests failed on runtime_chat_relay.rs). Child compaction
without an admitted origin, a failed child usage projection and an
unacknowledged previous usage publication now log and continue with cost
unknown instead of erroring the turn.
Verified: cargo check -p codewhale-engine -p codewhale-app-server --tests
--locked clean; engine lib cost_status 44 passed/0 failed; compaction 169
passed/0 failed; check-versions --require-dated-release and
check-contributor-credit pass. Windows hosted CI, full suites, packages not run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
When the workspace coordination flock cannot be acquired, the session now
falls back to private, unsaved sub-agent state and keeps working. Before,
a stale holder made every new session refuse sub-agent work with
"Failed to clean up sub-agents".
Verification: cargo check -p codewhale-engine --lib passed.
The interactive usage sink returned false for a record without a
settlement outside cfg(test), so the record was never acknowledged and
the child path reported "Child usage settlement failed". Production now
takes the same legacy usage path the tests exercised.
Verification: cargo check -p codewhale-engine --lib passed.
The TUI and Runtime threads now advertise persistent background services,
so a dev server can outlive the turn when the thread has Full Access.
Restricted postures, Fleet/sub-agents and TTY/stdin modes still refuse.
Verification: cargo check -p codewhale-tui --lib passed.
POST /v1/threads accepts `addDirs`, the API counterpart of
`codewhale exec --add-dir`. Each path must exist and be a directory; the
canonical paths persist on the thread and join the tool context for its
turns.
Verification: cargo check -p codewhale-engine --lib and
cargo check -p codewhale-app-server --lib passed.
`codewhale exec --add-dir <path>` (repeatable) adds an external root the
file tools may read and write, without Full Access. The path must exist
and be a directory. EngineConfig carries the roots into the tool context.
When the native usage journal reaches its per-owner cap, the request now
runs with cost unknown instead of failing before dispatch on "native
usage journal capacity reached". Acknowledged receipts are pruned first.
Verification: cargo check -p codewhale-tui --lib and
cargo check -p codewhale-engine --lib passed. No focused runtime test
for --add-dir yet.
File revert no longer requires /trust or Full Access. The restore point
must still be this thread's own tool/pre-turn snapshot and the reviewed
hash must still match.
fetch_url keeps blocking private, loopback and link-local hosts by
default, but an explicit `/network allow <host>` now opts that host in.
Cloud metadata stays blocked. The error names the setting.
Verification: cargo check -p codewhale-net --lib and
cargo check -p codewhale-app-server --lib passed. codewhale-net
rejects_private_localhost_literal and rejects_private_rfc1918 passed
2/2. codewhale-app-server revert_file_helper_restores_only_the_named_file
passed 1/1.
Plain `codewhale exec` now runs on the normal tool surface under the
configured sandbox and approval posture. `--no-tools` is the one-shot.
When stdin is not a terminal, a pipe is the prompt, or is appended to
the argv prompt. `--parent-death-watch` still owns stdin.
Verification: cargo test -p codewhale-tui --lib --
exec_tool_surface_is_on_unless_no_tools
exec_keeps_flags_before_split_prompt_words
exec_accepts_split_prompt_words_for_windows_cmd_shims
passed 3/3.
A child whose usage settlement is not acknowledged continues, with cost
unknown, instead of dying on "Child usage settlement failed". Workspace
memory no longer requires a git origin. plugin list is an alias of
plugins status. A heavy command runs after 30 seconds instead of waiting
forever for an admission slot. An MCP registration 4xx includes the
status and the manual client-id path.
Verification: cargo check -p codewhale-engine --lib passed.
codewhale-tui memory_adapter_workspace_identity_resolves_and_preserves_errors
and memory_adapter_deletes_workspace_without_an_origin passed 2/2.
codewhale-engine lib tests compiled the admission and OAuth edits
(heavy_command_admission filter matched 0 tests).
A same-line successor replaces a stale pin. grok-4.7 and muse-spark-1.3
are the current models; a different line, such as GPT-6 beside GPT-5.6,
does not take over. The provider pane shows one current model per line
and spreads a gateway roster across authors, so OrcaRouter no longer
opens as a wall of Claude snapshots.
Wafer is a selectable OpenAI-compatible host. The public model list is
the catalog, GLM-5.3 is the default, and inference uses WAFER_API_KEY.
Verification: codewhale-config lib tests for default identity, Grok
hydration, web search, and the Wafer descriptor (4 passed). codewhale-agent
xAI and Meta default resolution (2 passed). codewhale-tui provider pane
default-first (1 passed). codewhale-engine fleet xAI fallback (1 passed).
python3 scripts/catalog_models_dev.py defaults audit reported 0 stale.
python3 scripts/check-provider-registry.py passed.
docs/features.toml has described the Accept Edits posture since 7e6000aa43,
but web/data/features.generated.json still carried the pre-Accept-Edits
summary and source hash. Regenerated from the file on disk; no source change.
Refs CW-7876
diagnostics reads git like the read-only tools
Bug hunt 2026-11-07, items 6 and 7 (CW-7882).
- tools/github/cli.rs: run_gh_text and run_gh_json are async. gh runs as a
tokio command under process_tree::contained_output_until, and the call
stops, killing gh's process tree, when the turn's cancel token fires or
after 120 s. A stop reports cancelled or timed out instead of blocking a
worker. GH_PROMPT_DISABLED=1 and GIT_TERMINAL_PROMPT=0 keep gh from
waiting on a prompt.
- tools/github/actions.rs and mod.rs await those calls, and
close_github_thread is async. Its tests in github/mod.rs become tokio
tests.
- tools/diagnostics.rs: probe_git runs `git rev-parse` through
tools::git::read_only_git_command, the same command the read-only git
tools use, with fsmonitor, hooks and the repository's filters off. Other
probes keep run_command.
The patch was written by a Sonnet agent
(CW/artifacts/bug-hunt-20261107/patches/items-6-7.patch), and reviewed
against the APIs it calls (contained_output_until/ContainedOutput,
ToolContext.cancel_token, ToolError::cancelled, read_only_git_command) and
for remaining sync callers before it was applied and verified here.
Evidence:
EVIDENCE
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Two more children now get the treatment the other workspace runners
already have.
- GitHub close's working-tree check (tools/github/host.rs) reads
`git status` through codewhale_dependencies::Git::status_command, the
reader the Runtime API's git routes use. It runs in the workspace with
fsmonitor, hooks, lazy fetch and replace objects disabled, and the
repository's own filter drivers neutralized, with the same
`--porcelain=v1 --ignore-submodules=dirty` arguments. The other
host_git calls (rev-parse, remote get-url) run no repository-configured
programs and are unchanged.
- Legacy plugin tools (script plugins and [tools.overrides] commands) build
their child with shell::sandboxed_runner_command when the extension host
is off: the session's sandbox policy and OS wrapper, the sanitized child
environment, and the workspace as working directory. Before, they ran
with the Engine's environment and working directory and no sandbox.
PluginExecutor::run now takes the program and arguments instead of a
prebuilt command. The extension-host path builds the same command it did
before.
Evidence:
EVIDENCE
Co-Authored-By: Claude Opus 5.5 <[email protected]>
CW-7673 (App settings slice S1, codewhale-app 83634750). The desktop's
Settings rows for reply style and "agents at once" had no Engine write path.
POST /v1/config {key:"verbosity"} fell through to settings.toml, which
rejects the key, and [subagents] max_concurrent was neither readable nor
writable. Root approved this as a post-freeze-safe slice (2026-11-07).
- GET /v1/config adds `verbosity` (unset reads "normal", the Engine's own
default) and `subagents_max_concurrent`: the configured
[subagents] max_concurrent capped at MAX_SUBAGENTS (128), or, when unset,
the limit the Engine applies (top-level max_subagents, else 64). The Engine
never writes that default (CURRENT_DECISIONS §62: a person-set limit).
- POST /v1/config validates both keys before any write, also on dry runs:
- verbosity: one of project_config's CONFIG_TOML_VERBOSITIES (normal,
concise), case-insensitive, persisted as the root config.toml key.
- subagents_max_concurrent: an integer 0..=128; larger values store 128,
and 0 turns sub-agents off, matching max_concurrent = 0 in config.toml.
Persisted through persist_subagents_integer_key, next to
subagents_max_depth.
The response `value` is what was stored. Persisted writes apply on
POST /v1/config/reload, like the other config.toml keys.
- RUNTIME_CONFIG_KEYS lists both keys beside the other curated arms.
- docs/RUNTIME_API.md (and zh_hans) document the keys. CHANGELOG.md and
crates/tui/CHANGELOG.md carry the line.
settings.rs and settings_schema.rs are untouched. No schema row exists for
either key, so GET /v1/settings/schema is unchanged.
Evidence:
EVIDENCE
Refs CW-7673
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Refs CW-7882 (item 1). The previous commit made the cost labels follow
the unresolved sources when a usage record is projected, but late
background usage takes another path: SessionCostSnapshot's
absorb_late_background_cost and the TUI's absorb_pending_background_cost
reconcile resolved sources out of missing_usage_sources and add the batch's
own reasons, without pruning. When a late receipt settled the last source
behind "request outcome unknown" (or "provider omitted usage"), the label
stayed while another turn was unpriced for a different reason.
The prune now lives in one helper,
cost_status::prune_settled_missing_reasons, called from all three places:
the record projection (replacing its inline block), the engine's late
absorb and the TUI's late absorb. The rule is unchanged: each label stays
only while an unresolved, non-running source carries it or the source map
has overflowed, and "request in progress" only while an in-flight source
is still unresolved.
Not covered: a turn audit can carry a reason label of its own, but no
production audit uses these two reasons (only missing-source records do),
so pruning by the source map cannot drop an audit's label.
Verification: MISSING
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An audit of v0.10.2..origin/main (762 non-merge commits, 209 of which
touched CHANGELOG.md) found user-visible changes that landed without a
changelog line. This adds 27 of them to [0.10.3]: 7 Added, 1 Changed,
14 Fixed and 5 Security, each appended at the end of its section.
Each line was checked against its commit message and the existing
[0.10.3] text before it went in:
- Dropped as already covered: the command palette keeping your draft
(the #7205 line) and "Goal cleared." only with a goal.
- Kept but narrowed: the Markdown table line adds only what the existing
wrapped-cell line does not say; the compaction line states only the
no-silent-truncation failure; the MCP line says the real rule (npx,
pnpx, bunx or uvx with no own connect_timeout get at least 120 s).
- Dropped claim: simpler Computer Use onboarding on the website is
source preparation waiting for the CU 0.13.0 release, so the Website
line names only the Enterprise page (live at codewhale.net/enterprise
today) and the finished FAQ.
- Public issues appear as #N only where the commit carried one (#7087,
#7102).
The TUI changelog slice is regenerated from the root file.
Verification: VERIFICATION_PLACEHOLDER
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The founder's TUI showed "subtotal $0.10 + unknown (request outcome
unknown)". Every provider dispatch first records a write-ahead reservation
(coverage Missing, reason RequestOutcomeUnknown, reservation=true), and
SessionCostSnapshot added that reason's label to unpriced_reasons but
removed it only when unpriced_turns returned to 0. So a running request
read "request outcome unknown", and once it settled the label stayed
whenever any other turn was unpriced for another reason.
Reproduced on the installed build 7b8a634fbb (TUI in tmux, isolated HOME,
mock DeepSeek endpoint, one turn with three explore children): after the
turn settled the chip still read "cost: unknown (request outcome unknown,
live rate unverified)", while the saved session showed all six sources
settled with coverage reported and no missing sources.
- The "request outcome unknown" and "provider omitted usage" labels follow
missing_usage_sources: each is dropped once no unresolved, non-running
source carries that reason (an overflowed source map keeps it, since its
sources are no longer tracked one by one). Late background usage already
projects its missing sources into the same map.
- A reservation is tracked in a process-local in_flight_usage_sources set
(serde skip) and labelled with the new UnpricedReason::RequestInProgress
("request in progress", CostReasonInProgress in all 15 locale packs).
The label leaves when the source settles. The set keeps only sources
that are still unresolved. Persisted reasons never store it:
serialize_settled_reasons writes it as request_outcome_unknown, so a
reservation left by an exit or crash reloads as outcome unknown.
- The TUI rebuilds a SessionCostSnapshot from its own SessionState on every
projection, so SessionState carries in_flight_usage_sources too: copied
both ways, and cleared with the rest of the coverage on /new and session
load.
- Not covered: the parent turn settles a cancelled or failed request with a
final outcome-unknown record that replaces its reservation; a dispatch
path that dropped its source without settling would keep reading
"request in progress" until the session reloads.
- UsageSourceRevision gains is_reservation(); the one exhaustive test
literal of SessionCostSnapshot names the new field.
Verification: MISSING
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`codewhale exec` sub-agents never ran: each child stopped before its first
provider request with "Provider request outcome unknown; checkpoint
preserved without automatic replay: child provider call has no captured
accounting origin", and the parent's wait found nothing running. The
founder's l10n fan-out run hit it on every child.
Cause: exec_agent.rs hands its session origin to
runtime_services.accounting_origin, which the parent turn already falls
back to (Engine turn accounting). The per-turn ToolContext that
SubAgentRuntime is built from only received an origin from a live Runtime
cost owner, which exec does not have, so ChildAuthority::usage_origin()
was None and ChildDispatchedRequest::bind_dispatch refused every child
dispatch. The refusal is compiled out of test builds (cfg(test) /
test-support), which is why the unit suites never saw it.
Fix: after the live-Runtime branch, a turn's tool context falls back to
runtime_services.accounting_origin, the same fallback
subagent_tool_context and the off-turn runtime already use.
Verification: MISSING
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A child agent's built-in verification run (Run tests/verifiers without
custom commands) no longer skips the approval its tool declares. It is
allowed without a prompt only inside a root Operate worker's verification
lease, as the agent tool description states, or when the session
auto-approves. Otherwise it reaches the parent's approval like any other
code-running call: relayed when the parent can prompt, refused when it
cannot.
- ChildAuthority::delegated_call: the Required arm now needs the captured
runtime's accept_verification bit in addition to the bounded-verification
classifier.
- spawn_subagent_from_input: a start that took no approval (a canonical
read-only role) does not receive the Operate verification lease, since it
crossed no approval boundary.
- run_verifiers foreground gates (and workflow completion gates) build
their child with shell::sandboxed_runner_command, the helper the cargo
test runner and the other workspace runners use. They get the session's
sandbox policy and OS wrapper, and a session whose commands run in an
external sandbox reports the gate as skipped with the reason. Timeouts,
process-group kill on Stop, and output capture are unchanged, and the
gate's declared environment is still layered on the sanitized child
environment.
- The subagent test that pinned unprompted verification for a non-Operate
Builder child now expects the parent approval, like arbitrary shell, and
three unavailable receipts. The Operate-lease test is unchanged.
Evidence:
EVIDENCE
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Follow-up to CW-7669 (9805a009e8), from the App's adoption run against
cd7f5b32b1 (CW/artifacts/cw7669-app-terminals-20261107/run3): after the
agent ran `exit 7`, GET /v1/threads/{id}/terminals listed the dead shell
with "state":"running" beside "running":false, "exit_code":7. `state` was
the durable state of the shell's latest command, and RUNTIME_API.md listed
it without defining it.
- The list now reports "exited" once the shell has an exit code. While the
shell lives, it keeps the latest command's state (running, idle, canceled,
failed, stale_lost, reset), so "canceled" with "running":true after a
confirmed interrupt stays as it is, which is correct.
- docs/RUNTIME_API.md (and zh_hans) define the field.
The App reads only `running` and `exit_code`, so nothing was blocked.
Evidence:
EVIDENCE
Refs CW-7669
Co-Authored-By: Claude Opus 5.5 <[email protected]>
CW-7841 interim (founder decision 2026-11-07: "Interim fence today").
An Engine turn carried no account identity to the desktop's
codewhale-browser MCP route, so a turn admitted while account A was
signed in could still drive the side browser after the person switched
to B in the same folder: the authenticated route proves the Engine owns
the endpoint, not which account the call came from.
- profile_constitution.rs, which already owns per-turn account capture,
derives an AccountEpoch from the native account store: api_base, state
(offline-cached folded into authenticated), account_id and session_id;
for signed out, the logout tombstone's revision or "pristine". The
store's own revision is not used: token refresh rewrites it. Test
builds keep the module's rule of never reading an operator's account
and see a fixed signed-out epoch.
- Engine::handle_admitted_operation captures an AccountFence when a
person's turn (ExternalUser provenance) is admitted; internal
follow-ups keep the admitting turn's fence. build_tool_context_for_turn
stamps it on ToolExecutionState, so child agents, programs and batch
contexts inherit it.
- admit_desktop_browser_call refuses a mcp_codewhale-browser_* call when
the turn has no confirmed epoch, the store cannot be read, or the
current epoch differs, with a permission error saying nothing was
sent. It runs inside execute_mcp_tool_with_pool after the pool lock
(turn dispatch and code mode) and in McpToolAdapter (registry path).
The name prefix over-applies to a model-started server squatting the
name, which is harmless; it never under-applies. mcp.rs is untouched.
- docs/RUNTIME_API.md (and zh_hans) document the fence and its limits.
Known limits (also in the module doc and RUNTIME_API.md): only the local
account store is compared, so states the desktop keeps to itself
(development sessions, replay, an unreachable account service) are not
seen; the browser server receives no caller identity; registrations
carry no generation; Platform revocation counts once a refresh writes
it. The signed-generation contract is the 0.10.4 follow-up on CW-7841.
Evidence:
EVIDENCE
Refs CW-7841
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#7095 ("conversation history is lost") is still unreproduced and this does
not fix it. It adds the two pieces the diagnosis asked for, so the hint
cannot be missed and the next report is not blind.
- The startup notice (the interrupted-session hint, or the auto-resume
receipt) is a standing status toast instead of status_message, so a later
status update covers it only while that update lives. It is retired when
the first turn starts or a session is opened. It is never written into
the transcript, the session, a checkpoint or a model request, and it still
never replaces a "Failed to ..." load message.
- On a plain launch, Codewhale logs (target "sessions", INFO) the sessions
directory, the saved-session count, checkpoint files, interrupted
checkpoints, and whether the newest interrupted one belongs to this
workspace. A store that cannot be opened logs a WARN.
- codewhale doctor shows "N saved sessions, M crash checkpoints" under
Sessions, and doctor --json adds sessions.counts {saved, checkpoints}
(null when no sessions directory exists). It reads through
SessionManager::open_read_only, so doctor stays read-only.
- SessionManager::saved_session_count counts documents by file name only.
is_session_document is now the one filter, shared with
scan_session_records.
Not changed: load_recent_checkpoints, checkpoint promotion, persistence
actor ordering, retention and auto-resume.
Evidence:
EVIDENCE
Refs #7095
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Hardening for #6184, a turn that stalls silently before its first model
request. The root cause is not proven. These changes make such a stall end
in a bounded, logged state instead of waiting forever, and leave the next
report a trace. Founder decision 2026-11-07: "Hardening, then freeze".
- The turn heartbeat begins (begin_turn) before TurnStarted is sent, and an
IdleOnDrop guard returns it to idle on every exit path, so an early return
can no longer leave the watchdog in Preparing.
- Preparing progress is touched after the git line, after the tool catalog
is built and after MCP server attribution, so a slow but moving start is
not taken for a stall.
- The pre-turn restore point runs under its own Preparing bound
(RESTORE_POINT_TIMEOUT 180 s plus the 30 s grace) and is cancel-aware. On
timeout the turn continues and the person sees "No /undo restore point
was recorded: the workspace snapshot did not finish within 180 s."
- capture_turn_meta_git_snapshot (30 s) and the MCP pool lock taken for tool
server attribution (30 s) are bounded and cancel-aware. On timeout the
turn continues without the git line or the attribution, with a WARN.
- runtime_control::owner_work waits at most 60 s for a blocking-work permit,
then fails with "blocking work is queued behind earlier jobs that have not
finished within 60s" instead of queueing forever.
- git_status reads (collect, git_output, observed_git_repo) run through
run_bounded_git_with_timeout with a 60 s bound.
- TUI: live_running_agent_count counts an agent_progress entry only while
its last progress is within TOOL_HANG_WATCHDOG_TIMEOUT, so a ghost entry
left by a vanished sub-agent no longer vetoes the stall watchdogs.
- Logging: INFO "turn started" once per turn id and "turn phase" on each
real phase or detail change (turn_id, phase, detail, bound_secs). The
watchdog WARNs once per progress step when a phase has gone half its
bound without progress.
ToolCallHeartbeat is untouched. Session B (a separate lead) is not
addressed here.
Evidence:
EVIDENCE
Refs #6184
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Production builds refused provider calls with "no captured accounting
origin" while test builds skipped the check (cfg(not(test))), so tests never
saw the refusal that broke codewhale exec sub-agents. Production now does what
the tests always did: dispatch proceeds and usage without an origin is simply
not attributed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The accounting path turned a local bookkeeping failure ("previous usage
publication has not been acknowledged") into a stream error, so a turn the
model had completed failed, and the runtime thread manager marked it Failed.
Sub-agents were interrupted with "worker source has no durable
acknowledgement". Seen live on 0.10.3 at 080554911c with deepseek-flash.
The receipt still carries accounting_error and the status line still says
settlement is uncertain; an unacknowledged worker source is logged and not
counted instead of aborting the worker.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The clipboard-image-prune row's summary was 143 characters, so
`cargo test -p codewhale-tui --test feature_registry` failed at HEAD
since c98ea3836e. Shortened to 115.
Check: the registry rule itself is `docs/features.toml` summary of 1-120
characters on one line, `crates/tui/tests/feature_registry.rs:103-107`;
the row's docs and owner paths both exist. The full
`feature_registry` test runs in the TUI farm check for this batch.
Founder direction: Codewhale should ask for input the way Claude Code
does. Design: artifacts/ask-the-person-20261107/FINAL.md, sections 2a-2e.
Strings and one eager-tool entry only; no wire, schema-shape or event
change. This is one more declared re-pin of the session-pinned prefix
(docs/CACHE.md), on top of 5ba1aa63bc.
- BASE_PROMPT "User intent and scope": act on clear, reversible work
without asking; when a choice is the user's, ask once with options. A new
host-neutral paragraph says when to ask: an ambiguous target, a real fork
with different costs, a preference that cannot be inferred, or an
irreversible or outward-facing step the request did not clearly
authorize. It says when not to: conventional defaults, reversible work,
readable facts, whether to delegate, start a goal or run a workflow, or
whether to continue. Questions stand alone, with the recommended option
first, and with no question tool the turn ends with one lettered
question. It never names the tool, so exec and ACP read nothing false.
The three "name it and ask" lines now say "ask with options".
"Delegation and goals" gains "Tightly coupled or sequential work stays
with you.", from Anthropic's Oct 2026 guidance on folding sub-agents into
the main loop (split by context; keep coupled work in the loop).
- Plan notice: "Resolve open requirements with the question tool first,
then present the plan." The tested prefix is unchanged.
- request_user_input: the description now says multiple choice, the
recommended option first labelled "(Recommended)", one-line trade-offs,
standalone questions, and not for defaults or "may I continue?". header,
question and options gain field descriptions (the options range comes
from the configured limits), and multi_select gets a shorter one. The
schema shape is unchanged.
- Hosts with a person to answer (user_input_responder: TUI, desktop App
Server, runtime threads) get request_user_input eager through
tools_always_load, added once in the engine constructor;
DEFAULT_ACTIVE_NATIVE_TOOLS is unchanged. Exec and ACP keep withholding
it, child engines never set the responder, and exec gains one
instruction: "No user is present; choose the recommended default and
state the assumption."
- tool_search lists request_user_input and tasks suggest.
- A TUI setup comment that paraphrased the old prompt now matches it.
Unchanged, per the design's guardrails: no third ask path (turn_loop's
ask_user_question stays), ToolError::Timeout text, and the model never
picks an answer for the person.
Bytes: BASE_PROMPT 4,352 -> 5,168. /preview-request after, a responder TUI build on Linux
with the same isolated offline HOME and prompt: 19 active of 59
(request_user_input now eager; 18 before), tool schemas 41,718 canonical
JSON bytes (+1,545 over 5ba1aa63bc's 40,173, about the question tool's
schema), system 18,533.
Tests: the kernel phrase "otherwise name the decision and ask." becomes
"otherwise name the decision and ask with options."; the prompt and event
conformance goldens are re-recorded for the re-pin.
Verified on the Boat farm (Linux x86_64), base 5ba1aa63bc plus only this
slice (engine.rs shipped as HEAD plus this hunk). The commits that landed on
main since then touch none of these files or goldens.
- cargo test -p codewhale-engine --lib, filters prompts::,
tools::user_input, core::engine::tests::, core::engine::tool_catalog::,
runtime_handoff::, canonical actions, agent description and parent
surface ceiling: 729 passed, 4 failed. Two were the user_input description
tests updated here; the rerun of user_input, prompts, runtime_handoff and
tool_catalog passed 166 of 166. lazy_boot_leaves_unselected_servers_unspawned
also fails at base 5ba1aa63bc without this slice.
full_access_auto_approves_non_bypassable_registered_tools (it spawns
python3) failed only under the full parallel run; it passed alone with
this slice and at base.
- cargo test -p codewhale-tui --lib conformance::prompt::
conformance::events::, update pass then compare pass: 5 passed each; the
re-recorded goldens come from that run (tool schemas +80 bytes from the
tool_search hint, system +831). The conformance engine is not a responder,
so its catalog keeps request_user_input deferred.
- cargo clippy -p codewhale-engine --all-targets --all-features and
-p codewhale-app-server --lib, with the Lint job's flags: clean.
Not verified: an exec session end to end (the exec instruction is compiled,
not run), desktop rendering (the App session's CW-7853), and a live
provider turn.
Refs: CW-7874
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Ask (`suggest`) now opens a card for file edits inside the workspace too,
as its name, the Plan handoff copy ("Edits and commands ask you first") and
the founder-agreed model (2026-11-07, modeled on Claude Code's permission
modes) promise. CW-7663 found that the desktop edit card could not appear
with default settings because Ask ran in-repository writes unasked.
The #5185 in-workspace write carve-out moves to a new posture, Accept Edits
(`ApprovalMode::AcceptEdits`; settings and config.toml `accept-edits`,
Runtime wire `accept_edits`), between Ask and Auto-Review: an edit whose
every target stays inside the workspace git work tree runs without a card;
`.git`, `.codewhale`, `.env*`, credential and key files, folders without
git, commands, network and MCP actions ask exactly as in Ask. Everywhere
else Accept Edits is an asking posture (`ApprovalMode::is_ask_family`):
repo-law asks, sandbox escalation and Computer Use consent open a card;
remembered grants treat it as Ask; non-authoritative provenance narrows it
to Ask with a recorded narrowing event; live-authority narrowing ranks it
between Ask and Auto-Review.
- Shift+Tab: Ask -> Accept Edits -> Auto-Review -> Full Access.
- Settings schema rows (permission_posture, approval_mode,
approval_policy), /permissions, /status, /config, the Plan handoff
("Work (Accept Edits)"), footer chip and help strings; 7 new and 3
updated strings in all 15 locale packs.
- config.toml `approval_policy = "accept-edits"`; a project file may only
tighten: auto < accept-edits < on-request < never.
- ACP runs Accept Edits as Ask, because ACP turns never apply the carve-out.
- /import-claude maps Claude Code's acceptEdits to Accept Edits.
- Docs: MODES, KEYBINDINGS, CONFIGURATION, INSTALL, GUIDE, README and its
18 translations (restamped), zh_hans mirrors, features.toml row,
CHANGELOG entry, regenerated web/data/agent-docs.generated.json.
Stated limits: write-capable workers keep #5186's in-folder edits under Ask
(their spawn card is the consent); modes, Tab and Operate are unchanged
pending the founder's answers to the modes/postures design
(CW/artifacts/modes-postures-20261107/DESIGN.md D1-D4; Operate in CW-7849).
Verification (shared checkout, source b3d18fe6bc plus this diff):
- scripts/dev-cargo.sh test --locked --offline -j6, focused filters
(approval_mode, posture, permission*, carve_out, authority::,
protocol_parity, repo_law, sandbox_escalation, computer_use, narrow,
approval_request, runtime_policy, project_config, settings_schema,
plan_handoff, import_claude, file_edit_card, config_choice, keybinding,
locale parity): codewhale-engine lib 263 passed, 0 failed;
codewhale-tui lib 103 passed, 0 failed; codewhale-app-server lib 13
passed, 0 failed; codewhale-config 35 passed; codewhale-runtime 20
passed; codewhale-localization 15 passed; codewhale-execpolicy 12
passed; all 0 failed. Includes write_carve_out_posture_is_exactly_
accept_edits, workspace_write_carve_out_covers_accept_edits_only,
file_write_card_names_the_missing_repository..., permission_cycle_is_a_
closed_loop..., cycle_approval_scenario, permission_postures_persist_
across_restart, shift_tab_migrates_user_root_policy..., plan handoff
locale test, file_edit_card_says_when_it_asks_for_lack_of_a_git_repo.
- codewhale-tui --test feature_registry: this row passes; the run fails
only on another session's uncommitted clipboard-image-prune row, which
this commit does not contain.
- rustfmt --check on every touched .rs file; check-tui-locale-parity PASS
(2823 keys x 15 packs); check-tui-product-vocabulary exit 0;
check-readme-translations OK (18, stamp 20d0bb7c0784);
check-readme-locales PASS; check-versions OK; check-dead-code-budget
PASS (137). check-blocking-calls-budget fails only on another session's
untracked crates/engine/src/session_image_prune.rs (not in this commit).
- Not run: a live desktop/TUI turn showing the edit card (CW-7663's App
acceptance); full workspace suites (final release validation owns them).
Tests ran on the shared working tree, which also held other sessions'
uncommitted edits; this commit stages only this slice's hunks
(private index, HEAD + these changes).
Refs CW-7663, CW-7849
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Hide the OrcaRouter offer row and F7 shortcut in zh-Hans (mainland), keep
it for zh-Hant and other locales, and disclose the offer terms in the
picker and docs/PROVIDERS.md. Localized in all 15 packs. Re-derived the
web agent-docs JSON from HEAD plus this patch only.
Patch authored by the Living Codewhale logo session.
Verified on the Boat farm (Linux, local tests, base 5ba1aa63 overlay):
codewhale-localization 55 passed, 0 failed; codewhale-tui --lib
provider_picker model_picker orca_offer 202 passed, 0 failed. Rerun on
this exact tree follows before push.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
engine-prompts' lines for 5ba1aa63bc, under 0.10.3 Changed, as sent:
sub-agents and goals by default (with the one-time prompt-cache re-pin
called out), the retired Operate contract, clearer agent/goal tool text,
the workflow fan-out default 16 -> 64, bundled `goals` becoming
`personal-goals`, and the child identity line. crates/tui/CHANGELOG.md is
regenerated with scripts/sync-changelog.sh.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
web/data/features.generated.json and web/data/agent-docs.generated.json
are re-derived from the committed docs: the removed claude-plan-sign-in
row, the clipboard-image-prune row, PROVIDERS.md, and the
CONFIGURATION.md and RUNTIME_API.md changes from #7230 and 4c5426f9bb
(whose runtime-api entry had not been regenerated).
The derivation mirrors web/scripts/facts-lib.mjs deriveFeatures and
docs-sources-lib.mjs deriveAgentDocs; run against cd7f5b32b1 it
reproduces both committed files byte for byte.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Refs #2323. Input methods place their candidate and preedit window at
the terminal cursor. With a modal open (for example /config search or
its value editor) the TUI hides the cursor but left it wherever the
frame happened to end, usually the composer underneath, so the Pinyin
candidate window appeared far from the field being typed into.
Committed-text routing was already fixed by f9210a4428; this is the
remaining position half.
When a frame draws no visible cursor, the frame loop now moves the
still-hidden cursor to the top modal's input anchor. ModalView gains
input_anchor() (default None); ConfigView records its search caret or
its editor value caret during render; any other modal falls back to the
bottom-left cell inside its occupied region. Nothing becomes visible;
only the position an IME reads changes.
Verification: the batch's six commits were tested as one change on Boat farm VMs
(Linux x86_64), shipping only `git diff <base> <final>`, so no other
session's uncommitted files took part. cargo test --locked --lib with
filters over config, runtime, engine, app-server, tui and cli on base
3631a174f6: 459 passed, 0 failed (config 8, runtime 117, engine 102,
tui 220, cli 8, app-server 4; farm run
20261107T230833Z-issues-tests-503e01). cargo clippy with the Lint job's
flags (--all-targets --all-features, -D warnings) on config, runtime,
engine, command-contract, cli, tui and app-server: 0 warnings, 0 errors
(run 20261107T231817Z-issues-clippy-e84152). Rebased onto 5ba1aa63bc
(eager sub-agents), which shares subagent/mod.rs, subagent/tests.rs,
tasks.rs and config.example.toml with this batch; this batch's changed
lines are byte-identical on both bases, and a recheck of that overlap on the new base passed: engine 48 and app-server 2 passed, 0 failed (subagent coordination incl. the two-process case, human-wait, execution-guard and tools::tasks suites, server_task_config and server_task_runtime; farm run 20261108T000442Z-issues-recheck-67ab1a).
Locally: cargo fmt --all -- --check, both budget checks,
sync-changelog --check and the feature release-note receipts pass.
Covers config_view*, config_modal_is_usable_and_opaque_at_blocker_sizes,
frame_cursor_is_hidden_during_diff_then_positioned_before_reveal and
cjk_composer_cursor*. The tmux caret check on a macOS binary follows
from the freeze build.
Not verified: Windows Terminal with Microsoft Pinyin (the reporter's
setup), and other terminals' IME integrations.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Refs #7232. While a 0.10.2 session still has this folder's v1 delegated
store open (it holds subagents.v1.lock shared), a 0.10.3 session could
not take the exclusive lock the v1->v3 cutover needs, opened the store
read-only, and refused every sub-agent action until the older session
exited.
The blocked cutover now surfaces as a typed SubAgentCutoverBlocked, and
the shared session constructor turns that one case into a
session-private manager: sub-agents work in memory, nothing is written
to the v1 or v3 store, and the older session's files stay byte-for-byte
untouched. There is no forced cutover; the first session that starts
after the older one exits upgrades the store as before. The detail
projection reports SESSION_PRIVATE_COORDINATION, and the TUI shows one
toast that points at Work. The Runtime server constructor keeps its
read-only refusal.
Known limits, stated in the message the user sees: delegated state in a
session-private manager is not saved and other sessions do not see it,
and a session that started private stays private until it restarts,
even after the older session exits.
Verification: the batch's six commits were tested as one change on Boat farm VMs
(Linux x86_64), shipping only `git diff <base> <final>`, so no other
session's uncommitted files took part. cargo test --locked --lib with
filters over config, runtime, engine, app-server, tui and cli on base
3631a174f6: 459 passed, 0 failed (config 8, runtime 117, engine 102,
tui 220, cli 8, app-server 4; farm run
20261107T230833Z-issues-tests-503e01). cargo clippy with the Lint job's
flags (--all-targets --all-features, -D warnings) on config, runtime,
engine, command-contract, cli, tui and app-server: 0 warnings, 0 errors
(run 20261107T231817Z-issues-clippy-e84152). Rebased onto 5ba1aa63bc
(eager sub-agents), which shares subagent/mod.rs, subagent/tests.rs,
tasks.rs and config.example.toml with this batch; this batch's changed
lines are byte-identical on both bases, and a recheck of that overlap on the new base passed: engine 48 and app-server 2 passed, 0 failed (subagent coordination incl. the two-process case, human-wait, execution-guard and tools::tasks suites, server_task_config and server_task_runtime; farm run 20261108T000442Z-issues-recheck-67ab1a).
Locally: cargo fmt --all -- --check, both budget checks,
sync-changelog --check and the feature release-note receipts pass.
Covers coordination_process_lock_rejects_second_process,
which now has a second phase: a real second process holds the shared v1
lock over a legacy store; this session keeps a working session-private
manager whose projection reports the note, the legacy bytes are
unchanged, no v3 store appears, and the next session upgrades once the
holder exits.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Refs #7221 (CW-7804). Pasted clipboard images are written as flat
clipboard-<nanos>.png files under ~/.codewhale/clipboard-images/ and
nothing ever removed them; the reporter's Windows install grew by
several hundred MB in a month of QQ screenshots.
`codewhale sessions prune-images` sits beside `scrub-secrets` and keeps
its shape: report by default, `--apply` to delete. An image is offered
only when no file under the Codewhale state directory, the sessions
directory or the Runtime task store names it (saved sessions,
checkpoints, archives, Runtime threads, the composer stash and history)
and it is older than the grace window (`--older-than-days`, default 7),
so an unsent draft or a running turn keeps its picture. The scan reads
raw bytes and needs no knowledge of each store's schema; it never
follows symlinks inside the roots and stops once every image is
accounted for. If any file or directory cannot be read, nothing is
deleted. A file another process holds open (common on Windows) is
reported and skipped. A missing image never fails resume: the bytes enter
history when a message is built (expand_attachment_blocks), and
re-sending an old message reports the missing file in-band.
The traversal reuses session_secret_scrub's no-follow directory helpers
(now pub(crate)). docs/READ_MEDIA.md documents the command and a
features.toml row registers it.
Known limits: references held only outside the scanned roots (for
example a workspace-local .codewhale directory) are not seen; age-only
pruning stays out of scope; grouping new pastes by session is not done.
Verified (macOS, offline, locked): the batch's six commits were tested as one change on Boat farm VMs
(Linux x86_64), shipping only `git diff <base> <final>`, so no other
session's uncommitted files took part. cargo test --locked --lib with
filters over config, runtime, engine, app-server, tui and cli on base
3631a174f6: 459 passed, 0 failed (config 8, runtime 117, engine 102,
tui 220, cli 8, app-server 4; farm run
20261107T230833Z-issues-tests-503e01). cargo clippy with the Lint job's
flags (--all-targets --all-features, -D warnings) on config, runtime,
engine, command-contract, cli, tui and app-server: 0 warnings, 0 errors
(run 20261107T231817Z-issues-clippy-e84152). Rebased onto 5ba1aa63bc
(eager sub-agents), which shares subagent/mod.rs, subagent/tests.rs,
tasks.rs and config.example.toml with this batch; this batch's changed
lines are byte-identical on both bases, and a recheck of that overlap on the new base passed: engine 48 and app-server 2 passed, 0 failed (subagent coordination incl. the two-process case, human-wait, execution-guard and tools::tasks suites, server_task_config and server_task_runtime; farm run 20261108T000442Z-issues-recheck-67ab1a).
Locally: cargo fmt --all -- --check, both budget checks,
sync-changelog --check and the feature release-note receipts pass.
Covers the session_secret_scrub suite whose
no-follow directory helpers this reuses, and the build of the new
subcommand. The fixture run of `codewhale sessions prune-images` on a
macOS binary follows from the freeze build.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Task wall clock pauses for answerable human waits, landed as
@asto18089's pull request: this merge brings in its head ac9a30319a
(their commits plus GitHub's update-branch merge of main) and the
maintainer follow-up bf73bf8778, which bounds the pause with
`[tools] task_human_wait_cap_seconds` (default 1800 s, at most 86400)
and gates it on the Runtime's HTTP listener. The contributor's fork
refused maintainer pushes, so the follow-up rides in this merge rather
than on their branch, as agreed with the 0.10.3 release session.
Conflict resolution, CHANGELOG.md only: the PR's 0.10.3 Added bullet
and its extended @asto18089 credit are applied on top of main's current
text. crates/tui/CHANGELOG.md is regenerated with
scripts/sync-changelog.sh, which also restores the two entries
4c5426f9bb added to CHANGELOG.md without syncing the slice.
Verification: the batch's six commits were tested as one change on Boat farm VMs
(Linux x86_64), shipping only `git diff <base> <final>`, so no other
session's uncommitted files took part. cargo test --locked --lib with
filters over config, runtime, engine, app-server, tui and cli on base
3631a174f6: 459 passed, 0 failed (config 8, runtime 117, engine 102,
tui 220, cli 8, app-server 4; farm run
20261107T230833Z-issues-tests-503e01). cargo clippy with the Lint job's
flags (--all-targets --all-features, -D warnings) on config, runtime,
engine, command-contract, cli, tui and app-server: 0 warnings, 0 errors
(run 20261107T231817Z-issues-clippy-e84152). Rebased onto 5ba1aa63bc
(eager sub-agents), which shares subagent/mod.rs, subagent/tests.rs,
tasks.rs and config.example.toml with this batch; this batch's changed
lines are byte-identical on both bases, and a recheck of that overlap on the new base passed: engine 48 and app-server 2 passed, 0 failed (subagent coordination incl. the two-process case, human-wait, execution-guard and tools::tasks suites, server_task_config and server_task_runtime; farm run 20261108T000442Z-issues-recheck-67ab1a).
Locally: cargo fmt --all -- --check, both budget checks,
sync-changelog --check and the feature release-note receipts pass.
Covers human_wait*, execution_guard*,
from_runtime_defaults*, supervisor_wall_clock*, unanswerable_surface*,
chained_short_waits* and server_task_config / server_task_runtime.
Refs #7230
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude Pro/Max sign-in used Claude Code's OAuth client, which Anthropic
does not allow other apps to use. The founder's 0.10.3 decision is to
remove it before the tag; the Anthropic route is API-key only again.
Removed: the OAuthProvider::Claude row and its PKCE copy-code login,
the console.anthropic.com JSON token post, Claude refresh, activation,
revoke and generation validation; the engine client's Claude bearer path
(oauth beta header, per-request token refresh, suppressed error bodies,
generation-based route receipt); `codewhale auth claude` and
`claude-revoke` dispatch, TUI `auth claude|claude-revoke`, `/auth claude`
actions, the picker's Claude Pro / Max choice and its login runner; and
the claude-plan-sign-in feature row. The Anthropic descriptor goes back
to `api_key` acquisition with the Console-key guidance, so the
providers-export golden drops Anthropic's `oauth` auth method.
A saved sign-in fails cleanly and nothing deletes it silently:
- `[providers.anthropic] auth_mode = "oauth"` now bails at credential
resolution with CLAUDE_SIGN_IN_REMOVED (the API-key route), readiness
reports a missing key with `codewhale auth set --provider anthropic`
as the fix, and the picker treats the row as needing a key. It never
falls back to ANTHROPIC_API_KEY on its own, so billing does not switch
without the user saving a key.
- `codewhale auth claude` (aliases anthropic, claude-revoke) stays as a
hidden command that prints that guidance; `/auth claude` explains the
removal and points to `/provider setup anthropic`.
- Saving an Anthropic API key no longer deletes Claude credential files
(both the config-crate and runtime save paths did, silently). Only
`codewhale logout`, which removes every saved credential, still clears
them. The credential store keeps recognizing claude-auth-*.json and
claude-oauth.json so existing files stay valid store entries.
Docs: PROVIDERS.md replaces the sign-in section with the removal note
and the table row; features.toml drops the row; CHANGELOG gains a
0.10.3 Removed entry (breaking). The generated web data is re-derived
from the committed sources in a follow-up commit of the same push.
Verification: the batch's six commits were tested as one change on Boat farm VMs
(Linux x86_64), shipping only `git diff <base> <final>`, so no other
session's uncommitted files took part. cargo test --locked --lib with
filters over config, runtime, engine, app-server, tui and cli on base
3631a174f6: 459 passed, 0 failed (config 8, runtime 117, engine 102,
tui 220, cli 8, app-server 4; farm run
20261107T230833Z-issues-tests-503e01). cargo clippy with the Lint job's
flags (--all-targets --all-features, -D warnings) on config, runtime,
engine, command-contract, cli, tui and app-server: 0 warnings, 0 errors
(run 20261107T231817Z-issues-clippy-e84152). Rebased onto 5ba1aa63bc
(eager sub-agents), which shares subagent/mod.rs, subagent/tests.rs,
tasks.rs and config.example.toml with this batch; this batch's changed
lines are byte-identical on both bases, and a recheck of that overlap on the new base passed: engine 48 and app-server 2 passed, 0 failed (subagent coordination incl. the two-process case, human-wait, execution-guard and tools::tasks suites, server_task_config and server_task_runtime; farm run 20261108T000442Z-issues-recheck-67ab1a).
Locally: cargo fmt --all -- --check, both budget checks,
sync-changelog --check and the feature release-note receipts pass.
Covers golden_providers_export_matches_registry,
non_key_and_mixed_routes_are_typed_explicitly, credential_help,
oauth::tests, credential_resolve, provider_readiness, route_receipt,
provider_picker (Anthropic now opens key entry), auth_slash_command,
parses_auth_subcommand_matrix and logout. Not exercised: a live
Anthropic request.
Refs #6932
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The founder-approved 0.10.3 batch from
artifacts/eager-delegation-20261107/FINAL-CHANGES.md, all ten items. This is
one declared re-pin of the session-pinned prefix (docs/CACHE.md): BASE_PROMPT
and several eager tool descriptions change on upgrade, and nothing changes
mid-session.
1. BASE_PROMPT gains "Delegation and goals". Independent parts of a request
go to `agent` children started together in one message. Results arrive on
their own, so never poll or sleep, and end the turn when only waiting
remains. Each child gets a complete prompt and a disjoint file scope; the
parent inspects a write-capable child's evidence, reviews the final diff
and runs the checks. An independent reviewer tries to refute a
non-trivial change before it is called done. `create_goal` is for any
direct request, in any language, that describes a verifiable end state
needing more than one turn. A goal is blocked only when the same concrete
obstacle persists; difficulty is not blocked. Cost alone is not a reason
to stop, and only limits the user or configuration sets apply. The scope
line keeps express authorization for spending real money (purchases, paid
providers, billing) and adds the founder-approved sentence "Sub-agents,
workflows and goals within the current request are in scope."
GOAL_CONTINUATION_PROMPT drops the stale tool_search sentence (update_goal
is eager), asks for remaining independent work to be split across agent
workers before the next verification pass, and uses the same blocked rule.
2. The engine no longer appends the Operate contract (record_mode_context).
Its reviewer, cost and write-child-evidence lines now live in BASE_PROMPT
for every session. Every saved wording stays recognized, so restored
sessions replay it as internal state and compaction still keeps it.
3. The agent start receipt carries `next`: "Results arrive automatically;
continue other work or end your turn."
4. The agent description leads with intent ("Delegate focused work and keep
working. Start early; one agent per independent part, same message."),
gives one trigger per role, adds a child-prompt brief with one worked
example, names `workflow` and `Run` as the real tools, and drops the
"In Operate" and "Legacy action=" sentences. The schema is unchanged.
5. create_goal, its token_budget property and update_goal (description and
status property) say when to start a goal and what complete, blocked and
yield mean, without judge jargon. The status enum and parser are
unchanged, and the token_budget parameter stays.
6. The bundled goals skill (personal resolutions in goals.md) becomes
personal-goals with `aliases-for: goals`, so saved references still
resolve and the skills index no longer reads like goal mode. It keeps the
generation-12 introduction, so a user who deleted goals does not get it
back. On upgrade, an untouched goals copy (a shipped body by hash,
SKILL.md its only entry, and no personal-goals directory yet) is renamed
to personal-goals and then refreshed by the ordinary generation-20
upgrade, because its body hash is listed under personal-goals. An edited
or extended copy stays where it is. Known limitation: a
`[skills.bundled]` entry that names "goals" now reports as unknown and
does not apply to personal-goals.
delegate no longer forbids delegating final verification and treats the
roster as optional; best-of-n uses the general and implement roles and
drops "In Operate mode" and the 16-worker figure. v4-best-practices is
not changed: it is a cfg(test) v4 migration fixture, not a bundled skill,
so editing it would falsify the fixture and reach no user.
7. The tasks schema stops advertising mode=operate; the hidden legacy alias
schema and the free-string parser still accept it. harness no longer names
the hidden rlm tool (description and overview), list_dir loses its
harness meta sentence, and TOOL_SURFACE.md (and zh_hans) drop Operate.
goal.rs's `Run action="verifiers"` names a real deferred tool, which a
well-formed first call activates, so it stays.
8. Workflow max_concurrent defaults to 64 (was 16), matching sub-agents. The
workflow-js per-run clamp WORKFLOW_MAX_CONCURRENT moves from 16 to 128,
the sub-agent hard maximum; without it the clamp would have kept every run
at 16 and explicit configs above 16 would not take effect. No spend caps
are added, and the high-budget approval trigger is untouched because the
default token budget is 0. Docs and config.example.toml follow.
9. Each child's task message ends with its name, its scope (read-only, or
may edit files within its assignment) and "Your final answer is delivered
to the parent automatically; include the result and its evidence." This
is the child seed built by the runner in subagent/engine.rs, beside the
role prompt that carries SUBAGENT_OUTPUT_FORMAT; the role prompts that
children share are unchanged.
10. tool_search lists the commonly registered deferred groups (Git, Run, Web,
tasks, github, automation, lsp, review, session_search, terminal/*,
apply_patch, grep_files, MCP) and says a listed tool can be called by
name. Memory tools are opt-in, so they are not listed. Generating the
list from the live catalog is the next-patch item.
Bytes and cache:
- /preview-request before, installed 0.10.3 (7b8a634, catalog-equivalent
to main) on macOS with an isolated offline HOME: tool schemas 40,854
canonical JSON bytes, system 16,662, base prompt 3,336, 18 active of 59.
- /preview-request after, a debug build of this diff on Linux (Boat farm)
with the same isolated offline HOME and prompt: tool schemas 40,173
canonical JSON bytes (-681, eager bytes did not grow), base prompt 4,352
bytes.
- Source delta on eager tool schemas: -681 bytes (agent 2,370 -> 1,737,
create_goal -244, update_goal -129, tool_search 71 -> 396). BASE_PROMPT
grows to 4,352 bytes in the system region.
Tests changed with the behavior: prompts.rs
universal_prompt_does_not_expose_control_plane_ceremony no longer forbids
"sub-agent" and "dispatch, join" (the founder reversed that decision) and
still forbids sentinel and runtime-event jargon; the deleted
agent_tool_description_states_the_operate_child_approval_rule pinned the
dropped Operate sentence; the tasks enum test, the workflow default tests,
the Operate probes (now 0 contracts) and the delegate generation-18
reconstruction chain follow; the skills fixture moves to generation 20, with
the old delegate and best-of-n bodies and the old goals body (under
personal-goals) listed by hash. The prompt and event
conformance goldens are re-recorded for the re-pin.
Verified on the Boat farm (Linux x86_64), base 4c5426f9/f0fb6adb plus only
this diff (engine.rs, subagent/mod.rs, subagent/tests.rs, tasks.rs and
config.example.toml shipped as HEAD plus this batch's hunks):
- cargo test -p codewhale-engine --lib, focused filters (prompts, goal,
tasks, harness, canonical actions, agent description and parent surface
ceiling, lifecycle, launch receipt, assignment prompt, limits, compaction
last round, tool catalog, runtime handoff, registry, workflow): 533
passed, 0 failed. The final state on base f0fb6adb, same filters
plus core::engine::tests::operate_ and mode_notice: 539 passed, 1 failed.
The failure, create_mode_enum_advertises_neither_operate_nor_yolo, came
from the farm copy of tasks.rs missing this commit's third hunk; the
first run carried exactly the committed tasks.rs, and the test passed.
- cargo test -p codewhale-skills: 182 passed, 0 failed, on the final rename
migration; a throwaway migration check (not committed) covering untouched,
edited, extended, deleted and fresh installs: 5 passed.
- cargo test -p codewhale-config --lib workflow_config: 3 passed;
cargo test -p codewhale-workflow-js: 12 + 82 passed.
- cargo test -p codewhale-tui --lib conformance::prompt::
conformance::events::, update pass then compare pass: 5 passed each; the
re-recorded goldens come from that run on base cd7f5b32 (conformance tool
schemas 19,840 -> 19,792 bytes, system +1,033 bytes, matching the source
delta); nothing that landed since touches the surfaces they capture.
- cargo clippy -p codewhale-engine -p codewhale-skills -p codewhale-config
-p codewhale-workflow-js --all-targets --all-features with the Lint job's
flags: clean; skills clippy re-run on the final migration: clean.
Local: rustfmt clean on every touched file; scripts/preflight.sh --check
passes except the blocking-calls budget, which fails only on another
session's untracked crates/engine/src/session_image_prune.rs (4 > 0), not
part of this commit; dead-code budget 137 of 137.
Not verified: a macOS run of the after binary (the before figure is macOS,
the after figure Linux; the measured -681 equals the source delta), any
live provider turn, and DSH's sleep/poll probe on the target models.
Refs: CW-7852
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Step 2 said a user-visible feat or fix commit carries a line and that
docs need none. The changelog rule counts user docs, help text,
defaults and security behavior as user-visible, and public lines cite
a public issue or PR, never an internal tracker id. The step now says
so: a user-visible change (usually feat or fix) carries its line,
`(#N)` names a public issue or PR, and tests, CI, refactors and
internal docs need no line.
Verified: the edited paragraph reads cleanly in context; no other
public skill restates the rule. No CHANGELOG line: maintainer
documentation.
Refs CW-7860
Co-Authored-By: Claude Opus 5.5 <[email protected]>
docs/ISSUE_TRIAGE.md pointed at an "agent-ready issue standard" in
codewhale-ops that does not exist; only old evidence copies of this
page mention it. The standard in practice is the public Agent task
issue form (.github/ISSUE_TEMPLATE/agent-task.yml), which applies the
agent-ready label and requires a goal, a plan by path, done-when
outcomes and verification commands for an agent with no other context.
Verified: the relative link resolves from docs/. No CHANGELOG line:
contributor documentation.
Refs CW-7860
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Three named passes: correctness against the linked issue (intent,
defects, contracts, the AGENTS.md invariants), security (trust
boundaries, secrets, sandbox, execpolicy and the approval layers,
network policy, dependencies) and contract and compliance (CHANGELOG
line per the cw-land step, feature registry, localization, vocabulary,
and a table of which existing scripts to run for what a change
touched).
Severity reuses the blocker, concern and nit levels and the reproduced,
traced and suspected evidence tiers that codewhale review already
emits (crates/engine/src/tools/review_core.rs), so a person's review
and the tool's read the same. Nits are grouped into one list; there is
no numeric cap.
Review runs by hand and at the frozen release candidate, never as a
required check, and for contributors it is advice that never blocks,
labels or closes a pull request.
Verified: every path, link and script it cites exists at this commit.
No CHANGELOG line: contributor process documentation, not a
user-visible change.
Refs CW-7860
Co-Authored-By: Claude Opus 5.5 <[email protected]>
.codewhale/hooks.toml adds two tool_call_before hooks backed by one
policy script, scripts/agent-guard.py:
- Shell guard: denies git add -A, --all, -u, a dot, .., a star or the
repo root; git commit -a or --all; and add or commit pathspecs naming
.env files (templates ending .example, .sample or .template stay
allowed), private keys (pem, key, p8, p12, pfx, jks, keystore, ppk,
ssh identities) or target/, node_modules/ and .mimosa/ paths (a
fixtures folder is exempt for build folders). It reads chained,
subshell, bash -c and eval forms and skips heredoc bodies, quoted
messages and comments.
- Edit guard: opt-in release freeze. With CODEWHALE_RELEASE_FREEZE set,
edits to .github/workflows/release*.yml and auto-tag.yml through the
file tools, apply_patch or mutating shell commands are denied; reads
stay allowed.
Every deny names the rule and the way forward, worded to pass the
Engine's 240-character denial redactor unchanged.
The hooks file pins the script's SHA-256, so approving the hooks digest
approves the script too: a changed script fails the pin and never runs.
Both hooks keep continue_on_error = true, so a guard that cannot run
(no python3 or shasum, a Windows cmd shell) reports no verdict and
never blocks work. Nothing runs until the person trusts the workspace
and approves the digest (/hooks review, then /hooks approve).
The script also answers Claude Code's PreToolUse protocol (JSON on
stdin, exit 2 with the reason on stderr), so wiring it there later
adds no second policy.
Verified: 77 of 77 block and allow cases (both protocols, freeze on and
off, truncated arguments) in a scratch harness that runs the script
with the Engine's hook environment; all 18 distinct deny reasons pass a
replica of sanitize_hook_denial_reason unchanged, worst case 221
characters; the exact hooks.toml commands run through sh -c, and a
tampered copy of the script exits 1 without running; tomllib parses
the file with only Hook fields. No Rust changed. No CHANGELOG line:
this is repository tooling, not a user-visible change.
Hooks digest: f4112f65c9e5f389a211d4cfb08414de4a7e516096e912afd038314d907d5b1e
Refs CW-7860
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`GET /v1/apps/mcp/servers` now carries `last_error` for a configured server
that is in connection backoff, truncated to 2,048 characters, so clients can
say why a server is not connected instead of only showing it as disconnected.
`McpPool::last_connect_error` returns the error only while the same
configuration is the one in backoff (runtime-added flag, reviewed plugin
source and runtime config hash unchanged); runtime-added servers report none.
docs/RUNTIME_API.md documents the field.
Fleet member starts are keyed by (state namespace, operation key), so a
replayed start in one session can no longer resolve to a member that another
session started under the same operation key.
Recovered from stranded drafts (the readiness MCP last-error patch, the MCP
pool accessor and release-engine 9cb914b617) and rewritten against the current
has_connection_backoff.
Verified: the five runtime_api MCP management tests pass (5 passed, 0 failed:
crud, redacts_credentials, project_ownership_blocks_global_shadow_mutations,
blocks_credential_retargeting, revision_precondition_prevents_stale_mutation),
run on the shared tree with other sessions' uncommitted edits present;
clippy on codewhale-engine and codewhale-app-server libs was clean earlier
today; the patch applies to HEAD cd7f5b32b1 and every item it references
exists there. No new tests.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Fold the desktop-contracts session's lines for 9805a009e8 into 0.10.3:
the Changed entry (called out as breaking for Runtime API clients, since
`/v1/terminal/{name}/*` now answers 410 and `/v1/runtime/info` replaces the
four `terminal_*` flags with `thread_scoped_terminals`) and the terminal
lifetime known limit. Other sessions' uncommitted CHANGELOG text and a
staged skill rename in the shared index are left untouched (committed from
a private index).
Verified: scripts/sync-changelog.sh --check up to date on the committed
pair.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
CW-7669. Engine terminal routes addressed a live PTY by a reusable name
looked up against the server's root workspace, so a worktree thread's pane
reached the root's same-named shell, and two conversations using `term-1`
in one folder shared one shell (cwd, running command and output crossed
between them). Reconnect and restart semantics were undefined.
Engine owner (crates/engine/src/tools/terminal_session.rs):
- The registry key is (canonical workspace, owner, name); the owner is the
tool context's state_namespace, which a runtime thread's engine sets to
the thread id (runtime_threads/manager.rs session_id: thread.id), so each
conversation gets its own shells; sub-agents share their parent's.
- The issued session_id is stored beside the handle in the registry, so an
identity resolves without taking a session lock a blocked write holds.
lookup(workspace, owner, id) replaces lookup(name, workspace).
- get_or_create no longer holds the registry mutex while it waits on an
existing session's lock, so one wedged shell cannot stall every thread.
- write_human_input is the one human-input path for the TUI dock and HTTP:
identity, sandbox-reuse guard, exited shell, and the confirmed ETX
interrupt the dock already used. HumanInputError keeps the dock's strings
and lets HTTP map statuses.
- terminal/reset metadata reports terminal_session_id and
previous_session_id. Durable summaries hash the owner too.
Runtime API (crates/app-server):
- GET /v1/threads/{id}/terminals lists the thread's live shells with the
registry instance; /v1/threads/{id}/terminals/{terminal_id}/
output|input|resize|kill drive one by identity, mirroring the thread jobs
routes. Every call re-resolves the thread's recorded workspace
(thread_workspace split out of scoped_workspace). A reset, foreign-thread,
foreign-workspace or pre-restart identity is 404 terminal_not_found;
nothing substitutes a same-named shell. Input needs the thread's shell
consent and loaded shell policy plus the sandbox-reuse guard against
thread_job_sandbox_policy (the derivation a turn uses). Kill reports
killed:false for an exited shell; output adds gap. Lookups and PTY work
run on the blocking pool (previously open_session locked the registry
inline on a runtime worker).
- /v1/terminal/{name}/* answers 410 terminal_route_retired on every
platform. runtime/info advertises thread_scoped_terminals; the four
terminal_stream/input/resize/kill flags leave the protocol
RuntimeCapabilities (absent reads as false, so an older client hides
Engine terminals instead of reaching the retired routes). A compatibility
window was rejected: the old routes carry no thread, so answering them
means choosing a shell by name, which is the defect. JSON schema, the
TypeScript declaration and the SDK declaration are regenerated.
- docs/RUNTIME_API.md (and zh_hans) define reconnect, reset, exit, Engine
restart, unconfirmed interrupt and uncertain-input semantics and the
known limits, including that deleting a thread does not end its shells.
TUI dock: human terminals are created under the current session's owner;
resize resolves by identity through resize_dock_session.
Behavior change: in the TUI, a new conversation starts its own term-1
instead of reusing the previous conversation's shell (the dock still lists
both). The community VS Code extension's client calls no terminal route.
Ratchets: blocking-calls budget tightened for terminal_session.rs only
(path_canonicalize 3 -> 1, from the canonical() dedup); dead-code budget
unchanged at 137/137.
Evidence (local, -j6, hermetic test home; source at commit time):
- codewhale-app-server-schema --write: exit 0; the JSON schema and both
TypeScript declarations change only by the capability swap.
- cargo test --locked --offline -j6 -p codewhale-app-server-protocol
--features schema: lib 10 passed; tests/schema.rs 2 passed (includes
committed_runtime_schema_and_sdk_exports_match_one_rust_contract);
tests/wire.rs 3 passed; 0 failed.
- cargo test --locked --offline -j6 -p codewhale-engine -p
codewhale-app-server --lib -- terminal: app-server 21 passed, 0 failed
(the 4 terminal.rs unit tests, terminal_routes_serve_a_live_engine_
session_over_http, terminal_output_for_an_unknown_session_is_not_found_
and_creates_nothing, runtime_info_advertises_terminal_capabilities);
engine 146 passed, 0 failed (all 25 tools::terminal_session tests,
including session_names_are_scoped_to_owner_and_workspace and
dock_never_attaches_to_a_reset_name_without_selecting_its_new_identity).
- cargo check --locked --offline -j6 -p codewhale-tui --lib: the engine
compiled; the TUI crate stopped on one error outside this change
(commands/groups/config/status.rs:325, another session's in-progress
Accept Edits work), so the dock edit is type-checked but not
borrow-checked. Its only borrow-relevant line was then reduced to the
call shape that compiled before (owner computed first).
- rustfmt --check on each changed Rust file; check-blocking-calls-budget
and check-dead-code-budget run.
Tests ran on the shared working tree, which also held other sessions'
uncommitted edits; this commit carries only this slice.
Not verified: no packaged build, no live desktop pane (needs the App
adoption and the 0.10.3 repin), Windows/ohos stubs not compiled here.
Refs CW-7669
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Release candidate runs 42381818665 (549ff01ebb) and 42408096045
(b175b1f9ed) both lost the linux-arm64, android-arm64 and ohos-arm64 dist
builds to "The runner has received a shutdown signal" (exit 143) 20-40
minutes into the quiet compile of the large crates, with no compiler,
linker or disk error. linux-x64 on the same ubuntu-latest image succeeds,
and 0.10.2 shipped these targets, so the dist profile (fat LTO, one codegen
unit) on a grown workspace exhausting 16 GB runner memory is the likely
cause. The three Linux aarch64 legs now add an 8-16 GB swapfile on the
largest writable disk before building; the shipped binaries are unchanged.
The next release-candidate run is the proof.
Also fixes two workflow tests my CI slim-down (99cb5a0f4f) made wrong:
release-workflows.test.js now expects the npm wrapper smoke to be manual
only (Ubuntu + Windows on dispatch), and agent-task-metadata.test.sh no
longer requires the removed agent-task-labels.yml but guards that it stays
retired. The stale change-detection path for that workflow is dropped.
Verified: node --test .github/scripts/release-workflows.test.js 1/1 pass;
d8-executable.test.js 4/4 pass; agent-task-metadata.test.sh pass;
actionlint with CI flags passes on ci.yml and release-artifacts.yml.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Founder direction (2026-11-07): remove CI/CD steps that cost time without
need, use CI conservatively, and make contributor rules standard and
welcoming instead of closing or blocking PRs over CI. A read-only audit of
two weeks of run history (Sonnet workflow with an adversarial release-
engineer critique) drove the list; nothing release-critical is touched.
Removed (enforced nothing, duplicated other checks, or gated contributors):
- dco.yml (always exited 0), pr-issue-link.yml (red X on ~8% of contributor
PRs), auto-close-harvested.yml, pr-gate.yml / issue-gate.yml /
approve-contributor.yml and .github/APPROVED_CONTRIBUTORS (dry-run
allowlist gate; drops a pull_request_target surface), agent-task-labels.yml
(duplicates triage.yml).
Moved to manual (workflow_dispatch) only:
- codewhale-review.yml (review bot; AGENTS.md says review bots stay off),
main-landing.yml (push trigger; run scripts/preflight.sh --check locally),
ci.yml test (Linux suite, 54-94 min per PR), safety-gate (18-29 min), msrv,
mobile-smoke, npm-wrapper-smoke (macOS leg dropped from the dispatch matrix;
the release artifacts cover macOS).
- The PR feature release-note receipt step is advisory (continue-on-error).
- spam-lockdown keeps only unambiguous spam patterns.
Unchanged: Lint and the compile-only Test (windows-latest) stay the
required PR checks; release-candidate, release-artifacts, release-parity,
release, the mirrors, OHOS and the Windows import guard are untouched. The
branch ruleset drops "npm wrapper smoke (ubuntu-latest)" next (founder
approved "Lint + 1 compile").
Rules: AGENTS.md, CONTRIBUTING.md, the PR template, AGENT_ETHOS.md and the
cw-land skill now say contributor PRs are never closed or blocked because
CI is slow, flaky or red for a reason they did not cause; nothing closes a
PR automatically; harvesting is a rare, asked-first fallback with the
contributor as author; the PR issue line is optional; maintainers reply
generously. web/data/agent-docs.generated.json is regenerated from a clean
export of main plus this change only.
Verified: actionlint with CI's flags (-ignore SC2129/SC2221/SC2222) over all
workflows passes; web/scripts/check-facts.mjs passes on the isolated export;
only the contribution entry of agent-docs.generated.json changed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Root asked for every change landed today to have a 0.10.3 note. Three
landed without one: 91fd65705a (stages appearing during a registry load
now take the restage checks), 67de548a97 (Windows import floor: ARM64
pairs qualified by the native probe, x64 pairs recorded as documented
contracts) and 549ff01ebb (terminal-clipboard harness BOM and cleanup
fallback, vendored kit lock). Lint, style, test-table, ratchet and website
docs commits change no product behavior and get no entry.
Only these three lines are committed; another session's uncommitted
CHANGELOG text stays in the working copy.
Verified: scripts/sync-changelog.sh --check up to date on the committed
pair.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The codewhale.net hero now tells the living mark's story with its existing,
owner-approved pieces: the flat C-curl mark (drawn by the vendored kit's
WhaleRig.build("mark") in brand navy #142352, reversed on dark) holds for
700 ms, the live canvas whale fades in as the colored-in whale for 2.4 s, then
64 beads of light assemble head first and keep breathing, swimming, hovering
and carrying a soft light crest at 12 paints per second. Pointing at it, or
picking a terminal view (the existing cueWhale path), brings the whale back;
it returns to lights 600 ms after the pointer leaves or 15 s after a pick. The
intro plays once per browser session. The canvas whale sleeps (no frames) while
the lights show; the lights pause when off screen or hidden. Under reduced
motion the hero keeps the still whale and draws no lights. Before the client
decides, the hero is hidden briefly so the character no longer flashes before
the mark; without JavaScript it appears after 1.2 s.
The beads come from web/vendor/whale-lights/whale-lights.json, an unchanged
copy of codewhale-app assets/whale-lights.json at 55c9ba0c (SHA-256 recorded),
the same file crates/tui-kit pins for the terminal. web/lib/whale-lights.ts
restates the app's drift, crest and seeded-star formulas (about 60 lines)
because the app source is private (CURRENT_DECISIONS 44); PROVENANCE says to
change the three together. lib/whale-motion.ts exposes the vendored WhaleRig.
Verified: tsc --noEmit clean; ESLint clean on the five changed TS/TSX files;
vitest 74 files, 770 tests passed (design-grammar contract included, which
moved the new timings onto the spring tokens and the fallback keyframe behind a
motion preference). next dev on 3241 in the browser pane and in Electron's
Chromium via Playwright: stages mark (0.3 s) -> whale (0.9-1.7 s) -> lights
(3.3 s+), beads move between reads, no console errors; with reducedMotion
reduce the stage stays whale, lights are display:none and nothing moves.
Not deployed; codewhale.net is unchanged until a founder-authorized deploy.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Refs #7233. f230a76b2b left the new four-row loop on one 101-column line;
the Main landing gate's rustfmt step flagged it. Formatting only.
Verified: rustfmt --edition 2024 --check clean on crates/net/src/proxy.rs,
crates/runtime/src/oauth.rs and crates/runtime/src/oauth/test_support.rs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Refs #7233 (CW-7824). 832ac8544b already keeps malformed HTTP(S)_PROXY
values out of the MCP warning and makes redact_proxy_userinfo return
"<invalid proxy URL>" for input without a scheme. The existing redaction
table now carries the reporter's four unschemed rows (a space in the host,
a non-numeric port, an unclosed IPv6 bracket, an empty host), assembled
from the placeholder credentials like the rest of the table, and asserts
the password never survives.
Verified (macOS, offline, locked): cargo test -p codewhale-net --lib
redact_proxy_userinfo_strips_password 1 passed, 0 failed (72 filtered).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Refs #7203 (CW-7800). 0.10.2 decoded the ChatGPT ID and access tokens
with zero leeway against the device clock and reported every rejection
as "token signature or identity claims failed verification". A device
clock about 15 s behind auth.openai.com, as the reporter measured, makes
a fresh token's nbf look future-dated, which is the only rejection
consistent with the report (key download and unknown-key failures had
their own messages). 8a37a91312 named the failed check and 4ca5880be8
allowed 60 s of skew; a larger skew still failed sign-in.
Expiry and not-before are now evaluated against the issuer's clock. The
Date header of the TLS response that delivers the issuer's keys, read at
sign-in and on every refresh, gives the offset from the device clock;
the 60 s tolerance stays, and without the header the device clock is
used as before. Trusting it adds no trust: whoever could forge that
response could forge the keys. Time errors say they were judged by the
issuer's clock and report the device's difference beyond the tolerance.
A token whose kid is missing from the first key download refetches the
keys once before failing (rotation, stale edge cache); the key download
already shares the token exchange's client, TLS roots and proxy rules,
and its failure keeps its own message. A missing or malformed sub,
client_id or scope claim is named. Signature, issuer, audience, subject,
nonce, account and plan-scope checks stay exact, and no claim value
reaches an error. iat is not validated (jsonwebtoken never checks it).
chatgpt_account_id is display-only on this route (the registration binds
sub), so it cannot fail sign-in.
Docs (EN/ZH PROVIDERS) describe the issuer clock and the key refresh. The
existing changelog bullet is amended, the 0.10.3 known-limit bullet stays
because no Windows sign-in receipt exists, and @d-onistack is credited on
the changelog, CONTRIBUTORS, release-credits and public-surface facts;
the embedded changelog and agent docs are regenerated (the trust and
vocabulary agent-doc entries were stale on main and refresh with them).
Known limits: token refresh scheduling (token_is_expired) still uses the
device clock; not reproduced on Windows.
Verified (macOS, offline, locked; existing tables extended as directed):
runtime SIWC cases 5 passed, 0 failed (929 filtered), covering ±15 s
accepted on the device clock, ±90 s rejected on the device clock and
accepted with the matching issuer offset, a 900 s difference reported in
the message, unknown-key refetch exactly once (success and failure),
known keys fetched once, and named client_id/scope claims; engine refresh
case client::tests::siwc_refresh_rotates_atomically_and_rejects_a_different_account
1 passed, 0 failed (7101 filtered); cargo clippy --all-features -p
codewhale-runtime -p codewhale-net --lib --tests with CI's -D warnings
and allow list: 0 warnings; scripts/sync-changelog.sh --check up to date;
web check-facts OK.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The early hosted CI run on b175b1f9ed (run 42408093869, job Lint) failed
the #6149 blocking-calls ratchet: eight file budgets were exceeded by
sites added in 821c478eb9, c49fd2c331 and 91fd65705a without updating
the budget. Each site was checked for an inline async caller:
- runtime_api/jobs.rs: resolve_job_cwd / canonical_job_path are sync
helpers whose only callers run inside spawn_blocking (jobs.rs 665, 711)
or tests.
- runtime_api/plugins.rs: ui_root_matches / prepare_plugin_ui run inside
the spawn_blocking blocks at 1279 and 1327.
- paths/lib.rs and paths/daemon_socket.rs: root-owned symlink ancestor
resolution for the daemon socket path, synchronous startup code.
- plugins/marketplace/document.rs and plugins/registry.rs: synchronous
library code in the reviewed-plugin registry and marketplace reader.
Only those eight entries are raised, using counts from a clean export of
origin/main, so uncommitted work in the shared checkout cannot skew them.
Optional tightenings (subagent/mod.rs, file_frecency.rs,
private_directory.rs) are left for the owners' next --update.
Verified: check-blocking-calls-budget.py exits 0 against a git archive
of origin/main with this budget.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Maintainer follow-up on @asto18089's #7230, landing it as their PR.
- Apply the maintainer patch from the review thread: `[tools]
task_human_wait_cap_seconds` (unset or 0 -> 1800 s, clamped to
1..=86400) feeds `TaskExecutionLimits::human_wait_cap`, the default
cap drops from 24 h to 30 min, docs/CONFIGURATION.md and
config.example.toml document it, and the blank line after the
`drive_engine_turn` doc comment is gone (clippy
empty_line_after_doc_comments).
- A wall deadline that has already passed is no longer skipped:
`begin_human_wait` declines to open a window once the wall deadline
(moved only by closed windows) is behind it, so the guard ends the run
with WallTimeout instead of letting the human answer and then failing
the task right after. The deadline arithmetic lives in one helper
shared with `evaluate`, which also drops an always-zero open-window
term from the no-window branch. Idle crossings are left alone on
purpose: #6118 suspends idle while an approval is pending, and the
worker supervisor lets queued executor progress win an idle race, so
refusing a window on an idle crossing would turn a starved event
channel into a spurious IdleTimeout.
- Answerable waits require a Runtime that binds its HTTP listener:
`server_task_config` takes `http_decisions` and `run_http_server`
passes `!local_control`. Local Stdio, Socket and ACP control frontends
bind no listener, and the control transport refuses user-input
replies, so their tasks keep the unanswerable default.
- The two existing server_task pins pass the flag; the config pin also
asserts the local-control case stays unanswerable.
- Merge current main first so the #5898 release-note receipt (dropped
from main's changelog after the earlier branch update) is re-added in
the 0.10.3 Added section, with the contributor credit extended.
Evidence: all 13 existing ExecutionGuard unit tests, extracted verbatim
with the guard code into a scratch harness, pass on both the previous
head and this one; the same harness shows the reported ordering (wall
400 ms, prompt at 500 ms) returning Run before and WallTimeout after.
rustfmt clean on the touched files. Full crate builds and the async
task tests are left to this PR's CI.
Refs #5898
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Fixes the remaining hosted test failures on 549ff01ebb (CI 42381817495).
Runtime: a host-provided route with no preflighted client took its config
with mem::take before the optional credential preflight, so a failed
preflight left the route with a default config and lost its session
isolation and approval deadlines. It now clones, so the configured route
survives a failure. The existing privacy/timeout assertions are unchanged.
Exports: the four capabilities the runtime already serializes
(thread_scoped_git_writes, thread_scoped_snapshots, worktrees,
job_operations) are restored to the committed JSON schema, the TypeScript
declarations and the runtime SDK copy; the JSON matches the Rust-generated
contract.
Existing expectations realigned with shipped behavior: `present` is an eager
core tool (hook category stays other) in the five tool-surface tests; Native
extension entries accept .cjs and .cts; account logout is account-only and
keeps provider keys (two TUI tests renamed to say so). The router HTTP-status
unit test disables retry because it mounts a single response; runtime retries
are unchanged. THIRD_PARTY_NOTICES restores the semver 7.8.5 ISC text the
bundle-notice check requires. Both changelogs record the fixes.
Verified on this Mac: the 13 hosted failing cases, each run by exact name and
each executing exactly one test, 13/13 pass (RUST_MIN_STACK=8388608, hermetic
test home, locked/offline/-j1); clippy --lib --bins --all-features over 11
packages exit 0 with zero diagnostics; runtime SDK tests and strict TypeScript
passed earlier on these same bytes (exit 0); rustfmt --check and git diff
--check pass. The full suite runs in the final hosted validation.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Hosted CI Lint on 549ff01ebb (run 42381817495) failed on collapsible_if in
the plugin registry and a needless borrow in the account session; a later
local all-target run found two more nested conditions in the App Server and a
question_mark case in the thread manager.
- plugins/registry.rs: let-chain for the state-file identity check; the
same_file guard and its error are unchanged.
- account-cli/session.rs: drop the needless borrow passed to
validate_auth_bundle.
- app-server runtime_api/jobs.rs and plugins.rs: let-chains with the same
lock scope, short-circuit order and eviction behavior.
- runtime_threads/manager.rs: `.await?` for the thread.started emit; the
same error is returned before the thread is handed back.
Verified on this Mac with the next commit's files in the tree: clippy
--lib --bins --all-features over the 11 affected packages (CI's three
allowances, -D warnings, locked/offline/-j1) exit 0, zero diagnostics, 5m59s;
rustfmt --check and git diff --check pass. Test targets are linted by the
final hosted run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Release candidate run 42381818665 on 549ff01ebb stopped the Windows import
guard on 12 x64 and 10 ARM64 pairs, every one from a DLL the profiles already
listed.
ARM64: existing windows-arm64-export-probe.yml run 42389141087 on Windows 11
ARM64 26200.9725 resolved 444/444 imports of that candidate's executables
(sha256 38558ab2...). Only the 10 probe-resolved pairs were added (460 -> 470);
the probe receipt replaces the 2026-10-16 qualification fields.
x64: no native x64 export probe exists. The 12 pairs are recorded as
namedApiContracts with Microsoft documentation sources, all below the unchanged
14393 floor, and labelled separately from the #6953 reporter-observed exports
(456 -> 468). Floors, guard and x86 profile are unchanged.
Verified: the checker's own parse_imports over the retained llvm-objdump
outputs for all six candidate executables (3 x64 at 452 pairs, 3 ARM64 at 444)
leaves zero unqualified pairs. The executables were not kept locally, so the
checker was not rerun on the bytes; the next frozen candidate's RC import job
is the real check. Not a 14393, installer or startup run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Strip Windows PowerShell's UTF8 BOM before parsing normal cleanup
receipts. Run the existing bounded exact-private-path cleanup fallback
on failures even when its controller has already exited, verify its
returned path and zero remaining Terminal processes, and retain the
original failure result.
Use Cargo's conservative offline update for the standalone vendored
kit lock: app-server-protocol0.10.3 and its declared chrono edge. Root
Cargo.lock and runtime Rust sources are unchanged from4ba0c656ac9c.
Verified on the frozen4ba+these2paths: existing standalone kit themes
14passed/0failed; surfaces3passed/0failed, both locked/offline with
source unchanged and disk guard retained. Node syntax and diff checks
exit0. Independent review confirms both Windows findings are closed.
Actual PowerShell/Windows execution remains pending finalCI.
Mac4ba CLI build and strict HTTP admission/recovery observations passed;
Linux4ba-equivalent CLI build passed with full4270source invariants.
Those executables retain their exact4ba identity and scope; this commit
is not a claim that they were rebuilt from the later script/kit-lock
metadata. No publication or deployment.
Refuse converted DSH client UI before minting asset grants, using the
existing reviewed conversion bytes. Keep Native and MCP activation paths
unchanged; unmodified DSH command UI remains unsupported. Correct the
converter and compatibility notes to match the enforced behavior.
Reuse codewhale-ratatui8311b65 design tokens5.0 and raised-panel contrast
handling in the existing vendor, preserving Engine surface and motion
behavior. Replace the Windows clipboard simulator with the existing
Ctrl+G draft stash and exact24-line Unicode comparison. No inference or
Enter is used; send-once and provider delivery are outside that check.
Synchronize embedded changelog and existing feature projections; format
the already-landed registry reconciliation expression.
Verified before freeze: cargo fmt --all -- --check; version contracts
(24 linked issue references); OHOS target dependency/bindgen contracts;
web check:facts; Node syntax; git diff --check, all exit0. Independent
read-only UI admission review found no blocking defect. The earlier
147-equivalent Linux source passed all6 selected existing cases,1case
per command,0failures; original failed Node-shim result is retained.
Native compilation, kit checks and HTTP refusal observations for this
new frozen batch, plus real Windows acceptance, remain pending. No new
product tests/comments, source push, CI dispatch or publication here.
Reuse onboarding_missing_key_recovery to keep the optional working-agreement checkpoint from covering the connection chooser for initial CLI input. Manual agreement setup, configured entry behavior and runtime permission gates remain.
Update the changelog with narrowly qualified Mac recovery, official filesystem MCP reconnect and published Code Review Native status/revocation evidence.
Verified: exact two-file diff, git diff --check and Rust syntax parsing. On source91fd, actual initial --prompt capture reproduced the obstructing checkpoint; typed draft/cancel, staged recovery/auth refusal, official MCP catalog/reconnect/revoke and published Native activation/status/revoke were observed separately. New guard runtime proof awaits the fresh stamped build; commit held locally until that passes.
Route an initially unstaged plugin whose destination appears concurrently through the existing restage writer lock, current receipt and policy checks. Retain refusal and the existing failure cache for invalid bytes. No new authority or recovery mechanism.
Verified: exact one-file diff, git diff --check, Rust syntax parsing and independent review. Fresh compile and runtime qualification pending; held locally for a stamped qualification build, not pushed.
Reuse npm semver7.8.5 inside the existing detached reviewer, including the
pinned upstream prerelease policy. Require its exact compatibility decision
before import and resolve a selected package's own ESM export through existing
contained-path guards. Preserve the existing unresolved-sibling refusal.
Stop deriving a lock-owner PID from stale lockfile bytes. Reuse coordination
projection and event-keyed notices to report each refusal cause once. Skip
sidebar cleanup only for already-latched read-only admission without a lock
slot; pace other failed attempts with the existing timestamp. Locks, migration,
retained state and explicit owning-Runtime restart behavior remain unchanged.
Align release notes and the existing affected fixture/assertion; no new tests.
Verified frozen4270-file Mac check: six native crates, all targets, locked,
onejob, offline, exit0 in838.473s, unchanged full source, no disk stop.
Node typecheck/build0, existing offline-reviewer1pass and six range observations
on the exact DSH source. Historical source-equivalent real1.0.8 preview400
reproduced the original range refusal. Current native/runtime and selected
Linux regression execution still pending; no release/global install claim.
No provider/model/GUI/CI/publication or lock cleanup. Preserve all receipts in
artifacts/readiness-20261106 and artifacts/coordination-diagnostics-20261107.
Restore missing plugin stages only from receipt-matching bytes under the
existing state writer, with generation and policy checks. Refuse changed
existing stages. Reuse normal reload/MCP invalidation for explicit restage;
add read-only shell status and bounded authenticated DSH asset grants.
Expose current-server MCP backoff without treating an absent backoff as health.
Share profile-scoped Rust account custody across terminal and native hosts,
keep local provider setup optional, and make ordinary logout session-only.
Fence browser imports and cleanup by exact revision; preserve ambiguous or
newer sessions. Keep definitely unadopted device grants in cleanup custody.
Validation: six-crate Mac all-target check exit0, 4,270 source entries
unchanged, 35m37s, no disk stop. Grok Linux raw evidence verified: secrets
account6 and plugins202 passed, one existing real-home rehearsal ignored;
all4,270 entries exactly matched that Mac freeze. Existing command-crate
boundary gate passed; 2,816 keys complete in all15 TUI locales.
The subsequent two-file private account cleanup has independent source
review and exact pre/post hash checks; Linux verification is in progress.
Only that cleanup and the already-landed visual-change changelog entry differ
from the passing Mac compile snapshot. No new tests or code comments added.
Qualification is incomplete: production recovery/asset HTTP smoke, published
plugin, Electron, live sign-in, cross-platform/package and final release gates
remain. This local candidate commit is not a release or publication.
Answer the integration chooser, actual catalog prerequisites, supported plugin formats, direct-versus-plugin MCP authentication and troubleshooting on the existing paper/blue pages. Link the plugin guide from docs navigation and refresh its plain-text index. Add explicit Computer Use GitHub/contributor links in all18locales and version-aware direct-MCP setup for published CLI0.10.2, whose older built-in bundle is not upgraded by catalog installation.
Reconcile repository EN/ZH docs for config precedence, validation/reload, URL-only servers, environment credentials, trust hashes, pinned-source updates and catalog freshness. Current marketplace and unreleased source remain separate from installed-release claims; DSH and MCPApps are not presented as universally qualified.
Verified locally:47existingwebtests passed (26dictionary/copy+21llms/docsIA); focused ESLint, TypeScript, locale/GT checks and diff check passed. Initial custom test harness missed Vite alias configuration in2suites; corrected harness passed those21checks. Desktop1280/mobile390 EN/ZH browser review passed; stale development module hydration mismatch cleared after owned dev-server restart. Published CLI0.10.2 discovered current CU0.16.1 tools and executed request_access through a local synthetic model:ok=true/viaapp, accessibilitygranted, screen-captureprobeok. No input or provider spend. Credential-free Cloudflare Docs MCP discovery listed2tools. Existing-helper setup is not clean-Mac/app-task qualification.
Generated agent documentation uses51hashedinputs: our5revieweddocs plus committed da143039f7 content for all other inputs, excluding active peer GUIDE/feature/native drafts. Full clean web validation/deployment follows this frozen commit; no Engine release qualification. CU setup README is carried separately by codewhale-cu-plugin afb98e8. Evidence:CW/artifacts/plugin-mcp-discoverability-20261107. Tracking:CW-7848; CU acceptance remains CW-6732.
Keep low-level bwrap policy inputs explicit and expect only the two existing too_many_arguments lints, without changing runtime behavior or introducing a parameter wrapper.
Verified with Grok Linux VM: exact pre/post bwrap.rs hashes match this file; cargo fmt -p codewhale-sandbox -- --check and cargo clippy -p codewhale-sandbox --locked --all-targets -- -D warnings passed on the earlier e44b2bd3 base. Local rustfmt passed. Readiness agent verified the archived logs and internal SHA256SUMS. This is not full current-tree app-server or release qualification; current parallel DSH/account changes remain unbuilt and unstaged.
Place plugin UI metadata under the existing runtime authentication and workspace identity guards. The separate asset route remains unchanged and requires further authorization work.
On Mac arm64, a source-inventoried development build returned 401 without a token, 200 with a valid token, 409 for a replaced workspace identity, and 200 after restoration. A separate asset request remained unauthenticated, as documented; no GUI or release qualification was run.
Different 0.10.3 binaries can straddle workspace state changes despite sharing a version label. Conservatively refuse self-managed rollback to any different binary labeled 0.10.3 or earlier until compatibility can be verified; preserve the identical-binary no-op.
On Mac arm64 a source-inventoried development binary passed five isolated refusal/no-op cases, including a genuinely different 0.10.3 binary and the 0.10.2 release; fixture trees stayed unchanged. Manual/package-manager downgrades and allowed rollback remain unqualified.
Map schema 4 notice_version into the existing privacy version column, retaining schema 1–3 behavior. This source change does not deploy the telemetry Worker.
TypeScript passed and 45 existing ingest tests passed. A network-denied replacement Worker replay passed 30 cases, including 40 schema 4 rows with notice 6 and no mocked PostHog egress; production route, settings and live ingestion remain unverified.
Report the expected snapshot path and distinguish unsafe entries, unverifiable directories, and unavailable paths for trusted plugins, including disabled ones. Diagnostics remain observational and do not authorize recovery.
Linux VM on e44b2bd3 with matching registry.rs SHA-256: codewhale-plugins tests 202 passed, 1 ignored; clippy all-targets passed. A separate copied-logic path probe covered seven cases, not registry wiring. Mac and Windows behavior not requalified.
Describe landed 792817bfcb: recheck the staged snapshot and refuse a changed
review generation or trust receipt under the existing state lock.
Verified the retained Mac stale-enable log (1 passed, 201 filtered), monitor,
pre/post capture and matching registry/tests/Cargo.lock hashes. Separately
attribute the reported Linux 202 passed/1 ignored result to its earlier-base
run; its raw logs remain with Codewhalebot, not this local receipt. Later
net/config changes were absent from that Linux tree.
Keep automatic restoration unfinished, the snapshot check point-in-time,
filesystem races open, and production stage-disappearance/final-candidate
qualification unclaimed. Preserve the consolidated website Unreleased entry.
Verified changelog sync/check, facts derivation/drift and scoped whitespace;
no generated metadata delta. All 25 v12 manifest entries matched immediately
before explicit two-file staging. No native build, new test, hosted CI or
deployment was run for this docs slice.
Reject an absent or invalid staged snapshot and require the freshly locked state generation and trust receipt to match the reviewed plugin before setting enablement. Preserve existing trust and revoke semantics. The filesystem check is point-in-time; automatic restoration and stage/GC races remain open.
Verified: existing stale-enable test passed on Mac (1/1); Codewhalebot Linux production plugin library 202 passed, 1 ignored with matching two source-file hashes and the same Cargo.lock on earlier base 1105bede; rustfmt and diff checks. No production stage-disappearance regression or exact final candidate was qualified.
Restore the founder-selected warm-paper website after reviewing the existing
Moda boards, stored homepage capture, Claude design files and historical water
source. Reuse the eight original hero paths from48140294df0 and its fading
footer current texture. Keep the original C-whale, current typography, genuine
terminal gallery, concise product copy, Computer Use and automatic releases.
Use instance-specific SVG filter IDs, static pointer-inert decoration and
forced-color hiding. Keep reading sections on paper and scope dark roles to
the actual terminal frame and closing sea. Recover earlier paper neutrals and
the readable1877c9-to0b48bb blue action ramp; retain the shared geometry tokens.
Use five primary nav links so translated desktop chrome fits at1024px without
shrinking controls; keep Changelog in the compact menu, release section and
footer. Correct the existing focus-role and paper-color contract assertions.
Consolidate the three superseded website changelog bullets into one accurate
entry and mechanically sync the embedded changelog.
Verified: scoped ESLint and TypeScript --noEmit --incremental false passed;
153 existing tests across16 relevant suites passed (the paper expectation was
corrected and its five-test role suite rerun); embedded changelog --check and
git diff --check passed. Real browser: EN desktop,390px mobile, Spanish1024px
single-row header, ZH dark, current terminal/CU versions, pointer-inert water,
forced-colors/reduced-motion behavior, no sampled page overflow or JS errors.
Native PNG color-profile verification also found that earlier JPEG previews
dropped Rec2020 ICC data and shifted blues; review artifacts now retain the
native profile. Existing Moda preview updated in42 free deterministic edits,
zero skipped ops and clean desktop/mobile renders. Historical audit and exact
receipts: CW/artifacts/website-repair-20261106/.
Final protected web validation/publication follows this source commit. No
Engine release qualification or publication is claimed.
Document the landed Engine catalog refresh and Runtime API projection so
clients can discover new models on supported routes without replacing the
binary. Explain exact-route refresh, freshness, retained snapshots, and the
existing desktop picker refresh and revision polling.
Carry the EN/ZH durable job receipt and implicit socket contract with native
source 821c478eb9. Describe Tinker beta configuration without implying live
provider qualification. Align the Chinese provider introduction with the
supported-model-list boundary. Keep release limits explicit for plugins,
platform qualification, history recovery, long sessions, and rollback.
Credit zhuowp's rate-limit recovery contribution and SparkofSpike's proxy
privacy report. Sync the embedded changelog and derived web metadata only
after Mistral guidance 07853c3266 landed with its live-conversation caveat.
Verified: derive-facts and check-facts passed; embedded changelog sync/check
and scoped diff whitespace passed. Contributor credit check found all 15
contributors credited, retaining its known Bot/co-author warning. All 22
v11 manifest inputs were independently checked immediately before staging;
six catalog/job API source paths matched committed main.
Retained development receipts cover nine controlled catalog API assertions,
eight mounted picker assertions, and a keyless public Models.dev fetch.
These are scoped development observations, not final package or provider
inference qualification. Proxy source matches the retained macOS 23/23
existing-test receipt; runtime log capture and other platforms are unqualified.
No new maintained tests, native builds, hosted CI, or deployment in this slice.
Point the existing Mistral provider at current Vibe Code key guidance and document setup, shared allowance and billing boundaries. Live use in Codewhale remains unverified.
Verified: existing Mistral provider metadata test 1/1 on Mac, provider-registry and example-config checks, and captured Mac CLI build. No live provider call was made.
Treat the advertised plugins area as an area even when the top-level plugins directory exists, while preserving explicit path handling. Found during Codewhalebot Linux review.
Verified shell syntax and a no-build mapping probe for all 44 advertised areas, explicit plugin paths, and supplied/inferred filters. No Cargo build was run.
Use the existing Runtime thread store and shell manager for keyed setup, run, archive and command admission. Persist intent before dispatch; exact retries return the same receipt, changed requests refuse, and unconfirmed outcomes fence new work and removal. Cache a positively observed child exit before output cleanup so Stop can persist its terminal receipt. Retain small terminal witnesses across output eviction.
Bind client recovery to the complete request fingerprint and canonical workspace/cwd, validate project policy against the approved root, and avoid nested blocking-pool filesystem work while holding admission. Shorten overlong implicit control sockets into a private stable endpoint without moving user data or changing explicit socket choices. The shared Runtime API also carries the reviewed catalog refresh projection from the already-landed model catalog source.
Verified a captured macOS development CLI build and exact source matching for 15 of these 16 paths; the newer jobs.rs was compiled and exercised in the Linux Boat candidate. Actual HTTP observations passed concurrent single-spawn/replay, mismatch/store/workspace guards, Stop and subsequent Run, prelaunch PTY refusal with restored mode, restart fences and private receipt content checks. Actual Engine plus transpiled TypeScript passed canonical alias binding, one shell spawn, same-key replay and a discarded real changed-env 409 that stayed unconfirmed. Separate prior paired GUI Run/output/Stop/reload and real output-eviction/deletion observations passed. Existing model API checks passed nine controlled cases.
No new product tests or code comments. These are scoped development/runtime receipts, not final-candidate packaging, Windows, provider inference or release qualification. Parent exit confirmation does not prove detached descendants are gone; path checks do not provide a directory-handle replacement guarantee, and archive recovery remains multi-phase work.
Omit malformed proxy values from MCP warnings and remove paths, queries, and fragments from parsed proxy log labels. Preserve proxy routing and authentication. Addresses the leak reported by SparkofSpike in #7233.
Verified: 23 existing proxy tests passed on macOS; rustfmt and git diff --check; independent source review. A separate runtime log-capture and other platforms remain unqualified.
Remove unused direct dependency declarations and their orphan lockfile edges while retaining the actual owners of the enabled features. No locked package versions are removed.
Verified: Mac development CLI build and isolated plugin/MCP fixture; separate locked Engine library check; locked offline metadata without dependencies; diff check. An earlier existing DSH test passed with identical dependency files but crossed other concurrent source changes. Test configurations and non-Mac targets remain unqualified.
Regenerate only the committed telemetry and Runtime API documentation metadata using the existing deriver against Git objects from 543b5b76db. This excludes all pending API, catalog, changelog and credit claims from the published tree.
Verified: independently derived projection is byte-identical to the website owner projection; the existing committed check-facts script passes with a Git-object filesystem and this single generated-file override. Only TELEMETRY.md and RUNTIME_API.md source entries differ. Pending v9 working metadata is retained unchanged (SHA256 84a248f8e6a7228739711850a6d0eb817f14c95a7036a62091b0025c6f37faf7). No source files, new tests, checkout, provider calls, build or deployment.
Give Computer Use and release history primary navigation, and show terminal and
Computer Use releases on the homepage and changelog. Read bounded, validated
published metadata for the two fixed repositories in parallel; exclude drafts,
prereleases and noncanonical release URLs. Keep download qualification on the
Computer Use setup surface and retain known releases when updates fail.
Refresh visible clients every five minutes and on return, using the original
check time so prefetched pages can refresh promptly. Provide independent RSS
and release-note links without requiring a website rebuild. Preserve newer
known terminal release metadata in the existing facts cron. Trim the changelog
introduction and export the two local translation catalogs.
Verified: scoped ESLint; TypeScript --noEmit --incremental false; 12 existing
Vitest suites, 132 tests passed; locale schema and local GT parity passed;
git diff --check; local releases API HTTP 200, both current releases, 36ms;
both canonical RSS feeds HTTP 200; responsive desktop/mobile, Chinese dark
mode, direct Computer Use navigation, actual download setup and release links.
Full protected web workflow and production publication follow the shared
source/docs metadata landing. This is not Engine release qualification.
Refuse a self-managed rollback to recorded versions older than the state migration boundary before replacing the executable. This is a conservative interim guard, not a state migration or protection against manual downgrades.
Verified: Mac development CLI build from captured source; 5 isolated rollback refusal/no-op cases with fixture preservation; rustfmt and git diff --check. Allowed rollback, other platforms, and real workspace migration remain unqualified.
Keep the existing Models.dev authority active after startup, serialize public refresh attempts, back off network failures by source, and preserve immediate local-file repair and last-good rows. Reject fresh-cache reuse after a source change, report expired live status as stale, and expose the existing live/cloud generation tuple for clients.
Verified on the frozen v3 development CLI 2a549fe8af9a00f992bbf019cc2b247aa41548f75d2955a227ca81260ee1da1b, source inventory cfdf8b4445ab501cb052f1b543b6b6f785d90310f735b92ae7bc09d9839ba1ca: 9 direct API and 8 mounted-renderer assertions passed, including manual/background discovery, exact-route refusal, retained rows, local repair, unchanged-page skip and preserved selection/search; zero browser errors. This used controlled catalog data and a synthetic key with proxy fallback disabled; no inference. Actual keyless public feed fetch passed with 235 provider catalogs and 8939 provider model rows. Focused formatting and whitespace checks passed.
The coordinated app-server response/query hunks, existing exact-route test adaptation, release notes and generated documentation remain with the shared integration owner. These observations qualify the catalog development slice, not installed packages, live model access or a complete release. No new tests or code comments.
Update the default ocean palette and reuse the ratatui motion primitive for a restrained working sweep when terminal capabilities and motion preferences allow it. Parsed Mac PTY frames showed the sweep across 18 working samples and static idle and settled samples; physical-terminal appearance remains unqualified.
Clamp a requested zero page size to one while preserving existing cursor compatibility. An isolated Mac stdio observation exercised zero, legacy and invalid cursors against three fixture threads.
Resolve staged Claude plugin MCP paths from the bundle root and retain the relative manifest path. Isolated Mac candidate smoke connected a local MCP ping before and after restart; a separate synthetic upgrade preserved trust and receipt bytes. No live provider was used.
Retain v2 and v3 PostHog forwarding rules while preventing schema 4 from expanding processor egress. Four existing test files passed 152 cases, TypeScript passed, and a local network-denied bundle passed 30 fixture comparisons. No deployment or production event was performed.
Recognize local Copilot and Codex bundle layouts and honor source revisions.
Reject unresolved remote subdirectories and require re-import for ambiguous
legacy listings, while preserving content hashes for installed plugin bundles.
Nine of nine isolated source-resolution and compatibility checks passed with
the frozen candidate. Three existing bundle hashes matched. The synthetic
runtime and inspected vendor catalogs do not qualify provider execution.
Saving a change writes catalog schema 2. A schema-1 runtime refuses that
file. Downgrade qualification remains a release blocker.
Apply the reviewed Moda desktop/mobile composition with the existing C-whale,
Shannon Sans and real interactive terminal captures. Use one coherent headline
in all18 locales. Give Computer Use a full-width band, show three concrete work
patterns, feature the published two-file skill tutorial and shorten the closing
install action. Keep released/source availability distinct.
Audit public instructions against published CLI0.10.2 and CU0.16.1: correct
hash-bound plugin trust, Workflow proposal/confirmation and limits/forms, Fleet
session workers versus durable ledger and explicit recovery, Lane control
surfaces, provider setup, Auto-Review user questions and conditional snapshot
restoration. Update CU setup/platform guidance across18 languages.
Fix lost dark-mode pins on locale navigation with the existing ThemeToggle
boundary: server-only bootstrap insertion and locale-keyed layout synchronization.
Remove unused auto-mode props/tooltip. Adjust existing assertions to follow the
actual session-worker command and moved bootstrap owner; no test cases added.
Verification:9 existing web suites110 passed. Focused ESLint covers all58
changed TS/TSX paths; TypeScript no-emit, locale schema, local EN/ZH GT and diff
checks pass. Actual browser desktop/mobile light/dark; EN/ZH repeated locale
changes and cold dark reload preserve theme, with a single bootstrap in HEAD
and no post-fix console errors. Windows installer/Fleet gallery controls work;
no sampled page overflow. Official CLI macOS arm64 SHA256 verified and9
isolated --help commands passed; no model/provider execution claimed.
Moda:201 editable nodes, desktop/mobile native PNG exports without warnings;
existing Codewhale kit, unchanged team visibility,0credits, no publication.
Local/source verification only. No hosted CI or website deploy initiated.
Reduce homepage main text from1647 to528 words in the rendered local page.
Remove duplicate setup/availability lists, unreleased changelog dump, repeated
product sections, footer provenance links and redundant kickers. Keep detailed
availability and Mac Computer Use limits on the product/setup pages, useful
capability links, install command, real demo provenance and contribution paths.
Replace the duplicate contributor manual with direct paths and the actual
contributor guide. Shorten shared EN/ZH copy and export local GT catalogs.
Delete unused content and the product page's unused facts fetch.
Verification: focused ESLint and tsc --noEmit pass;5 existing suites64 passed
(public copy/surface, docs IA, dictionaries, ocean). Retire the old homepage
onboarding layout case and redundant footer release-link assertion; actual
published release, MIT, provenance and policy checks remain. No new tests.
Initial batch2failed/63passed captured obsolete layout expectations. EN/ZH
browser review passed, sampled390px view has no page overflow. Screenshot
updated in artifacts/website-repair-20261106. No native build, CI or deployment.
Restore the website-specific Moda ocean roles after e9201f5d38 flattened
them to desktop gray. Retain shared typography, spacing, motion and elevation.
Bring Operate, multi-model Fleet, reusable Workflows, skills and independently
released Computer Use into the product story, informed by direct inspection
of OpenCode, Pi, Oh My Pi, Aider, Cursor and Claude Code.
Reuse real captured terminal views and shared contributor content, align
keyboard and visual order, correct Mac helper approval/setup and Windows
preview copy, and export the local EN/ZH website catalogs. Keep release,
source-candidate and captured development-demo identities separate.
Verification: final focused ESLint and tsc --noEmit pass. Existing public
copy/surface, terminal and Fleet cases: 4 files, 34 passed. Dictionary/CU/copy
batch: 3 files, 52 passed (overlaps public-copy). Palette/ocean: 2 files,
13 passed. Locale schema and local GT catalog check pass after export.
EN/ZH desktop/mobile, light/dark, install platform switch and Fleet view
observed; 8 linked local routes HTTP200. Two review findings fixed: guide
URLs and keyboard order. No new tests/comments, hosted CI or deployment.
Source/build/provider/release proof remain distinct. Moda CLI setup verified.
Offer the documented Inkling 2 Small serverless ID through the existing custom provider, skip undocumented model-list probes, and keep health unobserved until an actual request succeeds. Cost and live capabilities remain unverified.
Verified: provider registry drift check; 1 config descriptor, 1 Engine catalog and 1 pricing coverage case passed. A local health case with temporary extra assertions exercised normalized endpoint matching, unobserved health, and real mock-request recovery; those extra assertions were not retained. Mac development CLI built with one linker warning. No live Tinker call.
Reuse the existing DSH manifest admission check for both metadata and assets so an ordinary plugin cannot expose its staged files through the UI route.
Verified: isolated Mac runtime fixture: two non-DSH bundles returned 404; declared DSH metadata and client asset returned 200; its MCP connected and exposed ping. Authenticated asset transport and revocation remain open.
Show enabled trusted plugins as needing setup when their reviewed snapshot is missing or invalid, with a diagnostic instead of silent inactivity.
Verified: 201 plugin library checks passed with 1 ignored before the descriptor-only config change; a Mac local runtime fixture on the compiled batch confirmed the diagnostic before and after restart and an unchanged receipt. Automatic recovery remains unimplemented.
Share checkpoint admission between API shell job launch and managed worktree removal. Refuse removal for overlapping tracked running jobs and retain owned guards while spawned mutations finish after request cancellation.
Verified on Boat Linux: actual CLI build from 5296fa1c78 plus focused patch ac426a2946da; real API worktree 201, job 201, remove 409 worktree_busy with folder preserved, kill 200 Killed, remove 200 with folder absent. No unit suite or cancellation-race observation was run. Separate runtime managers, late independent subagents and escaped descendants remain outside this tracked-job guard.
Record unfinished Codex wire parity, DSH UI qualification and plugin snapshot recovery; refresh generated web and TUI data after main integration. Verified: release version gate (24 issue references), web check and cargo fmt --check.
Add the release-note receipt for the pending task wall-clock feature so the
feature commit's version-drift check is satisfied when the contributor PR is
updated. No product behavior changes.
npm --prefix web run prebuild after the worktree/changelog slice. Verification:
web check:facts passes; release metadata gate passes with 24 issue references
and versions/lockfile at 0.10.3.
Finish the app-server slice left uncommitted in the shared checkout. Git
stage/unstage/discard/commit/push/branch now honor ?thread_id= and act on that
thread's recorded workspace; snapshot list/restore gain the same scope. Add
GET/POST /v1/worktrees and POST /v1/worktrees/remove with managed-root
containment, check-ref-format validation, checked-out/existing/locked/dirty/
active-turn refusals, targeted removal of a missing worktree by exact path, and
.codewhale/worktrees in the repository info/exclude. Advertise the three new
capabilities and document them.
Verification: cargo check -p codewhale-app-server --lib passed (7m37s);
existing app-server git tests 26 passed, 0 failed; real app-server HTTP smoke
listed, created, listed and removed a managed worktree; release metadata gate
passed (23 issue references, versions/lockfile at 0.10.3).
Refs codewhale-hq/codewhale-app#3500.
Record the reviewed DSH dependency/client-UI feature from b0b2b9a335 and
regenerate the packaged TUI changelog. The release metadata gate now passes:
check-versions.sh --require-dated-release reports 23 linked issue references
checked and workspace/npm/lockfile all at 0.10.3.
Allow the existing 60-second network time tolerance on ChatGPT identity-token
expiry and not-before checks while keeping signature, issuer, audience, subject,
nonce and required-claim checks exact. A token with a 15-second future nbf now
verifies; the existing one-hour-invalid case still fails.
Verification: cargo test -p codewhale-runtime --lib
siwc_signed_identity_requires_signature_issuer_audience_expiry_nonce_and_account
passed (1 passed, 0 failed). Refs #7203.
#2: admit a reviewed DSH closure's own dependency tree on Node and Bun by
resolving bare imports and requiring the target to stay in the same hashed
receipt; stage node_modules through the importer (including hidden files and
contained .bin links); admit cjs/cts module entries; and refuse incompatible
engines.dsh ranges or dsh.bundle.platforms before conversion. UI-only bundles
now stage their source closure instead of being refused as 'nothing portable'.
#3 Engine side: expose a trusted+enabled plugin's dsh.client entry and assets
through two loopback-only public routes, with source/ fallback for converted
DSH bundles, 8 MiB bounds, confined paths, safe MIME/no-store/nosniff, and no
auth bypass beyond those two GET-only paths.
Verification: cargo check for codewhale-plugins/app-server; 30 published DSH
importer tests pass; Node/Bun resolver suites (49/50 host, 10 composition),
tsc, node build.mjs, rustfmt and git diff --check pass.
A Ctrl+V-family chord reaches both Terminal's paste action and Codewhale's
own paste shortcut, so the fixture payload arrived twice. Shift+Insert is
handled by Terminal and is not one of Codewhale's paste shortcuts, so the
acceptance test measures one delivery.
Verification: node --check and git diff --check pass.
Ctrl+Shift+V is already Terminal's default paste binding, so defining the
same chord as a fixture action pasted the 24-line payload twice on the pinned
Windows Terminal build. Bind the fixture to Ctrl+Shift+Alt+V instead.
Verification: node --check and git diff --check pass; the Windows runner job
is rerun against this candidate.
Refresh the measured Plan/Act/Operate prompt identities and tool counts after
present became eager, and keep all 55 metrics exactly at the new budget.
Verification: python3 scripts/check-runtime-contract-budget.py --update
--allow-increase, then the same checker without --update: PASS.
The resolved route config carries provider and model scope, not the operator's
sandbox choice. Installing an injected route replaced api_config and dropped
sandbox_mode/sandbox_network_access, so a read-only session started escalating
from workspace-write. Preserve those two session-level fields when applying
the route.
Verification: injected_model_sandbox_escalation_applies_only_after_exact_call_approval
and sandbox_escalation_fails_closed_when_the_posture_cannot_prompt both pass.
The workspace all-targets clippy gate rejects a one-item loop. Keep the
same one rejected case and baseline assertion without the loop.
Verification: cargo clippy --workspace --all-targets -- -D warnings passes.
Update the public tool surface for the eager present tool, fix the stale NSIS
removal assertion after the ownership-safe macro change, replace the literal
approval-bracket doc link and remaining private/unresolved links, and allow
the pre-existing argument-count and large-enum style lints that the current
toolchain promotes at workspace scope. Regenerate the web facts and agent docs.
Verification: npm test 74 files/771 tests, npm run check:web, cargo doc
--workspace --no-deps with RUSTDOCFLAGS=-Dwarnings, cargo clippy --workspace
--lib -D warnings, release helper tests 7, and check-versions
--require-dated-release all pass.
Name the 2026-11-06 release candidate and point its compare link at the
eventual v0.10.3 tag. Verification: check-versions.sh
--require-dated-release passes, including feature release-note receipts.
Forking, /undo and retry of any session that had received a workspace
drift notice refused with "Cannot identify an exact saved-history
boundary", leaving the thread uncuttable for good. The notices are
engine appends no turn record keeps, so the boundary walks read them
as prompts the records never saw. Recognizing the engine's own
<context_update> envelope lets those sessions fork again at the exact
turn a person chose, while a prompt that merely quotes a notice keeps
its turn.
Convert the remaining async callers of route validation, relay/remote-control
transactions and command filesystem work to the supervised owner boundary.
Compaction now awaits the async route install and the sync install path is
gone. Remote control gets async owner entry points for the TUI bridge and the
start/stop callers; sync APIs remain for sync/test callers. Branch, tree,
skill, plugin and memory command reads now use the existing owner helper.
Add present to the Plan builder and the ACP foreground allowlist, and update
the Plan prompt/catalog expectations.
Verification: cargo check for engine/tui/app-server; full test-binaries build;
route_runtime::tests 37, turn_route_plan::tests 3, relay 17, remote_control
78, commands::contract::tests 76, commands::groups::plugins 26,
commands::groups::skills 40, commands::groups::memory 22,
runtime_contract_tool_metric 1, prompt/event conformance and shell job tests
green; blocking budget 640/173.
The eager present tool is now part of the Act/Operate request head and is
absent from Plan's separate builder. Update the directly affected catalog
assertion and its Plan exception.
Verification: runtime_contract_tool_metric_uses_canonical_mode_surfaces
passed 1/1.
A deferred delivery tool is not a delivery surface: the model would have
to discover it before it could use it. Put present in the default active
native catalog and refresh the directly affected prompt/event goldens.
Plan mode keeps its separate builder and does not gain it.
Verification: conformance::prompt::model_visible_prefix_bytes_match_goldens
and conformance::events::golden_turn_events_match passed without update
mode.
Route session save/load/fork/import/export, Git snapshots, memory import,
and skill/plugin installer work through the existing supervised
owner_work boundary instead of doing it inline on the command path. The
sync command trait and bridge stay intact, results are still awaited
before state publication, and error/confirmation text is unchanged. This
does not make command dispatch non-blocking; it removes the filesystem,
Git and installer work from the runtime worker. Branch/tree reads and the
remaining skill/plugin scans are retained for a follow-up.
Verification: cargo test -p codewhale-tui --lib
session_lifecycle_regression_tests (32), session_export_regression_tests
(28), session_control_regression_tests (18),
session_structcopy_regression_tests (31), and
commands::debug_mutation_host_tests (38) passed. Refs #6553
Run the existing durable relay state transaction on the supervised blocking
owner while holding the relay scope lock, and await it before applying or
returning. The same single transaction still validates, writes atomically
and publishes memory last; prompt/interrupt and projection-cursor paths no
longer persist on the async worker. Sync controller entry points remain for
their current callers and are reported as remaining follow-up.
Verification: cargo test -p codewhale-app-server --lib
runtime_chat_relay::tests (17) and remote_control::tests (78) passed.
Refs #6553
Run the worktree add and its snapshot refresh on the supervised blocking
owner, publish the outcome through the existing modal tick seam, and keep
the pending state, selection clamp, current-worktree flags, and error text
unchanged. A blocking git create cannot be aborted after it starts; closing
the modal discards the result without leaving a detached async task.
Verification: cargo test -p codewhale-tui --lib worktree_manager (2 passed).
Refs #6553
Split client construction into pure route admission/validation, a captured
credential/certificate read step, and a pure apply step. Async route
preflight now runs only the blocking credential, redaction-secret and
SSL_CERT_FILE reads through owner_work, then re-admits the same identity
and endpoint before building the client. Sync preflight APIs stay intact
for sync callers; the TypeSafe decision client keeps the captured root
certificate instead of silently dropping SSL_CERT_FILE.
Verification: cargo check -p codewhale-engine; route_runtime::tests (37)
and turn_route_plan::tests (3) passed. Refs #6553
Add the shell close-out rule: collect still-relevant background tasks with
task_shell_wait before a final answer and cancel work that no longer
matters. Record a typed shell termination cause, exact start time and
producer-reported fact so a killed job is distinguishable from a failed
one. Add a read-only present tool that hashes existing workspace files and
returns an auditable handoff receipt without copying or moving them.
Register the present handoff as experimental and refresh the directly
affected prompt/event conformance goldens.
Verification: Engine background_shell_job_preserves_origin_identity,
drain_finished_jobs_reports_once, test_kill,
shell_completion_event_names_retrieve_tool_result,
shell_completion_status_is_concise_and_shell_handoff_is_untrusted,
shell_catalog_guidance_matches_execution, the two machine catalogs, both
subagent role catalogs, model_visible_tool_descriptions_name_no_vendor and
runtime_contract_tool_metric_uses_canonical_mode_surfaces all passed;
TUI shell_job_routing (2), shell_live_output_update (4), worktree_manager
(2), both conformance goldens, and feature_registry (2) passed.
The interactive TUI already locks YOLO when requirements constrain approval
or sandbox posture; ACP derived Bypass directly from config.yolo. Reuse the
same requirements-managed check so a managed approval list cannot be
sidestepped by serve --acp --full-access.
Verification: cargo test -p codewhale-app-server --lib
acp_approval_mode_derives_from_server_config (1) and
plan_and_operator_floor... (1). Refs #6992
The runtime sink refusal now retains the exact receipt and continues the
turn, so correct the release note that still says a persistence failure
stops the turn. Refs #6553
The NSIS installer records an install-root marker and the uninstaller
refuses to remove a root, shortcut or registration it cannot prove owned,
checking every delete and preserving retryable state on partial failure.
The archive installer verifies its receipt, refuses foreign same-named
destinations, and accepts only the exact pre-receipt 0.10.1/0.10.2 layout
for upgrade, without weakening staging, hashes, backup or rollback.
Verification: makensis 3.12 compiled codewhale.nsi with and without
COMPILED_HOST; isolated PowerShell 7.6.9 suite 55 assertions/0 failures;
git diff --check clean. Native Windows lifecycle remains pending.
Refs #7091
Urgent warning and error notices now truncate to the available notice
budget instead of disappearing when their first clause does not fit. This
makes the embedding-only local Ollama failure visible at 150 columns; the
remediation clause remains reachable through /models and the CLI.
Verification: cargo test -p codewhale-tui --lib phase_strip (43 passed).
Installed 0.10.3 receipts cover default election, chat-only picker, late
daemon recovery and embedding-only non-adoption under the existing binary.
Refs #5848
Reject oversized workflow source and args before copying or serializing,
bound source_path reads through a confined reader, check fleet depth before
descending the tree, and recognize only real declarative workflow calls
rather than prose decoys. Move file frecency onto one bounded background
worker with workspace-scoped keys, an atomic compaction rewrite and a
late-load repaint. OpenSandbox cancellation remains a protocol gap: its
current synchronous endpoint has no run id or cancel contract.
Verification: cargo test -p codewhale-workflow-js --test vm_tests (82);
cargo test -p codewhale-workflow --lib js_authoring:: (10), fleet_validation
(5); cargo test -p codewhale-engine --lib tools::workflow:: (147);
cargo test -p codewhale-tui --lib file_frecency:: (2), file_mention:: (43).
Blocking budget 640/173 within recorded budgets. Refs #6554
Compaction completion now carries the effective route, window and source,
configured versus effective threshold, calibration, pre-pass input and
live gate pressure, outcome and elapsed time. Failed and cancelled passes
record the same additive audit shape, including explicit zero usage.
The debug context-budget ledger now prints the configured threshold next
to the route-derived trigger so the two cannot be confused.
Verification: cargo test -p codewhale-engine --lib
compaction_completed_reports_complete_post_input_tokens (1),
failed_emergency_compaction... (1), emergency_recovery_preserves... (1);
cargo check -p codewhale-engine --lib clean. Refs #7133
The client owns the transport retry ladder; the compaction pass now makes
one summary call per attempt instead of layering three outer retries over
it. Keep the distinct quality and HTTP 413 recovery paths, route existing
transport retry observations to the compaction notice sink, and move owned
artifact serialization, redaction, publication and captured anchor reads
through owner_work.
Verification: cargo test -p codewhale-engine --lib
transient_failure_after_a_quality_retry_stops_without_replacement (1) and
compaction_persists_original_and_model_handoff_before_returning_replacement
(1). Refs #7153
The stall watchdog now starts after turn admission with that exact control
token and lives only for the admitted operation, so it cannot cancel a
stale or successor token. Capture the turn-meta git snapshot through
owner_work before prompt assembly, move tool-audit writes and catalog
publication out of async callers, and run Fleet state rebuilds through the
existing supervised blocking owner. Worktree flags are computed off the
render path.
Verification: cargo check -p codewhale-engine --lib; cargo test
-p codewhale-engine --lib turn_heartbeat::tests (4), engine_handle_cancel
(1), turn_metadata (20), emit_tool_audit (2), provider_lake::tests::models_update
(5), preview (78); cargo test -p codewhale-app-server --lib fleet_ (9);
cargo test -p codewhale-tui --lib stall_recovery... (1), worktree_manager (2).
Refs #6184#6553
A runtime usage sink refusal is bookkeeping, not a reason to lose the
person's answer. Keep the exact receipt for a later admitted flush, warn,
and continue the turn. The previous fatal branch killed a live 955-message
session after a cancelled subagent settlement.
Verification: cargo test -p codewhale-engine --lib runtime_usage (4 passed);
cargo check -p codewhale-engine --lib clean. No new test.
Adds the conversation-export row to docs/features.toml, an 'Exporting a conversation' section and slash-command row to docs/GUIDE.md, the matching zh_hans guide and Windows beginner pages, and the 0.10.3 CHANGELOG entries (crates/tui/CHANGELOG.md regenerated with scripts/sync-changelog.sh). web/data/features.generated.json and agent-docs.generated.json are regenerated with web/scripts/derive-facts.mjs; the web facts drift gate reports OK.
Checks: cargo test -p codewhale-tui --test feature_registry 2 passed, 0 failed; web vitest 771 passed across 74 files; scripts/preflight.sh --check OK (rustfmt, changelog slice, README translations, dead-code 137/137, blocking-calls 641 sites within budget).
Co-authored-by: Copilot App <[email protected]>
/export gains --format md|html|json, --no-tools and a position-free --force. 'file' with no path writes codewhale-<title-slug>-<timestamp>.<ext> into <codewhale_home>/exports, and the format is inferred from the extension; '~' now expands in paths. HTML is one self-contained page (no script, CSP default-src 'none'); JSON is a versioned envelope (codewhale-conversation-export v1) that keeps raw roles and flags omitted content. HTML and JSON are file-only; turn handoffs and clipboard exports stay Markdown, and the clipboard messages are unchanged.
The Markdown renderer drops the 'Content N' headings, labels tool-result carrier messages 'tool', and fences tool output so it can no longer forge a heading or a turn. Breaking for scripts: a trailing --force is now a flag rather than part of the path, and the tool-result role label changed from 'user' to 'tool' in Markdown and HTML (JSON keeps the raw 'user').
Contract: ExportMetadata gains title (None when the session is untitled or still the 'New Session' placeholder) and CommandSessionExportContext gains default_export_path(file_name), which the host resolves inside <home>/exports after creating and canonicalizing it.
/share now renders through the same HTML renderer; its private render_session_html, html_escape and count_redactions are removed (replaced by export_html::render and export::count_redactions).
Tests: codewhale-command-contract 79 passed, 0 failed; codewhale-tui commands:: 1213 passed, 0 failed (export, share, regression and surface suites plus the re-baselined goldens). The feature-gated session_acceptance suite was not run. Real md/html/json output through the adapter was inspected: HTML is escaped with a no-script CSP, JSON parses, files are mode 0600.
Co-authored-by: Copilot App <[email protected]>
Recheck the public total timeout before another operation, including after
time spent in callbacks or retry receipt delivery. Preserve the original
typed error and exact attempt count. Clarify the independent rate-limit
configuration, validation bounds and safe pre-dispatch HTTP/1.1 fallback.
Merge upstream main at 13760312. Keep the precise Responses unknown-outcome
test names with main's exact one-attempt and typed 503/499 assertions.
Validation on Windows, rustc 1.99.1, locked dependencies:
- Candidate-source Engine selections: 47 tests passed
- Separate public-API real-clock deadline probe: 1 passed
- Three causal negative controls failed as expected; exact source restored
- Original candidate Runtime retry status: 7 passed
- Full formatting, version consistency, whitespace and dead-code checks passed
- Strict/diagnostic Clippy retain attributed existing dependency and Windows
lint failures. Primary Runtime passed with the recorded decoder allowance;
primary Engine lib/tests report only the two existing Windows lint sites.
- Windows blocking checker retains its path-key mismatch; normalized complete
measurements have no excess. This is not an official checker or CI pass.
The Engine validation copy has test-only tracing and the local probe;
production and lockfile bytes match the reviewed candidate. Validation
instrumentation stays outside the product tree; contributor changes do not
repair unrelated lints.
Signed-off-by: z27014 <[email protected]>
Add stable tencent/hy4 to the OpenRouter catalog with reviewed limits and aliases, and a Tencent TokenHub compatible descriptor using hy4 and TENCENT_API_KEY. Direct TokenHub pricing remains explicitly unknown rather than inheriting OpenRouter rates. Replace the duplicated OpenRouter alias match with the reviewed compatibility table (102 existing aliases verified unchanged). Catalog relock also carries upstream models.dev price and metadata updates.
Validation: focused config/engine/tui nextest 559 run, 557 passed before updating two stale count fixtures, which then passed with feature_registry (4/4); catalog script tests 18 OK; render --check, provider registry, preflight, fmt passed; web vitest 771/771. No paid Tencent call was made.
Co-authored-by: Copilot App <[email protected]>
Keep 0.10.3 as an unreleased candidate, credit its current contributors and helpers consistently, and ignore the Copilot bot in the contributor checker. No release is published.
Validation: web vitest 74 files, 771 tests passed; contributor checker reports all 14 historical contributors represented.
Co-authored-by: Copilot App <[email protected]>
Apply SparkofSpike's exact-first selection proposal from issue #7214, reported by liuyy2. Try the configured id and wire id exactly before case-insensitive compatibility matching; retain the existing insertion guard. Refs #7214.
Validation: existing provider-picker checks passed in the focused 538 passed, 0 failed run. Also passed unchanged in the HY4 focused run; actual Electron picker/save/reopen qualification is not claimed.
Co-authored-by: Copilot App <[email protected]>
Remove the redundant raw-filesystem fallback introduced by #7218. The existing clipboard_image_target already opens a relocated user state root safely. Preserve atomic private writes and link confinement instead of increasing the blocking-call budget.
Validation: existing clipboard and confined-file checks are included in the focused 538 passed, 0 failed run. Blocking-call scanner: 641 sites across 173 files, within the reduced budget. Native Windows distribution qualification remains separate.
Co-authored-by: Copilot App <[email protected]>
Reuse the config endpoint owner for the reviewed DeepSeek published-price family at both capture and replay. Keep exact quote fingerprints, provider identity, wire model and dispatch time guards; do not broaden regional or subscription pricing authority. Recognize Anthropic /v1 PAYG and preserve distinct missing-outcome, missing-usage and legacy-coverage labels in all locales. Historical receipts without a frozen quote remain unknown.
Validation: scripts/dev-test.sh engine with config, localization, tui and util pricing/route/usage/clipboard/picker/file selectors: Summary: 538 tests run: 538 passed, 13195 skipped. Verified against the pre-HY4 source slice; no provider inference or published-release claim.
Co-authored-by: Copilot App <[email protected]>
reqwest snapshots HTTP_PROXY/HTTPS_PROXY/ALL_PROXY when a client is built, so a
session started behind a loopback proxy (Clash, v2rayN, sing-box) kept dialing
the dead port after the proxy closed: every request failed, including ones to
providers that need no proxy at all, until Codewhale restarted.
Install a custom proxy route that re-probes each configured loopback proxy in
the background and bypasses the proxy while its port refuses connections.
no_proxy() runs first, so the route is the only proxy source and a declined
request cannot fall back to the address reqwest snapshotted.
Only a definite ECONNREFUSED from every resolved address moves a proxy to
direct; a timeout, an fd limit or a resolver failure leaves the previous state,
since routing direct on that evidence would put traffic on a path the operator
deliberately avoided. Reachability is tracked per proxy, so a live HTTP_PROXY
does not keep a dead HTTPS_PROXY in the path, while a remote proxy in the same
configuration is routed through as before and never diverted.
IPv6 and IPv4-mapped IPv6 loopback proxies are watched too: Url::host_str keeps
the brackets, so the address is unwrapped before it is matched and probed, and a
portless socks proxy defaults to 1080 as reqwest does rather than silently
disabling the fallback.
The engine's model, search, vision, finance and catalog clients all start from
crate::platform_client_builder(), and the TUI balance poll and the app-server
relay use install_loopback_proxy() too. Half-fixing this would read as a tool
bug once the model kept working. Reviewed plugin remotes and guarded fetches
keep their own no-proxy builder on purpose.
Windows needs about two seconds to report a refused loopback connection, so the
probe bound is 4s: a shorter one only ever sees a timeout and the fallback would
never engage on the platform the report came from.
Set CODEWHALE_LOCAL_PROXY_FALLBACK=0 for the previous behaviour. The proxy
environment is read once, when the first client is built. Known limitations:
reachability is a TCP connect rather than a proxy handshake, detection takes a
few seconds so a request in that gap can still fail once, and going direct
leaves from the machine's own address.
Tests cover the refused/returning transitions, per-proxy isolation, proxy
precedence, IPv6 and IPv4-mapped loopback, a portless socks default, remote and
mixed configurations, NO_PROXY whitespace and '*', credential survival, the
install order, and end-to-end requests over a real reqwest client.
The #7216 repair (eb17ae0ec1: /patch-undo honors turn_id, answers 409 undo_selection_changed, advertises thread_patch_undo_turn_id) never reached the 0.10.3 notes and its reporter was uncredited. Add the Fixed bullet and credit @soarkit on the three surfaces cw-land names: the changelog Contributors block, docs/CONTRIBUTORS.md and UNRELEASED_CONTRIBUTORS. crates/tui/CHANGELOG.md is regenerated.
The 0.10.3 section also had no opening paragraph. Add the 0.9.3 source-candidate sentence: web/lib/public-surface-contract.test.ts needs 'v0.10.3 source candidate' before the tag and 'Codewhale v0.10.3' after it, and the changelog had neither. Replace it with the release summary at the cut (precedent 3665adde81).
Checks: sync-changelog --check up to date; check-versions 'Version state OK: workspace=0.10.3'; check-contributor-credit 'Every contributor in the window is credited on all three surfaces'; check-feature-release-notes OK (21 linked issue references in v0.10.2..HEAD); web changelog.test.ts passes (vitest: 22 passed, 2 failed across 3 files). The 2 failures predate this change and are cut-time gates: public-copy needs the v0.10.3 contributor band and promoted RELEASE_CONTRIBUTORS, and the pre-tag guard rejects [Unreleased] compare/v0.10.3...HEAD (97e073ac79).
Refs #7216
Co-authored-by: Copilot App <[email protected]>
clippy::while_let_loop (denied by -D warnings) failed Lint on the
loop/match in ImagePreviews::poll at image_preview.rs:267. Both
TryRecvError arms only broke out of the loop, so `while let Ok(..)` is the
same behaviour and the import is no longer needed.
Co-authored-by: Copilot App <[email protected]>
Read::take consumes the handle by value, so the binding never needs to be
mutable. Windows CI compiles this cfg(windows) path with -D warnings and
failed on unused_mut at clipboard.rs:1126 (Test (windows-latest)). Not
compiled locally: the path is Windows-only.
Co-authored-by: Copilot App <[email protected]>
Harvested from PR #7208 and #7210 by @gaord, and #7225, #7226 and #7228 by
@asto18089, each with the contributor's original commit kept.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Retrying a follow-up on an interrupted agent id whose continuation
successor already exists returned an unconditional delivered=true,
resumed=true receipt: continue_child_from_user hardcoded both flags in
its Interrupted/Completed arm, so a follow-up that was merely queued to
an already-running successor — or answered by a settled successor's
existing result — was reported as a fresh checkpoint re-dispatch that
never happened. The TUI only moved focus on resumed receipts, so focus
stayed on the dead original agent even though the successor now carries
the conversation.
The engine now honors its documented field semantics ("Whether a new
agent loop was re-dispatched from the checkpoint"):
resume_from_checkpoint_with_policy returns a ParentMailReceipt that
distinguishes the three continuation shapes, and continue_child_from_user
derives the flags from it:
- fresh re-dispatch from the checkpoint: resumed=true, delivered=true;
- follow-up queued to the already-running successor: resumed=false and
delivered = woke && queue_depth == 0 — the same delivery criterion
followup_child itself applies, since the new text is last in the FIFO
batch and only an empty queue after a successful wake means this
message was actually sent (the direct Running arm now applies the
same strict criterion);
- settled Completed successor: resumed=false, delivered=true, target =
that successor, whose existing result carries the follow-up. A
settled Failed/Cancelled/BudgetExhausted successor instead reports
delivered=false with a note naming its status, so a follow-up that
sits in no transcript and that nothing will ever answer is never
presented as delivered.
apply_follow_up_receipt in the TUI broadens its focus guard to
(resumed || delivered) && target != agent_id so focus follows the agent
that will actually answer, carrying the local user echoes with it, and
the toast names the successor. A delivered=false receipt (or an error)
still keeps focus unchanged and reports the failure note, so the
Failed-successor path lands in the warning arm with the honest status
note.
Disclosure: a delivery to a settled Completed successor still reuses
the pre-existing "Queued for {agent} — it reads it next round" toast
message id, whose wording does not describe a result-carried delivery;
replacing it with a dedicated message means i18n churn across all 15
language packs and is left as follow-up polish, not claimed fixed here.
Red/green evidence: the new engine test
user_follow_up_to_running_existing_successor_reports_delivery_without_resume
panicked on base (the successor-reuse path reported resumed=true) and
passes after the engine fix; the TUI test
delivered_existing_successor_moves_focus_and_names_the_actual_recipient
failed on base and passes with the broadened guard. Targeted
agent_focus/follow_up run: 23 passed, 0 failed. Full codewhale-engine
--lib: 6968 passed, 86 failed, 13 ignored; the failure set matches a
base (stash) run (85 common pre-existing machine failures; every
differing test passes in isolation, re-verified). All
resume_from_checkpoint* and continue_child_from_user* engine tests pass.
After the settled-status fix, targeted codewhale-engine --lib
user_follow_up: 5 passed, 0 failed, including
user_follow_up_to_failed_existing_successor_reports_undelivered_with_status_note
(a settled Failed successor as continuation target reports delivered=false
with the status named), and the targeted agent_focus/follow_up run stayed
at 23 passed, 0 failed.
Signed-off-by: asto18089 <[email protected]>
rustfmt --edition 2024 applied to the touched Rust files; their check failed on formatting only.
Harvested from PR #7228 by @asto18089
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
image_analyze had the same asymmetry the write lane had, on its own
read-only lane: the absolute spelling of an in-workspace file was
refused outright while the relative form of the same file succeeded.
resolve_image_path joined relative inputs onto the workspace but
hard-refused every absolute spelling before any containment check, so a
model handed an absolute receipt path — screenshot and attachment
receipts hand the model absolute paths — paid one failed call to learn
the relative form of the same file.
The schema never justified the refusal: the image_path field is
described as "Path to the image file to analyze"
(crates/engine/src/vision/tools.rs). Unlike the write and edit tools,
whose descriptions explicitly say "(relative or absolute)", it says
nothing about spelling at all — so refusing absolute spellings was an
undocumented narrowing of an underspecified input, not a pinned
contract.
Absolute spellings now resolve through the existing canonical
containment check, the same one relative spellings always went through:
an absolute spelling that canonicalizes inside the workspace resolves to
exactly what its relative form resolves to and is admitted; one that
canonicalizes outside still gets the containment refusal ("image_path
must resolve within the workspace and cannot escape it."). The absolute
branch applies the same lexical `..` guard the relative branch applies
(minus Prefix/RootDir, inherent to any valid absolute spelling), judged
before canonicalize so a missing escaping target still gets the boundary
message instead of a resolve error. The read-only envelope and the
symlink-resolves-outside refusal are unchanged. The test comments are
aligned with this invariant — in particular the trust-boundary pin in
execute_rejects_absolute_path, which previously read as refusing all
absolute spellings and now states that outside is rejected while inside
is admitted through the same canonical containment as the relative
spelling.
Verified: ./scripts/dev-cargo.sh test -p codewhale-engine --lib
vision::tools (17 passed; 7050 filtered out), including the new
resolve_image_path_accepts_absolute_paths_inside_the_workspace and the
pinned execute_rejects_absolute_path,
execute_rejects_parent_dir_traversal, and
execute_rejects_symlink_that_resolves_outside_workspace refusals
(resolve_image_path_accepts_windows_absolute_spellings_inside_the_
workspace is cfg(windows)-gated and runs on Windows only); the broader
./scripts/dev-cargo.sh test -p codewhale-engine --lib vision filter
(43 passed; 7024 filtered out — the "vision" substring also matches
"revision"-named tests) passes with 0 failures. No dependency on the
subagent write-claim change: vision resolves containment with its own
canonicalize-and-starts_with check, so this commit stands alone on base
7659d0a26.
Signed-off-by: asto18089 <[email protected]>
rustfmt --edition 2024 applied to the touched Rust files; their check failed on formatting only.
Harvested from PR #7226 by @asto18089
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Three classes of stale user-facing copy had drifted from en.json (the
single source of truth) in the shipped non-English locale packs:
1. Stale alternate-jump copy (12 packs): KbAltJumpPlanAgentYolo still
described the deleted Plan / Work / Operate alternate-jump in ca,
de, es-419, fr, hi, id, ja, ko, pt-BR, ru, uk, and vi. It now
translates the current behavior: jump to Plan/Work, or request
Full Access as a legacy alias. zh-Hans/zh-Hant were already correct.
2. Rail residue (87 keys): the rail UI was renamed to the workbar, but
rail words survived in ca (9), es-419 (9), fr (9), hi (9), id (9),
ja (10), ko (10), pt-BR (10), vi (9), de (1), ru (1), and uk (1).
Each key followed the current en.json value: keys whose English
value still says "workbar" now use the pack's workbar term (barra
de treball / Leiste / barra de trabajo / barre de travail /
वर्क बार / bilah kerja / ワークバー / 작업 바 / barra de trabalho /
робоча(я) панель / thanh làm việc), while keys whose English value
now talks about a dashed reasoning line or side placements
(ConfigChoiceDetailThinkingHighlightOff, ConfigHintThinkingHighlight,
ConfigHintWorkSurfacePlacement) follow that meaning instead of a
mechanical workbar substitution.
3. uk turn/step conflation (14 keys): uk said "крок" (step) where the
English counterpart says "turn" - KbReasoningDetail,
CmdTurnInspectDescription, KbTurnInspector, KbSteerCurrentTurn,
KbSelectedDetails, ModelPickerAuto{Network,Local}Hint, CmdCache*,
CmdTokensReport, and TelemetryNotice{,Compact}Body - now uses the
established "хід", matching existing uk usage in ApprovalStopBody,
ExecAfterHelp, and CmdCacheUnpricedNote. Keys where en says "step"
keep "крок".
en.json is untouched and byte-identical; key sets and ordering are
unchanged in every pack; all placeholders are preserved.
Gate: new test stale_mode_jump_and_rail_copy_is_swept_from_non_
english_locales asserts (a) no pack matches the stale jump markers
(Plan/Work/Operate, Plan-/Work-/Operate, 계획/실행/운영) or any single
residue mode ("operate", ko "운영"), with placeholder parity against
en for KbAltJumpPlanAgentYolo, (b) no rail-word residue remains in the
10 swept keys (id matches the English loanword "rail" too), with
placeholder parity against en, and (c) uk KbSteerCurrentTurn and
CmdTurnInspectDescription keep the хід/ходу turn stems instead of
крок. Verified red on base (stash of locale JSONs: FAILED - "ja still
describes the deleted Plan / Work / Operate alternate-jump") and green
after the sweep; the hardened assertions bite (id KbFocusSidebar
reverted to "panel rail" and uk KbSteerCurrentTurn to крок each failed
the gate, then were restored). Full suite:
cargo test -p codewhale-localization --lib --locked -> 55 passed, 0
failed, including shipped_complete_packs_have_raw_key_parity_with_
english and the placeholder-parity gates.
Signed-off-by: asto18089 <[email protected]>
rustfmt --edition 2024 applied to the touched Rust files; their check failed on formatting only.
Harvested from PR #7225 by @asto18089
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
WeCom stops accepting updates to a stream message after ~10 minutes and
answers errcode 846608. A turn that streams for longer lost every
remaining update, so the phone never saw the final result. Wrap the
replyStream calls in a helper that finalizes and reopens the stream near
the cap and reopens once when an update is rejected.
Conflicts with the merged #7211/#7212 and the harvested #7208 (same exports and tests in lib.mjs and lib.test.mjs) were resolved by keeping every side; node --check passes on both sources and npm test in integrations/wecom-bridge passes 22 of 22.
Harvested from PR #7210 by @gaord
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
WeCom SDK rejections arrive as plain {errcode, errmsg} objects; the
previous formatter stringified them into "[object Object]", so phone
users saw an opaque failure with no reason. Render errcode/errmsg (and
message/JSON fallbacks) so the reply names the actual cause.
Conflicts with the merged #7211 and #7212 (both add exports and tests in the same files) were resolved by keeping both sides; node --test (npm test in integrations/wecom-bridge) passes 20 of 20 after the merge.
Harvested from PR #7208 by @gaord
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
child_wall_time_is_unbounded_unless_an_owner_sets_it cancels the parent's token
on purpose so no provider request runs. run_prepared_subagent_task selects on
that token first, so a child that is polled before AgentTool start commits its
launch exits and drops the receiver, and start reports "prepared child
execution owner closed". That is the intended behavior for a cancelled parent,
but the test unwrapped the result and failed whenever a yield let the child run
first (blocked #7211, #7217, #7218, #7228, #7229 on ubuntu and windows). The
test now retries that one error up to 50 times and fails on any other error.
Verified: the test passed 25 of 25 consecutive runs after the change. It was not
run in a loop before the change, so the prior rate is unmeasured here. No hosted
CI.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
auto_review_force_prompt_is_denied_without_opening_a_modal read the thread's
events and the pending-approval count as soon as the mock engine saw the deny.
The mock outcome is observable when the decision is committed, but the runtime
appends approval.decided and calls cancel_pending_approval after that, so the
test could look first and fail with "auto-review hold must emit
approval.decided". It failed the Safety gate on several PRs and #7213 reported
2 failures in 5 runs on an unrelated change. The product order (receipt, then
event, then acknowledge) is intended, so the test now polls for the event with
the pending count at 0, within 2 seconds.
Verified: the test passed 15 of 15 consecutive runs after the change. The failure
rate before the change was not measured here, only taken from #7213's report.
No hosted CI.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
#7213 added workspace.canonicalize() inside the blocking search closure, which
is why 5c154502b7 recorded a path_canonicalize site for file_search.rs. The
workspace is now canonicalized with tokio::fs::canonicalize on the async side
and passed in, so the site is gone and the counter returns to 0.
Verified: scripts/dev-test.sh engine file_search 11/11 passed;
check-blocking-calls-budget.py (641 sites across 173 files, within budget, file
clean after --update); rustfmt --edition 2024 --check clean. The edit was
written by the in-app agent at 17:31 and proven here. No hosted CI.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A turn that had recorded 64 sourced usage receipts failed on the next one with
"captured usage sink did not acknowledge its source; exact receipt retained",
and the person lost the answer. Founder direction: no cap at all.
MAX_ROUTED_USAGE_RECORDS_PER_TURN is deleted with every branch that read it:
the source-receipt capacity bail, the routed/drop-record and decision-receipt
overflow markers, the diagnostics membership guard, both validate() bounds, and
the partition spill in append_owned_usage_source_record, which nothing can
reach once there is no bound (its four callers use append_usage_source_record).
Reading and settling existing partition records is unchanged. The two tests
that asserted the bound are rewritten or removed.
Together with the preceding revert, a sink refusal that is not about capacity
(owner lock loss, thread mismatch, load or save failure) is fatal again.
Verified locally on this tree: scripts/dev-test.sh engine
routed_usage_append_has_no_cap terminal_settlement_preserves_late_sink_receipts
2/2 passed; engine runtime_threads::tests:: usage partition 424/424 passed;
rustfmt --edition 2024 --check clean on the three files. The in-app agent's
uncommitted file_search.rs edit was in the working tree during both builds.
Not verified: no turn has been run past 64 requests against a provider. The
turn record is still rewritten on every request, so a very long turn writes more
per request; moving receipts out of the record is the larger change in
artifacts/handoff-20261105/USAGE-LEDGER-SIMPLIFICATION.md. No hosted CI.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Landing preflight on main still failed one counter: #7213 (a049aa62f1) added
`workspace.canonicalize()` inside the blocking search closure in
crates/engine/src/tools/file_search.rs, while the budget records 0. Record the
actual site so the gate reflects the tree again. The proper fix canonicalizes
on the async side before the closure (an edit is in flight in this checkout,
unverified at commit time); when it lands this counter returns to 0 with
--update.
Verified: python3 scripts/check-blocking-calls-budget.py (642 sites across 174
files, within budget).
A long turn died at exactly 64 model requests with "provider source admission
failed: captured usage sink did not acknowledge its source; exact receipt
retained". Two turns in the live profile show it (turn_2d28ea1cbd284bb on
2026-11-03 and turn_ee2e1f69448e45c on 2026-11-05, 457 s): both carry
routed_usage_source_ids = 64 and source_usage_records = 64, equal to
MAX_ROUTED_USAGE_RECORDS_PER_TURN, routed_usage_dropped_records = 1, and
modelRequestsStarted = 64. Append refused the 65th source, the sink returned
false, publish raised, admit_dispatch propagated, and model_step made the whole
turn fail. The person lost the answer because a bookkeeping ledger filled up.
Three parts, because the limit has a real job — the turn record is rewritten on
every model request, so an unbounded ledger makes those writes quadratic:
1. cost_status.rs publish: when the sink cannot acknowledge, retain the exact
receipt, warn, and return Ok(()). The Session destination already behaved
this way; Runtime was the outlier. The receipt is already in the journal for
a later admitted flush, so nothing is lost and the turn continues.
2. MAX_ROUTED_USAGE_RECORDS_PER_TURN 64 -> 4096. It also bounds
decision_receipts and a hot-path guard, so it stays a constant.
3. The one existing test whose loop hardcoded 0..90 now runs to
MAX_ROUTED_USAGE_RECORDS_PER_TURN + 26 so it can still reach the bound.
Verified: `cargo check -p codewhale-engine --locked` clean (1m36s);
`dev-test.sh engine routed_usage_append_is_bounded` passes 1/1 (7078 skipped)
at the raised bound. Both runs include a peer's uncommitted edits that were
present in this checkout at the time.
Not verified: no turn has been run past 64 requests since, so the end-to-end
"a long turn now finishes" observation is still owed. This changes how usage is
accounted for a turn that overflows: the ledger records an explicit
incompleteness marker and the receipt is settled later, so a reader of that
turn sees fewer inline records and must consult the retained journal.
Landing preflight went red on main for two counters that the tree already
exceeds: crates/util/src/files.rs path_canonicalize 2 -> 5 and
crates/tui/src/lib.rs std_fs 35 -> 36. The new canonicalize calls come from
the home-workspace state exemption (0ce030840b, 5ad3d821c7, 8f82d02e78);
both files are synchronous path helpers with no async context to move the work
off, and the other calls were already present when the ratchet was recorded.
--update rewrites only these two numbers (641 sites across 173 files), so no
other drift is folded in.
Verified: python3 scripts/check-blocking-calls-budget.py passes (all other
budgets unchanged); no test run needed for a recorded counter.
Adds a Fixed entry and Contributors credit for #7223 (asto18089), merged as
d4fc3646c5, and regenerates crates/tui/CHANGELOG.md with
scripts/sync-changelog.sh.
Verified: scripts/check-contributor-credit.py reports every contributor in the
window credited on all three surfaces. Docs only; no build or test run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A turn that had admitted MAX_ROUTED_USAGE_RECORDS_PER_TURN (64) sourced
receipts failed on the next settled receipt: append_usage_source_record
refused capacity, the Runtime usage sink returned false, publish retained the
receipt and returned "captured usage sink did not acknowledge its source;
exact receipt retained", and the end-of-turn settlement downgraded a completed
turn to Failed with "Turn usage remains unacknowledged: ...". Once refused
receipts filled the per-owner journal, the next request failed before it was
dispatched with "native usage journal capacity reached before dispatch".
The partition records already exist for this: a receipt past the turn's bound
is written to its own turn record and summed into the thread totals, so the
parent record stays bounded while the totals stay exact. That path was
unreachable once routed_usage_source_ids and source_usage_records disagreed
(the model-request diagnostics path records membership without a receipt) or
before the first settled admission. Test the bound itself instead, so a new
settled receipt at the bound always partitions. An inherited turn still
refuses to create an original-owner partition, and every non-capacity sink
refusal (owner lock loss, thread mismatch, load failure, save failure) stays
fatal.
A broader variant - raise the bound to 4096 and treat every sink refusal as
non-fatal - was reverted. It would let those integrity failures continue with
unrecorded spend, and raising the bound only moves the wall because the turn
record is rewritten on every request. Reference behavior (Codex, Grok Build:
running totals, warn-and-continue on persistence failure) and our extra
exactly-once/budget guarantees are written up in
artifacts/handoff-20261105/ENGINE-USAGE-CAP-REFS.md.
Verified: scripts/dev-cargo.sh check -p codewhale-engine --lib --locked
(Finished in 7m 46s, no errors); rustfmt --edition 2024 --check clean. The
focused nextest runs (routed_usage_append_is_bounded,
terminal_settlement_preserves_late_sink_receipts) were started twice and both
times invalidated by concurrent edits from another agent in this shared
checkout, so they are not claimed. No existing test covers
overflow-to-partition, and no new test was written (AGENTS.md).
Adds an Added entry for #7211 (gaord) and a Fixed entry for #7207
(SparkofSpike) with Contributors credit, and regenerates crates/tui/CHANGELOG.md
with scripts/sync-changelog.sh.
Verified: scripts/check-contributor-credit.py reports every contributor in the
window credited on all three surfaces. Docs only; no build or test run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds Fixed entries and Contributors credit for #7205 (Lstarsky0) and #7229
(asto18089), and regenerates crates/tui/CHANGELOG.md with
scripts/sync-changelog.sh.
Verified: scripts/check-contributor-credit.py reports every contributor in the
window credited on all three surfaces. Docs only; no build or test run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds Fixed entries and Contributors credit for #7215 (Lstarsky0) and #7227
(asto18089), merged as d3e2edf231 and f44525ae34, and regenerates
crates/tui/CHANGELOG.md with scripts/sync-changelog.sh.
Verified: scripts/check-contributor-credit.py reports every contributor in the
window credited on all three surfaces; sync-changelog rewrote only the TUI
slice. Docs only; no build or test run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
crates/tui/CHANGELOG.md is derived from CHANGELOG.md by scripts/sync-changelog.sh
and I edited CHANGELOG.md for the contributor credits without running it, which
turned the Landing preflight's 'TUI changelog slice' check red on main.
Verified: scripts/sync-changelog.sh rewrote only crates/tui/CHANGELOG.md. The
blocking-calls budget failure on main (file_search.rs, from #7213) is separate
and not addressed here.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds the Added entry and Contributors credit for #7212 (gaord), merged as
0ba20139e7.
Verified: scripts/check-contributor-credit.py reports every contributor in the
window credited on all three surfaces. Docs only; no build or test run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds the Fixed entry and Contributors credit for #7209 (gaord), merged as
51cc867aa5.
Verified: scripts/check-contributor-credit.py reports every contributor in the
window credited on all three surfaces. Docs only; no build or test run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Extends the issue triage workflow (the predecessor it replaces: its area and os
rules, which targeted label names that did not exist on the repository, so they
never fired) to pull_request_target. Pull requests now get area labels from the
files they change (including the desktop, web and mobile paths the apps will
land under), a type label from the conventional-commit title prefix, and
contributor:external for authors outside the team. It adds labels only, and only
ones that already exist; nothing is enforced and nothing is removed.
Issue rules gain desktop, web, mobile, accessibility, freebsd and ohos matches;
the sandbox hint now maps to area:sandbox. CONTRIBUTING.md documents the label
families.
Repository labels changed alongside this commit (applied with gh label edit /
create, history preserved on rename): tui, mcp, tools, plugins, providers,
packaging, runtime-api, remote-control, ci, sandbox, subagents, localization ->
area:*; windows, linux, macos -> os:*; created area:core, area:state,
area:desktop, area:web, area:mobile, os:freebsd, os:ohos, lang:zh,
priority:p0/p1/p2, accessibility, contributor:external. No workflow, template or
script referenced the renamed names; stale.yml's security label is untouched.
Verified: actionlint on triage.yml clean; the script body executed in node
against nine mocked events (PR by path, docs-only, tools-in-engine, engine core,
over-broad PR, future desktop/web paths, bot, issue with desktop+Windows, issue
with accessibility+mobile) and produced the expected labels. Not verified: a live
GitHub run (pull_request_target runs from the base branch only after this lands).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds Fixed entries and Contributors-block credit for #7213 (Lstarsky0), #7224
and #7222 (asto18089), merged as merge commits a049aa62f1, 671f50977c and
b0be3cb761. Both handles were already present in web/lib/release-credits.ts.
Verified: scripts/check-contributor-credit.py reports every contributor in the
window credited on all three surfaces. No build or test run (docs only).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
GET /v1/runtime/info capabilities gain thread_scoped_reads (default false for
older payloads) so clients can require it before sending thread_id to the
git/diff/file read routes: older Engines reject the unknown query parameter.
Schema, TypeScript and SDK declarations are regenerated with
codewhale-app-server-schema --write (the --check passes). RUNTIME_API.md
documents the capability, status_unavailable on GET /v1/workspace/status and
GET /v1/git, and the filter-driver scope rule (repository-defined drivers are
neutralized on status reads; user global/system drivers such as Git LFS keep
working).
Verified (local, source only): full workspace nextest 19068 passed with this
change; schema --check clean. Not verified: the desktop consuming the flag
(next), docs/zh_hans/RUNTIME_API.md has no matching section yet and needs
alignment at release.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The CI Lint job (clippy --workspace --all-targets --all-features with
uninlined_format_args, too_many_arguments and unnecessary_map_or allowed) failed
with 22 errors in codewhale-engine plus the ones hidden behind them in app-server,
tui and cli; Test (windows) stopped at two -D warnings unused imports in
codewhale-app-server. Mechanical fixes were applied with clippy --fix (collapsible
if/let chains, needless borrows, unneeded `..` patterns, `?`, is_multiple_of).
By hand: the mock-approval field type gets a MockApprovalPublications alias;
MailboxMessage::TokenUsage.record is Option<Box<RuntimeUsageRecord>> (about 900
bytes dwarfed every other variant; serialization is unchanged); the WebSocket
handshake origin refusal is a named function carrying
#[allow(clippy::result_large_err)] because tungstenite's callback signature fixes
the error type; auth.rs tests move to the end of the file; the unused
EnvVarGuard/lock_test_env import in git.rs and std::sync::Arc in bridge.rs are
gated to unix (their only users are unix tests).
Verified (local, source only, rustc 1.99.0): clippy exits 0 with CI's exact flags
across the workspace; cargo fmt --check clean; scripts/preflight.sh --check OK;
full workspace nextest: 19068 run, 19068 passed, 32 skipped. Not verified: hosted
clippy on the runner's rustc 1.99.1, the Windows job (no Windows host: only
hosted CI can confirm the two cfg gates and anything behind them in later crates).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
GET /v1/workspace/status and the git status detail spawned `git status
--porcelain=v1` with only core.fsmonitor and core.hooksPath disabled, so a
repository-configured clean or process filter executed (observed on native-v36:
filter-clean and filter-process markers written), and a failing process filter
made git exit nonzero so the route silently reported dirty=false with zero counts
on a dirty repository. Status reads now go through Git::status_command, which
enumerates filter drivers with `git config --show-scope` and neutralizes any
driver with a definition in a non-global/system scope (local, worktree, other),
including a local redefinition of a global driver; drivers defined only in the
user's global or system config, such as Git LFS, keep working. If the scoped probe
fails it falls back to the all-scope neutralizer (fail closed). --ignore-submodules
=dirty stops a repo-controlled submodule from running its own drivers. Status runs
once in collect_workspace_status (it ran twice). A git failure no longer reads as
clean: WorkspaceStatusResponse and GitStatusDetailResponse gain
status_unavailable (serialize-only, no generated schema references). The existing
status_and_guards_respect_clean_filters check keeps its assertions and now
configures its filter in a global-scope config via GIT_CONFIG_GLOBAL.
Verified (local, source only): app-server workspace/git/status/runtime_api and
dependencies checks: 527 run, 526 passed; the one failure,
owner_bootstrap two_crash_reentries_converge..., is flaky (passed once and failed
once earlier). Not verified: the P12 observer against a rebuilt server with the
scoped neutralizer (and a global-scope driver fixture), a Git LFS repository, the
thread-summary dirty (runtime_api.rs:3995) which still fails open, the TUI callers
(fleet_draft, init) and review_sync/diff reads, which still neutralize all drivers
or spawn unhardened git; docs for status_unavailable not yet written.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Sessions saved by v0.9.10-v0.9.12 while sub-agents were live carry a
broken Agent-topology sidecar placement. The sidecar checkpoint shipped
in de062ebd1 with END placement (appended after the replacement
history); the placement anchor landed only in 1f5f3a7e6, first released
in v0.10.0. A v0.9.10-v0.9.12 session therefore stores
`[..., tool result, summary (user), topology sidecar (user)]`. Restoring
such a session on current builds rewrote the sidecar in place and, at
request time, moved only the summary - so the restored carrier stayed
directly after a role:"tool" message, and strict paired Chat-Completions
templates rejected the first request with HTTP 400 (role "user" after a
tool result).
Fix: restore now relocates the carrier to its placement anchor - the
same boundary `replace_agent_topology_checkpoint` writes at save time
(after the latest genuine user prompt, before that round's
assistant/tool chain, else before the first assistant message). The new
`relocate_restored_compaction_topology` recognizes both the live sidecar
envelope and the restored two-block display form, runs on both restore
paths in `restore_compaction_checkpoint` (checkpoint-less restores
relocate only and delete nothing), and derives the summary boundary from
the history's era (`history_is_post_provenance` +
`is_compaction_checkpoint_message_in_era`). In a history carrying a
provenance-stamped carrier, a user turn that merely pastes a summary
header is content - never the boundary, never moved, never touched. In a
pre-provenance history a pasted legacy header still reads as the
boundary, and the carrier anchors in front of it while the pasted text
stays verbatim. The anchor's prompt scan applies the same era-aware
exclusion, so a bare legacy summary is never mistaken for the prompt the
carrier anchors after; when the boundary window is empty (a summary at
index 0), the carrier deliberately moves to index 0, mirroring the
save-time fallback chain. Save-time `replace_agent_topology_checkpoint`
now retires both carrier forms before inserting the fresh sidecar, so a
restored carrier that reached the live round cannot persist beside the
fresh sidecar as a superseded duplicate. `carries_tool_result` is
extracted from the save-time placement rule and shared, so save and
restore cannot drift. Engine-saved sessions saved by the placement anchor restore
byte-identical (beyond the standard projection; a replacement history
carries exactly one checkpoint), while a host-imported history carrying
an older summary before an already-anchored carrier is shuffled -
wire-legal, and it self-heals at the next save via the both-forms
retire.
The checkpoint-None path no longer drops wire-format checkpoints:
production derives the checkpoint from history, so the path is
unreachable that way, and keeping them matches the legacy branch's
documented intent. The TUI App mirror (`install_restored_api_messages`
for /fork, //branch and //import) stays projection-only and
engine-authoritative; transient mirror divergence self-heals on the
next engine SessionUpdated. One canonicalization gap remains: a carrier
saved directly after an assistant-like message with no checkpoint in its
window stays in place, which is wire-legal (no 400).
Evidence: `restore_relocates_pre_anchor_topology_sidecar_saved_after_tool_result`
was written first and failed on unmodified main with
`left: [3], right: [1]` - the restored carrier sat directly after the
tool result instead of at its anchor (test result: FAILED. 0 passed;
1 failed; 7066 filtered out). With the fix the same test passes, and the
new wire-level test `restore_repairs_pre_anchor_topology_sidecar_wire`
asserts restoring the v0.9.12-shaped history then building chat
messages yields no tool-to-user adjacency. Full engine lib suite: 88
failures on the fix run and 88 on pristine main - the same
machine-environment failures, with sets differing by ~3 timing-flaky
tests per side (85 common to both). The new tests pass individually by
name and are never counted on top of that baseline.
Signed-off-by: asto18089 <[email protected]>
A write-capable child spawned with a workspace scope could not spell its
target as an absolute path, even when that path resolved inside its own
claimed workspace: mutation_paths fed the raw string to
normalize_claim_path, which hard-refuses any absolute spelling ("write
scope path must be repo-relative without traversal"), so enforce_write_claim
refused the write. The same target spelled relatively succeeded, because
the write tool resolves inputs against the child's workspace either way —
one file, two spellings, opposite outcomes. The refusal also sat below
the tool surface the model sees: the write and edit schemas already
advertise both spellings — "Path to the file to write (relative or
absolute)." and "Path to the file to edit (relative or absolute)."
(crates/engine/src/tools/file_tool.rs).
mutation_paths now takes the child's workspace, normalizes it once per
batch (normalize_subagent_workspace), and strips the workspace prefix from
absolute spellings before claim validation, canonicalizing the deepest
existing ancestor so a target the write will create (leaf not yet on disk)
resolves like the tool would. The strip delegates to the core's single
resolve_deepest_existing walk (now pub(crate)) — the same one behind the
write carve-out's real-path check — so the claim lane cannot drift from it
on symlinked or partially-existing spellings.
Still refused, exactly as before: absolute paths outside the workspace,
relative and absolute `..` traversal, symlinked ancestors that resolve
outside the workspace, and the existing normalize_claim_path bounds
(length, control characters, non-normal components).
Scope: write-claim coordination is subagent authorization plumbing, not
sandbox/execpolicy — admission is decided entirely inside crates/engine
and no crate other than crates/engine is touched. Sibling lanes that
remain repo-relative-only by documented contract are deliberately
unchanged so reviewers do not discover them as missed siblings: the
write_roots/exact_files/coordination_contracts scope declarations and
the deliverables/CHANGES reporting lane per docs/SUBAGENTS.md ("At most
16 repo-relative file paths are accepted. Absolute paths, traversal,
repository metadata paths, and symlink traversal are refused."; "List
edited repo-relative file paths under `### CHANGES`").
The acceptance test
scoped_mutation_paths_accept_absolute_targets_inside_the_workspace
covers the existing-target, not-yet-existing-target, and scope-lane
admissions, and pins the outside-workspace, relative and absolute
traversal, and symlink-hop refusals.
Verified: ./scripts/dev-test.sh engine scoped_mutation_paths (1 passed;
7066 filtered out — the acceptance test) and ./scripts/dev-test.sh
engine mutation (20 passed; 7047 filtered out — the mutation_paths
filter, including the pinned outside-workspace, traversal, and
symlink-hop refusals). No dependency on the vision change: the claim
lane uses resolve_deepest_existing (pub(crate)) from crates/engine
core, so this commit stands alone on base 7659d0a26.
Signed-off-by: asto18089 <[email protected]>
git_repo_root walked up from the workspace and scanned the children of
every ancestor for nested checkouts, up to MAX_PARENT_LEVELS. A session
workspace a few levels below the home directory that is not itself a
repository therefore made the walk reach an ancestor whose children
include the user's unrelated project checkouts and fail with "Multiple
git repositories found under /Users/<name>. Specify cwd to
disambiguate" over projects that have nothing to do with the workspace.
Only the anchor itself (levels == 0) may adopt nested checkouts now —
that is the discovery path exercised by harness or session workspaces
that wrap a single repository. The guard changes exactly that scan:
ancestors keep their containment probe but no longer scan their
children; deepest-repo-wins ordering at the anchor and the error texts
are unchanged. The narrowing also drops base's silent single-repo
ancestor adoption — a non-repository workspace under a directory whose
children contain exactly one repository was silently adopted before.
That removal is intentional: the same defect family as the multi-repo
ambiguity error.
Evidence (codewhale-engine):
- the new regression test
create_isolated_worktree_ignores_unrelated_repos_above_the_workspace
(<root>/RepoA, <root>/RepoB, workspace <root>/a/b/c/workspace, not a
repo) is red on base: "Multiple git repositories found under
/private/var/.../.tmp.... Specify cwd to disambiguate: .../RepoA,
.../RepoB" (1 failed; 7066 filtered out);
- green with the guard: cargo check passes and the worktree-filtered
test module passes (40 passed; 0 failed), including
create_isolated_worktree_discovers_nested_repo_from_harness_parent.
Signed-off-by: asto18089 <[email protected]>
The child per-step deadline was request_dispatched_at + step_api_timeout, and
request_dispatched_at is taken before the child's dispatch admission, which does
a durable publish and a settled projection. When that local accounting work
exceeded the step timeout, the biased select in process_stream saw the deadline on
its first poll, never read the already-buffered MessageStart carrying the
provider's usage, and settled the receipt as Missing / request-outcome-unknown.
The deadline now starts when stream processing starts; opening the stream keeps
its own step_api_timeout, so the worst case is one open budget plus one body
budget. No timeout is raised and no receipt or accounting guarantee is weakened.
Verified (local, existing checks, source only): 983 child, tools::subagent and
turn_loop checks passed, 0 failed; child_host
child_step_timeout_interrupts_open_and_body_without_replay_and_keeps_exact_sources
passed 3 of 3 reruns (it was the last failing test in the 19068-test workspace run,
19067 passing). Not verified: a full workspace rerun after this change.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The turn layer already treats human waits as outside the budget:
TurnWallClock's doc in crates/runtime/src/turn_budget.rs says "the
budget bounds what the agent spends on its own, not how long a person
takes to answer. Without that exclusion an approval prompt left open
overnight would fail the turn — and discard the work the user just
approved — the moment they came back." The durable task layer did not
follow that philosophy: ExecutionGuard::evaluate charged the whole wall
clock, so an automation or /v1/triggers run parked on an approval or
user-input prompt was interrupted at the 30-minute wall_time default,
withdrawing the pending approval on the host's behalf
(ApprovalOutcome::Cancelled — a withdrawal, not a denial) and
discarding the run — exactly what the turn layer's own doc says must
not happen.
That wall deadline was a deliberate bound, not an unnoticed bug: the
journal's #6118 note — "`0` waits indefinitely by configuration; the
wall deadline still bounds the run" — accepted it as the backstop for
prompts nobody could ever answer. This change relaxes that bound on
purpose for the automation-with-no-operator case; it extends the
turn-layer exclusion to the task layer, opt-in only.
TaskManagerConfig::human_waits_answerable (default false, the
from_runtime default) gates the new journal arms: where a host can
actually deliver a decision — today only the runtime API server, whose
HTTP POST /v1/approvals/{id} and user-input endpoints reach a task
thread's engine turn — approval.required/user_input.required open a
human-wait window and the paired decided/answered/timeout/canceled
events close it, so an answer submitted past the wall budget completes
the task instead of being collected and dropped. The window is also
published as a supervisor-side HumanWaitStarted/Ended task event pair so
the worker supervisor's guard follows in lockstep; surfaces without a
delivery channel (the TUI's private task runtime) never arm the window
and their wall clock behaves exactly as before. The exclusion is
bounded: ExecutionGuard tracks the open window and the total across the
whole task, subtracts both from wall elapsed and moves the wall deadline
with them (#5898's crossed-first attribution and the idle deadline
still decide the label), and a fail-safe human_wait_cap (24h aggregate,
default) terminalizes HumanWaitTimeout when windows sum past it —
checked after shutdown/cancel so a Canceled/Shutdown task is never
mislabeled. A window that is parked backs the journal poll off from the
200ms catch-up cadence, doubling up to a 2s cap; an arriving decision
wakes the loop through the event subscription, so the backoff only
removes empty polls. Closing a window restarts the idle budget fresh,
composing with #6118's in-window idle suppression (which is unchanged)
to cover the model's own post-decision latency. The `[approval]
timeout_seconds` semantics are untouched.
docs/features.toml: the thread-scheduled-turns row now says a scheduled
run parked on a human answer pauses its wall clock (the registry's
120-character row limit shaped the wording).
Verified (local, existing checks, source only):
`scripts/dev-cargo.sh test -p codewhale-engine --lib --locked --
human_wait execution_guard unanswerable chained`: 21 passed, 0 failed
(reason serialization, parked backoff 800ms/2s literals and the
aggregate-budget wake, cap between windows, cancel/shutdown outranking
a spent cap plus its parked-window twin with the window still OPEN,
idle restart on window close, from_runtime default, wall exclusion end
to end, fail-safe cap, chained sub-cap windows, supervisor signal
pair, unanswerable default, and the moved-deadline wall-timeout
attribution pin); `execution_guard_parked_wait_respects_the_aggregate_budget`
added and green; `scripts/dev-cargo.sh test -p codewhale-app-server
--lib -- server_task`: 2 passed, 0 failed (builder pin plus the wiring
pin that reads the flag off the executor the manager actually runs
tasks on, through the cfg(any(test, test-support)) as_engine_executor
seam — these two pins cover the builder and executor-forward hops; the
run_http_server call site itself is not pinned, since pinning it would
require booting the server);
`scripts/dev-cargo.sh test -p codewhale-engine --lib --locked --
task_manager::`: 101 passed, 0 failed, 1 ignored (module regression,
including the #5898 and #6118 pins);
`scripts/dev-cargo.sh test -p codewhale-tui --test feature_registry`:
2 passed, 0 failed.
Red evidence, each observed under a source mutation and green again
after restore, all against the same suite:
- reverting evaluate() to the raw wall clock reds
execution_guard_human_wait_close_restarts_the_idle_budget
("a closed window must restart idle, got Interrupt { reason:
WallTimeout }") — the wall deadline must move with excluded windows;
- hoisting the aggregate cap check above the shutdown/cancel
classification reds execution_guard_cancel_outranks_a_spent_aggregate_cap
and execution_guard_shutdown_outranks_a_spent_aggregate_cap
(HumanWaitTimeout vs Canceled/Shutdown) — the ordering the fork
learned the hard way;
- dropping the between-windows aggregate arm reds
execution_guard_spent_aggregate_cap_terminalizes_between_windows;
- removing the answerability gate reds
unanswerable_surface_keeps_the_wall_clock_running_through_a_prompt
(the task parks past its wall budget instead of WallTimeout);
- hardcoding server_task_config's opt-in to false reds both server
pins ("server task config must arm answerable human waits").
During development the first cut of the deadline arithmetic kept the
raw started_at + wall_time deadline and was caught red by the
idle-restart pin before any commit; the deadline now extends by every
excluded window.
Signed-off-by: asto18089 <[email protected]>
Formatting only; fixes the hosted Landing preflight rustfmt failure on the recent test-fixture commits.
Verified: cargo fmt --all --check clean after this commit (no behavior change).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The child unknown-origin, guardian missing-coverage and event-capacity checks
asserted drain() counts that cannot hold in the settled-usage model: the pending
pool holds one record per revision of a source, and priced/unpriced counts exist
only after SessionCostSnapshot::project_usage_record folds them (replacing by
source fingerprint, so no double counting). Their fixtures now save the origin
session first so publishing reaches the pending pool, and the assertions fold the
records and keep the one-source fingerprint and unknown-outcome checks. The
event-capacity check keeps priced_turns 0 and expects unpriced_turns 2 (the
cancelled child's unknown outcome plus the root turn's unpriced mock-model
usage) and one missing source; an assertion on a field that exists only on the
old drain type was dropped.
Verified (local, source only): the three checks pass in the full workspace run
(19068 run, 19067 passed). Still failing: child_host
child_step_timeout_interrupts_open_and_body_without_replay_and_keeps_exact_sources
('the child's usage receipt settles: Elapsed'), undiagnosed; it was flaky before
and is deterministic in the saved-session fixture.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Since 20f8244aaf every snapshot/checkpoint load took the exclusive
write-admission lock to mint an optional write receipt, which creates the
late-usage sidecar directory and lock leaf, so a pure read wrote to disk and
could fail on read-only storage. Loads now observe the session/checkpoint pair
under the shared, non-creating read lock (with_session_observation, which returns
None for a tombstoned session); an absent lock leaf is read unlocked, and when no
observation can be taken the existing degrade path returns the transcript without
a write receipt (the commit message of 20f8244aaf states receipts are optional).
save/rename/archive/delete/capture_session_write_source/resume_session and the
admitted branches are unchanged, and publish_session_inner still refuses a value
with no captured native producer, so a session loaded without a receipt cannot
publish until a caller captures one under write admission. One behavior
difference: a read no longer runs ledger sync/retirement on a deletion marker; it
returns NotFound and the next writer cleans up. A first-ever save racing a read
before the lock leaf exists can surface the hash-mismatch ResourceBusy error where
the exclusive lock used to wait.
Verified (local, existing checks, source only): full workspace nextest run with
this change: 19068 run, 19067 passed, 1 failed (child_step_timeout, unrelated);
session_manager::tests::late_usage_reads_do_not_create_accounting_storage passes
unchanged, as do the decision-receipt, tombstone and publication-fence checks.
Not verified: the TUI persistence_actor handling of a load that degraded to no
receipt (it was not read), a read-only-media observer.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
productive_tool_results_do_not_hit_no_user_input_backstop, enforce_lru_capacity_does_not_loop_when_all_threads_are_active (its fixed child id was already terminal in the shared temp-dir sub-agent store from an earlier run; it now uses a unique child name) and rlm::bridge llm_batch_dispatch_pins_configured_child_model are repaired as stale fixtures; no product code, caps or accounting semantics changed.
Verified (local, source only): full workspace nextest run after these edits: 19068 run, 19063 passed, 5 failed (67 -> 37 -> 11 -> 8 -> 5 across the series). Still failing and unresolved, stated plainly: child_host child_cancelled_dispatched_open_keeps_unknown_origin (now 2 vs 1 unpriced turns after the fixture fix) and child_step_timeout (usage receipt settle times out), the auto_review guardian missing-coverage receipt check (2 vs 1), event_capacity_cancelled_repl_child (provider source admission fails with the writer's attempted fixture, reverted), and session_manager late_usage_reads_do_not_create_accounting_storage (product decision: reads now create the sidecar directory since 20f8244aaf). The partial edits for the child_host and guardian checks are not committed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Two regressions from the settled-usage and write-admission changes. (1) A settled
late-usage record that carries a bounded routing decision took the settled branch
and continued before its decision receipt reached cost.route_receipts, so the
receipt vanished on replay although the ledger kept it; the insert now runs once
for every record before the diagnostic/settled branches. (2) After 20f8244aaf
added a hash check against the per-session checkpoint, the legacy latest.json
was read with Some(session_id), the check failed with ResourceBusy and the error
was swallowed, so `--continue` reported the legacy checkpoint as preserved but
not resumable; it is read with None, as load_legacy_checkpoint already does, and
the identity filter on the next line still binds the session.
Verified (local, existing checks, source only): 187 session_manager, TUI
setup_helper and checkpoint checks run, 186 passed. Fixed by this commit:
decision_receipt_survives_restart_replay_and_session_deletion,
list_checkpoints_includes_legacy_slot_and_skips_offline_queue and TUI
setup_helper_tests::continue_recovers_legacy_checkpoint_without_removing_it.
Still failing, not changed here:
late_usage_reads_do_not_create_accounting_storage: since 20f8244aaf every
snapshot load takes the write-admission lock, which creates the sidecar directory,
so a pure read now creates accounting storage (and may fail on a read-only
sessions directory). Whether reads must stay write-free is a product decision.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Since ccc087723c every RLM/child dispatch ends as a settled RuntimeUsageRecord
(a missing or unknown request carries coverage Missing and a reason; there is no
drop guard or dropped_records entry), so the RLM bridge/turn/rlm_host checks now
assert settled records instead of drop_records. The guardian/auto-review fixtures
get their own per-test sub-agent store (the shared temp store hit another live
test process's writer lock). cost_status gains a test-only fallback so a runtime
usage sink registered on one thread resolves when a multi-thread runtime resumes
the task on another (still ambiguous if two scopes name the same owner). The
decision-router check follows the current coverage for HTTP 402/500 (Missing),
the default-pricing coverage list drops the novita/siliconflow routes that now
have reviewed pricing, and conformance goldens/marketplace checks follow the 0.10.3
changes.
Verified (local, source only): full workspace nextest run before this commit:
19068 run, 19057 passed, 11 failed (37 and 67 in the two prior runs). The 11 still
failing, stated plainly: 4 session_manager/TUI setup-helper checks (a product
edit in session_manager.rs was reverted pending review), the two child_host
unknown-origin/step-timeout checks, event_capacity_cancelled_repl_child,
productive_tool_results_do_not_hit_no_user_input_backstop,
rlm::bridge llm_batch_dispatch_pins_configured_child_model,
enforce_lru_capacity_does_not_loop, and the auto_review missing-coverage receipt
check. Not verified: the regenerated conformance goldens against an independent
oracle.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The conformance goldens are regenerated for lawful consequences of the batch:
the tool catalog bytes and hashes change (two JSONPath descriptions grew ~269
bytes in b8c5417927, tools_bytes 18575 -> 18844, so tools_sha256, prefix and
prefix_cache_change descriptions move), provider request receipts gain an
accounting_error field, and the workspace listing includes the new sub-agent
store layout file. The harness treats request_id as volatile and ignores the
subagents.v3.writer.boot_ files. The bundled marketplace checks follow the new
first-party pin (21 candidates, Computer Use v0.16.0 asset URL); the exec
stream-drop acceptance check and a TUI test literal follow the current types.
Verified (local, source only): full workspace run after these changes had 37
failures in total; conformance::events::golden_turn_events_match,
bundled_catalog_is_offline_installable..., and
plugin_e2e_acceptance::plugin_toml_binary_lifecycle... still FAIL and are not
fixed by this commit (they appear in the run). Goldens were regenerated by a
writer; reviewers should diff them against the stated causes.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Updates the existing command_catalog HTTP check's expectations for the current
turn dispatch behavior (it failed identically on binaries built before this
series).
Verified (local): not rerun separately; the full suite run still listed
get_v1_commands_serves_the_catalog_over_http as failing, so this does not fix
it. Kept as the writer's partial change; do not rely on it.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Product: SubAgentManager::expand_write_claim returned its result as an Err inside
a store transaction, so a rejected expansion lost its contention record on
rollback; the closure now returns Ok(..) and the caller still gets the Err.
Test support: the captured RLM caller and MockLlmClient now follow the real
contracts (test-only session origin for RLM callers; the mock records an outbound
transmission and settles it OutcomeUnknown/200 so guardian usage coverage is not
NotDispatched). Fixtures: sub-agent agents and workers are created inside store
transactions with a session owner, contexts recapture the accounting origin,
the cancel-test setup is persisted, replay checks use the lawful settled schema
version, session secret scrub saves through the manager.
Verified (local, source only): full workspace nextest run before: 19069 run, 67
failed; after this and the TUI/plugin changes: 19068 run, 19031 passed, 37 failed
(35 of the 67 now pass; 5 other tests newly failed). Not verified/unresolved,
stated plainly: the two budget_handback cancellation checks, ~10 rlm
bridge/turn checks, the auto_review_* guardian family (5 of 8 still fail), the
MCP action approval gate (child_host probe drops the runtime MCP server scope),
child_host unknown-origin checks, enforce_lru_capacity, the 201-round backstop,
and several flaky timing checks. parent_agent_surface ceiling stays 92100: two
JSONPath descriptions grew ~291 bytes in b8c54179271; headroom is 22 bytes.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The verify tool's model-visible description said "This is not a test
runner (use run_verifiers)". That is dead guidance by upstream's own
contract: RunVerifiersTool::model_visible() returns false
(tools/verifier.rs), so the model-visible catalog never offers it, and
"run_verifiers" is listed in RETIRED_TOOL_NAMES
(tools/canonical_action_test_support.rs). A model following the hint
would attempt a call it can never make - exactly what the #6747
invariant assert_text_names_only_callable_tools ("no hidden name, no
retired name") exists to prevent.
Point the hint at the canonical surface instead: the Run tool, which is
model-visible and dispatches action="verifiers" to RunVerifiersTool
(tools/run_tool.rs), matching how goal.rs already teaches the same
form ("Run action=\"verifiers\"").
Guard coverage: no_advertised_tool_teaches_a_retired_name built its
registry without the verify tool, so this surface was never scanned.
Extend the builder chain with .with_verify_tool(None, ...); VerifyTool
is model-visible, so to_api_tools() now includes its description in the
retired-name scan.
Red/green evidence:
- red: with the old text, the extended guard fails with "tool `verify`
advertises the retired name `run_verifiers`; name the canonical
action form instead"
- green: with the new text, all 9 canonical_action_tests tests pass,
all 8 tools::verify and 9 tools::verifier tests pass, and cargo
check -p codewhale-engine is clean.
Signed-off-by: asto18089 <[email protected]>
fetch_url is an in-process HTTP client: it is a subject of the network
policy but has no OS sandbox of its own. The sandbox contrast belongs to
exec_shell, whose sandbox policy may deny outbound network, so move the
parenthetical there and keep the network-policy and decoding claims about
fetch_url itself.
Signed-off-by: asto18089 <[email protected]>
Preserve quota-window transmission and caller recovery semantics, bound
native JSON inspection and settle its refusals, retain server minima and
account guidance, and restore ordinary transport recovery receipts.
Keep the existing typed LlmError contract at the inspection boundary and
preserve the original worker assertions and deadlines.
Validation on Windows with rustc 1.99.1 and locked dependencies:
- Rate-limit filter: 37 passed; scoped quota/receipts: 16 passed
- Responses unknown outcomes: 2 passed; official quota guidance: 1 passed
- Long minimum, Engine receipt, child classification and wall budget:
1 passed each; retry configuration: 3 passed
- Runtime retry status: 7 passed; parser negative control confirmed
- Changed-file formatting passed
- Strict Clippy retains 23 pre-existing findings; full formatting retains
the unchanged app-server test formatting failure
An earlier checkpoint deadline timeout is retained in the evidence. The
final complete rate-limit filter passed with its original deadline.
Signed-off-by: z27014 <[email protected]>
- Kill the child after ten seconds and cap its stdout at 16 MiB, reading on
a helper thread so a child that fills the pipe cannot deadlock the wait.
A clipboard owner that never renders the format froze the TUI before.
- Use the absolute %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe
instead of a bare name resolved through PATH.
Not changed: the fallback still runs whenever arboard's text and image reads
both fail, which is the only signal available - arboard does not expose the
clipboard's format list, so "no native image" cannot be told apart from
"nothing on the clipboard" without a second Win32 query.
The scratch workflow commit is gone: this branch is cut from upstream main.
Verified with: cargo fmt --all -- --check. Compilation and tests were not
re-run for this commit at the reporter's request; CI is the first build.
arboard reads the Win32 clipboard through CF_DIB/CF_DIBV5. QQ's screenshot
publishes the picture under the same trio (Bitmap, DeviceIndependentBitmap,
and Format17) yet the native read comes back empty; the picture only becomes
readable after it has been pasted into another application first.
.NET's Clipboard.GetImage() does read it (249x171 in the reported case), so
fall back to a one-shot PowerShell that saves the bitmap as PNG and returns it
as base64. The bytes go through the same no-follow clipboard-image writer, so
the destination keeps its owner-only, link-refusing guarantees.
The fallback runs only on Windows and only after arboard already failed, so
the common path pays nothing.
Verified with: cargo fmt --all -- --check; cargo check -p codewhale-tui --lib
--tests; cargo test -p codewhale-tui --lib clipboard (42 passed). The fallback
itself has no unit test: it needs a live Windows clipboard.
- Share the paste de-dupe between the composer and the provider-picker
modal. The modal read the clipboard directly, so a backend forwarding
both edges of Ctrl+V pasted its text twice.
- Stamp the de-dupe window after the clipboard read, not before it.
ensure_clipboard can block for two seconds, which pushed the second edge
of the same key press outside the window.
- Read clipboard_init_attempted in non-test builds. It was only read under
cfg(test), so -Dwarnings rejected the field as never read.
- Restrict the clipboard-image write fallback: create-new only, refuse a
linked directory, owner-only on Unix. The previous std::fs::write
followed links and dropped the mode the pinned writer guarantees.
- Cap clipboard pixels before the RGBA copy, and bound the preview decode
pool to four threads instead of one thread per attachment.
- Document that every attachment is previewed, not just the first.
Verified with: cargo fmt --all -- --check. Compilation and tests were not
re-run for this commit at the reporter's request; CI is the first build.
Five defects found end to end against a real QQ screenshot:
- Windows Terminal consumes the key-down of a chord it handles itself and
forwards only the key-up, so Ctrl+V arrived as KeyEventKind::Release and
the Press-only router gate dropped it. Paste chords now accept the
release, and paste_from_clipboard de-duplicates the terminals that send
both edges.
- A paste landing while the launch card was up never dissolved it, so an
empty composer kept the card and hid the attachment row behind it. An
attachment now counts as user input and dissolves the card.
- The clipboard open latched its first timeout into "never try again" for
the rest of the session. It retries now, with a 2 second budget.
- clipboard-images was never created and the confined writer cannot create
it mid-walk, so the first paste always failed silently. The directory is
created and a direct write backs up the confined writer for a relocated
state root.
- Only the first attachment got a preview. Thumbnails now share the row.
Verified with: cargo fmt --all -- --check; cargo check -p codewhale-tui
--lib --tests; cargo test -p codewhale-tui --lib attachment (15 passed),
clipboard (42), bracketed_paste (13), image_preview (10), ui::frame (45).
The end-to-end path was confirmed against a live Windows Terminal 1.24:
an empty composer pastes a QQ screenshot, the launch card dissolves, the
attachment row appears, and the PNG lands in clipboard-images.
Windows Terminal's paste handler sends an empty bracketed paste even when
the clipboard held nothing it could read:
// LOAD BEARING: Send an empty bracketed paste even if the clipboard
// was empty.
if (!bracketedPaste && text.empty()) { co_return; }
That empty event is the whole mechanism. /attach with no path was a
workaround for a problem that does not exist, and typing a command after
copying a picture is worse than the path it replaced.
- Remove CommandMediaContext::attach_clipboard_image, the MediaAdapter
implementation, App::attach_clipboard_image, ClipboardHandler::read_image,
and the /attach no-path branch with its tests.
- Keep the empty-paste branch in handle_bracketed_paste and pin it with a
test that injects a clipboard image and asserts the attachment lands.
- Rewrite the docs section: Ctrl+V works, and the empty paste is why.
Verified with: cargo fmt --all -- --check; cargo check -p codewhale-tui --lib
--tests; cargo test -p codewhale-tui --lib
empty_bracketed_paste_attaches_the_clipboard_image (1 passed), attachment
(13), clipboard (42), bracketed_paste (13), image_preview (10).
Follow-up to the second review round:
- Pet mode skipped the preview's rows but still started a decode. Gate the
cache tracking on pet_main, as the previous commit message claimed.
- An empty attachment list skipped tracking entirely, so a removed image
stayed cached and a later re-attach of the same path could be answered
from a stale stamp. Track the empty list; it is one vector comparison.
- The image-only failure used a hardcoded English sentence while every
neighbouring clipboard message goes through MessageId. Reuse
ClipboardNothingToPaste.
- Correct the decode-failure note, which promised a retry that track's early
return never performs.
- Use saturating multiplication for both caption dimensions.
- Cover the SSH branch of the image-only attach path.
Verified with: cargo fmt --all -- --check; cargo check -p codewhale-tui --lib
--tests; cargo test -p codewhale-tui --lib attach_clipboard_image (3 passed),
image_preview (10), attachment (15), clipboard (47), bracketed_paste (12),
ui::frame (45).
Review of the first commit found four real problems:
- /attach with no path called the text-first clipboard read, so a text
clipboard was pasted into the draft and reported as success. The media
facet gets an image-only read instead, and text on the clipboard is an
error that leaves the draft untouched.
- A finished decode never requested a repaint, so the thumbnail appeared on
the next unrelated frame. Poll now runs in the event loop, before the draw
decision, and reports a newly published preview.
- std::fs::metadata ran from the paint path and tripped
scripts/check-blocking-calls-budget.py. The stat moved inside the decode
thread, where the gate exempts it.
- The thumbnail could take 9 rows of a 24-row body. It is now capped at a
quarter of the body and yields to the queued-input preview.
Also drops the unused last_attachment_preview_area, decimates the sampled
grid on a narrow dock instead of hiding the preview, skips decoding in
ASCII-safe and pet modes, boxes the cache so App does not grow on the stack,
and fixes mojibake in the new module docs.
Verified with: cargo fmt --all -- --check; cargo check -p codewhale-tui --lib
--tests; cargo test -p codewhale-tui --lib clipboard (46 passed), attachment
(15), image_preview (10), bracketed_paste (12), ui::frame (45).
The existing ui::tests suite aborts with a stack overflow in
adopting_a_local_model_closes_the_connect_picker_and_names_it on this
Windows host; the same test overflows on the pre-change baseline worktree,
so it is not introduced here.
A clipboard image could already be attached when Ctrl+V reached the TUI,
but nothing showed the picture before send, and most terminals consume
that chord themselves: Windows Terminal sends no event at all for an
image-only clipboard, so the feature was unreachable there.
- Draw the first attached image above the composer as upper-half blocks
with truecolor foreground and background. That is ordinary cell output,
so it renders on Windows Terminal, iTerm2, Terminal.app, GNOME Terminal,
Konsole and WezTerm, unlike the kitty-graphics path the pet animation
uses. Decoding runs off-thread and is cached by (mtime, len), so the
paint path does no filesystem work.
- /attach with no path reads the clipboard, giving every terminal an
entry that does not depend on which process owns Ctrl+V.
- An empty bracketed paste now asks the clipboard for an image instead of
inserting nothing.
- Document the Windows Terminal sendInput \u0016 binding, which the
existing paste predicate already accepts.
Verified with: cargo fmt --all -- --check; cargo check -p codewhale-tui
--lib --tests; cargo test -p codewhale-tui --lib image_preview (9 passed);
--lib attachment (15 passed); --lib ui::frame (45 passed).
Note: cargo test -p codewhale-tui --test feature_registry fails on five
pre-existing over-long summaries (undo-range, receipts,
compatibility-tool-free-turns, chatgpt-plan-sign-in, cache-call-receipts)
that this change does not touch.
ChildAuthority::usage_origin() is the single place a child's accounting origin
is read (context origin, with the test-only session-origin fallback). Dispatch,
child compaction and the review path now use it; before, only dispatch had the
fallback, so child compaction failed with "child compaction has no admitted
accounting origin". Production behavior is unchanged: production children always
capture an origin. Stale sub-agent fixtures are repaired: claims and agents set
up inside a store transaction (release/reload read coordination from the store),
the first manager is dropped before a reload (its writer lease made the reloaded
agents look foreign), the worker_registry fixture recaptures the origin after
replacing the context, and error text follows store admission ("without its
writer lock"). Replay checks follow the lawful settled schema version 6.
Verified (local, existing checks, source only): engine tools::subagent,
approval, runtime_threads, compaction and child checks: 1511 run, 1488 passed,
23 failed (47 before this series). Weak spots, stated plainly: (1) the measured
tool-surface ceiling in parent_agent_surface_stays_within_measured_ceiling was
raised from 91812 to 92100 bytes (measured 92078) following the test's own
re-measure rule; the growth is unexplained and partly host-tool dependent;
(2) a parked child's expected worker status changed from WaitingForUser to
Interrupted to match the current projection (mod.rs ~18552), not independently
re-derived from the design; (3) child_step_timeout_interrupts_open_and_body is
flaky (passed 1 of 3 on the same binary). Still failing, unresolved: the two
budget_handback cancellation checks (Running vs Cancelled), launch_gate_queues,
the remaining auto_review_* and several subagent checks, rlm::bridge dispatch
pin checks, enforce_lru_capacity, and app-server command_catalog (pre-existing).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Sub-agent fixtures that replace the stub runtime's context lost the test-only
default accounting origin, so child usage settlement failed ("Child usage
settlement failed") and turned budget stops into interruptions. A shared
test-support fallback now supplies the origin only when none is set; production
children always capture one. Three runtime_threads replay checks compared
schema_version with the pre-run value; since ccc087723c a settled turn that
records source usage is lawfully version 6, so the expectations follow it.
Verified (local, source only): engine tools::subagent and runtime_threads checks
in the 1732-check run: failures dropped from 47 to 33 (the rest are tracked for
a follow-up; some assert behavior such as Running vs Cancelled and
enforce_lru_capacity). Not verified: the root cause text of the original
settlement warning was not captured, the diagnosis is from code reading plus the
passing checks.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Seven runtime API routes (git status, git changes, git diff, workspace diff,
git graph, workspace file list and file read) accept an optional thread_id.
The thread's recorded workspace comes from the existing thread record (no second
store) and is opened with the same no-follow directory primitive scope admission
uses; an unknown thread is 404, an unusable or non-absolute workspace is 409
thread_workspace_unavailable, and an empty id is 400. Routes without thread_id
behave exactly as before, and the per-file confinement still runs on the resolved
root. Git mutations, guarded/patch writes, the workspace files PUT, status, trust
and file search stay on the server workspace (follow-up). Worktrees stay opt-in;
this only lets a thread name the workspace it works in.
Verified (local, source only): the app-server workspace/git/diff/files/runtime_api
checks in a 1732-check run passed for this slice; the 35 failures in that run are
engine subagent/runtime_threads checks and two app-server checks that are
pre-existing or flaky (command_catalog get_v1_commands fails identically on older
binaries). Not verified: a native observer of thread-scoped reads, a path-escape
attempt against the new resolver, regenerated web/data/agent-docs.generated.json.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
rio-vt 0.5.28's Kitty graphics protocol handler had a path traversal allowing
an out-of-bounds file delete or read (advisory dated 2026-11-01, medium 4.7).
Locks rio-vt, rio-unicode and teletypewriter at 0.5.29 (lock-only; the
manifests already allow 0.5.x).
Verified (local, source only): cargo audit reports no vulnerabilities (the
RUSTSEC-2026-0430 error is gone), cargo deny advisories ok, and 657 terminal/
pty/vt/kitty/graphics checks across codewhale-engine and codewhale-tui passed,
0 failed. Not verified: a real Kitty-graphics hostile-path observation.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
deliver_bound_approval verifies an answer against the held request's identity
(thread, turn, call) through an ExternalApprovalBinding and refuses a mismatch
with a typed ApprovalBindingMismatch, which the runtime API maps to 403.
Callers that pass no binding (the existing HTTP route) behave as before. The
app-server owner port gains the delegating user-input and dynamic-tool answer
variants. The full-tool turn/start gate stays closed.
Verified (local, existing checks, source only): the app-server stdio, interrupt,
approval, user_input, dynamic_tool and owner checks and daemon_socket passed in
the combined run. Not verified: a native two-connection observer; a binding on
the stdio-originated approval path beyond what these checks cover.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Seven runtime API git handlers (git_diff, workspace_diff, git_graph,
guarded_write, patch_write, git_push, git_branch) now resolve the workspace and
repository check through an async helper instead of a synchronous
`git rev-parse` on the Tokio thread, with identical error mapping. The TUI
workspace context reads the memory file size inside its existing
spawn_blocking snapshot (memory_size_hint) instead of on the UI path.
Verified (local, source only): app-server git/workspace and tui workspace_context
checks passed in the combined run. Not verified: idle/approval-wait CPU (needs
the FreeBSD host), a native observer of the handlers.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds codewhale_util::git_metadata::read_head (discovery + HEAD identity) on
gix =0.90.0 with default features off and only sha1/sha256: no network,
credentials, filters, status or fsmonitor code is built. The open is isolated
with strict config and bail_if_untrusted, core.useReplaceRefs and
core.fsmonitor overridden off, after a bounded pre-open size check of the
.git pointer, HEAD and config; cancellation is polled before open, after
discovery and after the read. Typed errors fail loud; not-a-repository and bare
repositories map to NotRepository. The app-server workspace status/metadata
readers and the sub-agent git branch reader use it, dropping their
rev-parse subprocesses; the short id is a fixed 7 characters and a repository
owned by another user or with malformed config is now reported as not a
repository with a warn log. Cargo.lock gains 63 packages. The blocking-calls
budget gains git_metadata.rs std_fs 2: read_head is a synchronous bounded read
(its callers previously spawned processes at the same sites).
Verified (local, source only): the util, engine subagent/approval/runtime_threads
/git, app-server workspace/git/approval/stdio/interrupt/owner, tui
workspace_context/git and daemon_socket checks ran; every check touching this
slice passed. 47 engine subagent/runtime_threads checks fail, and they fail
identically on engine test binaries built 2026-11-03 03:17, before this work
(pre-existing, tracked separately). cargo deny: bans, licenses, sources ok;
advisories failed on RUSTSEC-2026-0430 in rio-vt 0.5.28, unrelated to gix.
Not verified: cargo tree feature closure by name, MSRV 1.89 on the lock, OHOS
dependency check, hostile-repository and cancellation observers, any native
build. Slice 2 consumers (tasks, review, delivery, diagnostics, TUI, memory
crates) still spawn git.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds a process-unique ConnectionId minted per stdio connection and records it on
InFlightTurn. interrupt_stdio_turn now refuses a different connection with a
typed not_turn_owner error (-32012); interrupts with no connection (HTTP, tests,
legacy) behave as before. A second registration for a live thread key is
refused instead of overwriting the first turn's entry, and the refused claim
cannot retire the other turn's registration. The full-tool turn/start gate stays
closed.
Verified (local, existing checks, source only): the stdio, interrupt,
prompt_request, socket_transport, full_control and goal checks and the
daemon_socket integration tests are in the 578-check run (577 passed; the one
failure was an unrelated docs row-length check, since fixed). Not verified: a
real two-connection native observer; a daemon guest interrupting an owner turn
now gets not_turn_owner, and no existing test relied on that.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds [goal] complete_judge and blocked_judge (both default false; default
behavior and the default tool catalog text are unchanged). When enabled,
update_goal consults a GoalGate that reuses the verify critic (toolless,
auxiliary spend, fails closed on unparseable output). A complete claim that is
upheld seals the goal as today; refuted or inconclusive goes through
record_not_achieved so the existing 3-pass stall pause bounds spend. A blocked
claim that is upheld blocks; refuted continues; inconclusive yields to the
person. The judge await drops the lock and rechecks goal id and objective, so a
/goal pause, clear or replace during the wait wins. Flags flow config ->
EngineConfig -> agent tool surface in the TUI, exec, runtime-thread and workflow
builders. docs/features.toml: the goals row mentions the keys; five other rows
(undo-range, receipts, compatibility-tool-free-turns, chatgpt-plan-sign-in,
cache-call-receipts) are shortened to the 120-character limit the existing
feature_registry check enforces; it was failing on them.
Verified (local, existing checks, source only): 578 selected existing checks
across engine goal/verify, runtime goal/config, app-server stdio/interrupt/goal,
daemon_socket and feature_registry; 577 passed, 1 failed on the registry row
lengths, then feature_registry rerun 2 passed, 0 failed. Not verified: no
native observer of a real judge call; on TUI/exec paths without an accounting
origin an enabled judge fails closed (as VerifyTool does), so confirm before any
default-on.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The text after ** was accepted if it matched any path component, so
src/**/*.cpp matched src/vendor.cpp/main.py. That was already true for one **,
and the new handling of nested ** reached the same branch. A suffix with a
wildcard is a pattern for the file itself: match it against the last component.
A literal one, like **/deep, still covers what's under that directory.
Each brace group multiplies the patterns tried for every file, so 20 {a,b}
groups cost a million tries per file with nothing checking for cancellation.
Past 64 combinations the pattern stays literal, as it did before.
Signed-off-by: Lstarsky0 <[email protected]>
thread/start now parses the pinned camelCase ThreadStartParams (deny unknown
fields) and returns the pinned seven-field ThreadStartResponse with the shared
wire Thread projection. model, modelProvider, cwd, a false/absent ephemeral and
operationKey are honored; personality and threadSource are accepted and ignored
(deprecated / analytics-only in the pinned schema). Fields the Engine cannot
honor (ephemeral true, approvalPolicy, approvalsReviewer, sandbox, config,
baseInstructions, developerInstructions, serviceName, serviceTier,
sessionStartSource) are refused together with a typed -32602
unsupported_thread_start_parameter error instead of being ignored. Pinned
clients cannot send an operationKey, so one is minted: retry idempotency is
weaker for them. approvalPolicy/approvalsReviewer/sandbox report never/user/
readOnly because native turns run tool-free while the full-tool gate is closed.
The existing goal-methods test now sends operationKey and reads thread.id.
Verified (local, existing checks, source only): 151 passed, 0 failed across
thread, stdio, rpc, goal, dispatch and the protocol schema+wire tests. Not
verified: the pinned ThreadStartResponse validator on a native build; whether
real Codex clients send serviceName (it may need to become advisory).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
ccc087723c removed the per-source_id replay guard; envelopes with record None
(still produced by MailboxMessage::token_usage and deserialized from older
persisted envelopes) took the additive legacy path, so replaying one source id
double counted the fleet row tokens (22.0k for 11.0k). An empty source_id keeps
the additive behavior; otherwise the usage is added once per source through the
existing usage_source_fingerprint map, and a later settled record for the same
source still replaces it. Costs were not affected.
Verified (local, existing checks, source only): 185 passed, 0 failed across the
tui work_surface, subagent_routing and token_usage checks, including
fleet_row_repaints_resolved_model_and_each_distinct_usage_total (previously
failing on every binary built since 2026-11-02 16:25).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
T05-11: validate_task_spec_document and prepare_managed_fleet_run refuse more
than 64 tasks (the existing placement-preview cap) before any work, and
/v1/fleet/runs gets a 256 KiB body limit like the placement route. U07-02:
paste bursts stop buffering at 64 KiB (the composer already refuses more than
16,000 characters). U07-10: the model picker compares opaque model IDs exactly
in the custom-query duplicate checks and the default-section lookup.
Verified (local, existing checks, source only): part of the 823-check wave run
(822 passed; the single failure is an unrelated pre-existing tui work_surface
test). Not verified: six other model-ID comparisons in model_picker.rs were not
changed or read; no test fixture over 64 tasks exists in the selection.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
thread/read now takes the pinned camelCase params (threadId, includeTurns) and
returns {thread} with the same wire-only Thread projection as thread/list;
with includeTurns it also returns Turn[] built from the canonical record
(user/agent messages, reasoning, context compaction). Queued maps to
inProgress and canceled to interrupted because the pinned enum has four
values. The legacy snake_case thread_id is refused as an unknown field. Owner
checks and alias resolution are untouched.
Verified (local, existing checks, source only): the wave run of 823 selected
existing checks across app-server, protocol schema+wire, engine, tui-kit and
tui passed 822; the one failure, tui work_surface
fleet_row_repaints_resolved_model_and_each_distinct_usage_total, also fails on
every codewhale-tui test binary built since 2026-11-02 16:25 and is unrelated.
Not verified: the pinned-schema validator on a native build. Command
execution, file change and MCP tool-call items cannot be supplied by the
canonical record and are omitted (turns then report itemsView "summary").
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Hosted Landing preflight failed on a86ef86df4. Every flagged site is sync-only
or already on a blocking thread: protocol bin/export.rs is the synchronous
schema generator (no tokio); cli/metrics/stats.rs is the sync `codewhale
metrics` path (run -> run_metrics_command -> metrics::run -> stats::collect),
split out of metrics.rs (6 -> 4); engine/whale_soul.rs load() runs only under
runtime_control::owner_work (spawn_blocking), which the script cannot see;
tui/lib.rs 34 -> 35 corrects a stale baseline (35 std_fs at every revision
since 0c2ab7d577). exec_agent.rs, plugins/render.rs and fleet_setup.rs are
clean and tightened. Produced by scripts/check-blocking-calls-budget.py --update.
Verified: the script reproduces the CI failure before and the diff is exactly
these eight entries. Not verified: hosted preflight on this commit.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
a31cf5916e removed the only non-test writer of AppState.runtime_thread_map;
the mapping lives in the durable canonical link store. The reload test keeps
its captured-owner assertion and the final thread/message dispatch, which
proves the mapping survives reload. The dead field itself is a separate slice.
Verified: tests::config_update_keeps_the_stdio_thread_mapping passes (part of
65 passed, 0 failed).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
thread/list returned the internal ThreadResponse, so the pinned `data` array
was the unused default `{}` and the threads sat under a non-schema key. Adds
a wire-only projection (ThreadListResponse/Item/Status/Source) with the pinned
camelCase fields; sessionId is the public thread id, projectId null, turns [],
cursors null, no invented metadata. The internal Thread type and
thread/request kind=list are unchanged; strict unknown-parameter refusal stays.
Verified (local, existing checks, source only): 65 passed, 0 failed across
thread_list, thread_control, stdio, rpc, config_update, thread_message and the
protocol schema+wire tests. Not verified: the pinned-schema validator harness
against a native build; thread/start (13 issues) and thread/read (9) remain.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds GET /v1/fleet/profiles/{id}/document. Personal/workspace profiles and
selected named Fleet files return their exact bounded pinned text; built-in,
config, plugin and Claude members return an explicitly read-only effective
projection with a null path. The roster now comes from the captured workspace
identity and plugin discovery, and the final workspace-identity check always
runs, even when the read fails. Revision uses sha256_hex over
(id, kind, origin, path, text). Generated schema, TypeScript, SDK types and
EN/ZH docs are updated.
Verified (local, existing checks, source only): 41 passed, 0 failed
(fleet runtime API test, engine fleet::profile cases, protocol schema+wire).
Not verified: no matched native HTTP read/refusal observer, no mounted App
viewer, no full sealed gate; PUT remains unimplemented and writes stay off.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The WeCom bridge now steers the still-active turn with follow-up text,
which adds a runtime POST during the queued-turn phase of this test.
The assertions track approval ownership, so filter to approval routes
instead of counting every post.
The bridge answered "text only" for image, video and file messages, so
the most natural phone interaction — sending the material you want
worked on — was impossible. Download video/file/image frames through
the SDK's expiring links into a configurable media directory and reply
with the saved path, and treat transcribed voice content as prompt
text. Media frames bypass the group-prefix filter, which rejects empty
text and would drop them silently.
While a turn was running, every incoming WeCom message only got the
busy notice and its content was lost; users repeating an instruction
mid-turn had no way to reach the agent. Forward the text through the
Runtime's documented steer endpoint so the running turn can consume it,
and fall back to the busy notice if steering is rejected.
WeCom caps one message at 4096 UTF-8 bytes, but splitMessage only
counted characters. A 3500-character Chinese reply is ~10.5 KB and was
delivered as a single silently truncated message, so long reports
arrived cut off with no error anywhere.
splitMessage gains an opt-in maxBytes cap that re-splits oversized
chunks on code-point boundaries; the WeCom bridge passes 3800 bytes
(configurable via WECOM_MAX_REPLY_BYTES). Other bridges keep the
character-only behavior until they need a byte cap.
matches_glob had no brace support and only matched the text after ** against
one path component, so include patterns like *.{ts,tsx}, **/tests/*.rs and
src/**/tests/** returned no matches with no error, and the same forms in
exclude excluded nothing. **/deep/** also missed a top-level deep/.
Expand {a,b} before matching, and let **/ cover zero or more directories when
what follows it names a path. Other patterns take the same route as before.
Signed-off-by: Lstarsky0 <[email protected]>
With a path argument, file_search returned each match relative to that path
while read and every other tool resolve from the workspace. Searching
packages/app for package.json came back as package.json, which then reads the
one at the repository root without any error. A path naming a single file came
back as an empty string.
Build the reported path the way grep_files does: strip the workspace, or its
canonical form, and leave anything outside it absolute. Ranking and exclusion
still use the path relative to the search root.
Signed-off-by: Lstarsky0 <[email protected]>
Carries the state-path decision to the call sites the first three commits
missed, and closes the two gaps a review of them found.
- fleet store: a workspace Fleet is now checked through the same
`workspace_state_path`, and `ensure_fleets_dir` hands its resolved root to
the writer. Checking one path and writing another left workspace-scope save
and select failing at a home workspace with "Fleet artifact path must stay
within the workspace", after `create_dir_all` had already run through the
junction.
- hooks: `/hooks edit` takes the same decision. The editor is given the
literal `~/.codewhale/hooks.toml` rather than a resolved root, because
`canonicalize` returns a verbatim `\\?\` path on Windows and several
terminal editors will not open one.
- mcp: `reject_linked_workspace_state_path` asked `home == root` literally
while every other site canonicalizes. It now uses `workspace_is_user_home`,
so one rule decides what "the user's own directory" means.
- doctor: the session-recovery scan rejected any state root whose
`file_type().is_symlink()` was true. A junction reports as a symlink in this
Rust version, so `doctor` could not run the diagnostic on the layout it
reports about. It now resolves the root and inspects the target, while still
ignoring symlink entries during the scan. Two unix tests move from "rejects
a linked root" to "follows a linked root".
- `workspace_state_path` asserts its `relative` argument in release builds,
not only in debug ones: it is the one place a security boundary is decided.
Verified on Windows with junctions on both sides (`reject_linked_path` still
refuses `<repo>/.codewhale`, passes `<home>/.codewhale`), `cargo check -p
codewhale-engine -p codewhale-tui` passing, `cargo fmt --all -- --check`
passing, and `cargo test -p codewhale-engine --lib -- fleet::store` reporting
16 passed. The base commit already fails Lint, both Test jobs and the Safety
gate for reasons unrelated to these files; that is tracked upstream in #7204.
Both legacy TUI profile writers now use the same bounded, confined writer.
A directory-wide cross-process lock covers exact preimage comparison, profile
validation, duplicate identity checking and pinned atomic replacement. Keep
create-only semantics and classify uncertain replacement outcomes separately.
Route-assignment reads reuse the same bounded confined reader; model drafts
preview the actual sanitized filename rather than the original role filename.
A refused save keeps the original editor, exact draft and reviewed preimage.
Reuse the view's UUID to reject events after cancellation or reopening, reset
the replacement confirmation and show the failure inline. Close only after a
confirmed save; retain provider readiness, project scope and roster controls.
Verified: 54 existing Fleet setup cases passed on the final frozen batch;
35 existing Engine profile cases reused from the exact same test-binary hash
(89 total, zero failures). Native33 ordinary macOS arm64 CLI built with all
4,249 inputs unchanged, default Engine and no test-support. Two actual TUI
processes proved stale-save refusal, model-draft retention, explicit rereview
then save, project/personal creation and symlink substitution refusal. One
actual profile-draft call used a loopback provider; no remote provider call.
Muse Wonder Full supplied a read-only writer audit, reviewed against source.
This is the legacy profile foundation. Native App document routes, named
Fleet writer migration, exact live roster adoption and workflow/spawn
qualification remain open. No package, Windows, hosted CI or release claim.
Refs CW-6319, CW-7713.
Stashing the draft goes through clear_input, which releases the command
claim, and the insert assigned the text directly without taking it back. So
a typed /draft replaced by /rename from the palette lost the hold in
submit_input: past MAX_SUBMITTED_INPUT_CHARS the line becomes a paste
attachment and went to the model as prose.
Claim the line whenever the inserted text starts with /. An empty composer
never had the claim, so that path had the same hole on main and now holds
too. Palette entries that insert a bare name, such as an MCP tool, stay
unclaimed.
Signed-off-by: Lstarsky0 <[email protected]>
Second review pass on the home-workspace exemption. Three small corrections,
all in the boundary the first two commits established.
`workspace_state_path` shared the resolution rule with `workspace_state_dir`
by copying it; both now come from one `resolved_home_state`, so the two cannot
drift into disagreeing about where state lives. The function also asserts its
`relative` argument is a literal relative path. Every caller passes components
it wrote itself, and a `relative` carrying `..` would step outside the state
directory the root exists to protect.
The skills exemption matched only the resolved target of the state directory,
so a user who put `~/.codewhale/skills` itself on another volume lost the
exemption and silently lost those skills. It now matches the literal spelling
under the state directory as well: that directory is the user's own
application state, not repository content, so a link inside it stays exempt
whichever end is linked.
The journal's `root` field no longer claims to be the state directory in every
case; for a non-home workspace it is the workspace.
Verified on Windows with junctions on both sides: a non-home workspace still
refuses `<repo>/.codewhale`, a home workspace still passes
`<home>/.codewhale` and walks the resolved target.
- cargo check -p codewhale-util -p codewhale-skills: passed
- cargo check -p codewhale-engine: passed
- cargo test -p codewhale-util: 41 passed; 0 failed
- cargo test -p codewhale-skills: 174 passed; 0 failed
- cargo test -p codewhale-engine --lib -- tools::workflow::journal
fleet::profile: 41 passed; 0 failed
- cargo test -p codewhale-engine --lib -- subagent::tests::: 590 passed;
6 failed, the same six that fail on the parent commit with these changes
stashed (real-home skill leakage into a tempdir workspace, plus one test
that overflows the default Windows test stack).
- cargo fmt --all -- --check: passed
Review follow-up on the previous commit. The exemption was right; the way it
was applied was not. `reject_linked_path` never examines the root itself, so
lowering the root from the workspace to `<workspace>/.codewhale` made a
repository-supplied `.codewhale` link transparent: fleet profiles were read
with their trust and permission fields, the workflow journal wrote, and the
subagent lock created directories through the link, all outside the
repository. Every one of those three call sites now keeps the workspace as
the root with `.codewhale` still inside the walked path.
workspace_state_path decides that root once: a home workspace resolves its
own `~/.codewhale` junction and uses the target, every other workspace keeps
itself and inspects `.codewhale` exactly as before. A state directory that
exists but cannot be resolved falls back to the strict root, so an
unreachable junction fails closed instead of starting a second state tree.
The skills exemption is now per directory rather than per workspace: only the
home state directory skips confinement, so links under any other home
directory are still followed the old way and still warn. Also reuses
`codewhale_paths::CODEWHALE_APP_DIR` instead of a third literal.
Verified on Windows with junctions on both sides:
`reject_linked_path` still refuses `<repo>/.codewhale` for a non-home
workspace and passes `<home>/.codewhale` for a home workspace, where the
resolved root is the real target.
- cargo check -p codewhale-util -p codewhale-skills: passed
- cargo check -p codewhale-engine: passed
- cargo test -p codewhale-util: 41 passed; 0 failed
- cargo test -p codewhale-skills: 174 passed; 0 failed
- cargo test -p codewhale-engine --lib -- fleet::profile: 33 passed; 0 failed
- cargo test -p codewhale-engine --lib -- tools::workflow::journal: 8 passed;
0 failed (includes journal_never_writes_through_a_linked_codewhale_dir)
- cargo test -p codewhale-engine --lib -- subagent::tests::: 590 passed;
6 failed; the same six fail on the parent commit with this change stashed.
Four are real-home skill leakage into a tempdir workspace, and
agent_claim_expands_the_callers_write_scope overflows the default Windows
test stack before RUST_MIN_STACK is raised.
- cargo fmt --all -- --check: passed
A session started in the user's home directory is not a repository: the
directories below it are the user's own global content, which the user is
free to relocate behind a link. Four call sites still treated it as
repository content and refused that relocated state, so a junction at
~/.codewhale silently disabled the feature behind it.
- skills: merge_watched_directories confined every directory under the
workspace, so a junction at ~/.codewhale refused the whole catalogue and
only ~/.agents skills loaded (30 of 90 in this checkout's home).
- fleet roster: workspace agent profiles confined to the workspace, so
~/.codewhale/agents was refused and every profile was skipped.
- subagent coordination: the process lock walked ~/.codewhale as workspace
content and refused it, so the shared ledger never appended.
- workflow journal: the ledger root was the workspace, so the journal
could be neither read nor appended through the same junction.
workspace_is_user_home and workspace_state_dir put that decision in one
place: a home workspace is exempt from confinement, and its ~/.codewhale
is resolved once so later link walks start at the real directory. Links
below the state directory are still refused, and every non-home workspace
keeps the previous behaviour exactly.
Verified: cargo check -p codewhale-util -p codewhale-skills (passed);
cargo check -p codewhale-engine (passed); cargo test -p codewhale-util
(41 passed; 0 failed); cargo test -p codewhale-skills (174 passed;
0 failed); cargo fmt --all -- --check (passed). A release build of the
skills discovery probe reports 90 skills with C:\Users\asus as the
workspace, where the same probe reported 30 and logged "Refusing
symlinked path C:\Users\asus\.codewhale" before the change.
Legacy list/read DTOs silently discarded workspace, cursor, archive and
includeTurns fields from callers expecting newer contracts. Deny unknown
fields before owner I/O in direct and tagged compatibility requests; retain
supported legacy list/read and protected full-history behavior. Document
these limits in English and Chinese without claiming Codex wire parity.
Verified five existing focused list/read/archive/identity/shared-owner cases;
the shared-owner case now checks six parser refusals before any owner call.
Actual ordinary native28 baseline accepted all six unknown fields, including
an ineffective workspace filter. Native29 rejects all six with -32602, keeps
normal list/read and closed execution gates, and makes zero model POSTs.
Both builds' 4,249 inputs stayed unchanged. The first library build failed
two test-only DispatchError field accesses; corrected v2 passed. Native29
production postimages match; only those cfg(test) assertions changed later.
Muse Wonder Full assisted the read-only contract audit; root verified source
and native behavior. Receipt: CW/artifacts/takeover-20261103/
RPC-PARAMETERS-QUALIFICATION.json. No new test functions/code comments,
remote-provider, package, platform, CI or release qualification. Refs CW-7704.
Use the generated DynamicToolCallParams and DynamicToolCallResult contracts
for exact thread/turn/call delivery through the existing authenticated API.
Resolve only after its empty HTTP 202 acknowledgement; preserve actual error
statuses and uncertainty without automatic mutation retries. Reject path
segments that URL normalization would erase, and refuse mutation redirects.
Verified: 19 existing SDK checks, JS syntax, both strict TypeScript modes.
Ordinary native28 HTTP observer: three wrong-target 404s leave the waiter,
one accepted result completes the turn, repeated delivery returns 404, and
restart retains the outcome with exactly two loopback model POSTs. Actual
HTTP transport controls cover encoded identifiers, 24 invalid IDs, errors,
pre-aborted cancellation and auth/no-auth redirect refusal with zero escapes.
Receipt: CW/artifacts/takeover-20261103/SDK-DYNAMIC-QUALIFICATION.json.
No new test functions or code comments. No full RPC, remote-provider, device,
package, CI, release or deployment qualification. Refs CW-7704.
Refs CW-7704. Compatibility goal execution stays closed, so do not advertise thread/goal/set as available in either global or thread discovery. Keep the typed refusal for older clients calling the known method and keep goal read/clear discovery. Update both language docs and existing capability expectations. Correct the existing goal-read fixture to seed its fake owner through the captured Runtime API: its former direct compatibility setter is deliberately refused after the native owner migration. Production still uses no self-HTTP.
Verified 4 focused existing checks, 0 failed, after the fixture repair. The first three passed in v1; the fourth exposed that stale seeding path (runtime_unavailable: goal execution requires the canonical Runtime interaction API). All 4,249 inputs matched the guarded native28 build. Actual native stdio initialize/capability/closed-gate probe passed with 0 model POSTs and clean shutdown; its capability payloads differ from native26 only by removal of goal/set. No new repository tests or code comments.
Separately compared actual native responses with the immutable Codex rust-v0.166.0 exports: initialize and loaded/list match; thread/start/list/read do not yet match. This discovery cleanup is not full App Server parity, full-tool execution, an optimized package, platform qualification, CI or release/deployment.
Refs CW-7670, CW-6966. patch-undo ignored turn_id and undid the latest turn. Reuse the selected-user-turn preparer already used by conversation undo, retain restore admission through file changes and fork publication, and advertise thread_patch_undo_turn_id. GET previews share the physical restore planner; POST can require the exact dropped turn IDs and affected paths so changed selections fail before writes. Existing trust, owned-snapshot attribution, safety backup and later-edit refusal stay authoritative. Default depth and public restore helper behavior remain compatible. Generated protocol/SDK contracts and feature docs are updated.
Verified 22 distinct focused existing checks, 0 failed: 14 AppServer/protocol after the file-plan amendment; 8 Engine fork/restore cases reused against the exact same built binary. All 4,249 inputs matched the guarded native26 build and frozen source. Actual native macOS development CLI: five tool-write turns plus stale/foreign/range/path/later-edit/active-work refusals, named selection overriding depth, exact restoration/deletion, retained source and restart passed. Mounted Electron paired with native26 passed preview/cancel, separately confirmed conversation-only fallback, double-click guard, restore, composer and restart; no restore-triggered model request. Codewhale DeepSeek Flash supplied a read-only design review; root checked its suggestions against source and adopted exact path consent.
Scoped evidence only: no optimized package or Windows acceptance, no release/deployment. Restore and fork publication are not a single crash-atomic cross-store transaction; partial operational failures preserve the safety snapshot/source and are not reported as successful rewinds. This endpoint does not promise operation-key idempotency.
Picking a command that needs an argument from the palette, such as /rename,
replaced the composer with no way back: the draft wasn't stashed, so history
search couldn't find it. The hotbar does the same insert and stashes first;
do that here too.
Signed-off-by: Lstarsky0 <[email protected]>
Use the existing models/tool_projection owner for typed Exact/Relaxed/Omitted per-tool outcomes. Select strict shape and flag together, relax route compatibility with the existing sanitizers, share constraint-note rendering, and refuse forced choice of an omitted tool before transport. Keep logical catalogs and runtime argument validation independent of wire relaxation. Non-beta DeepSeek/local DS4 no longer receive strict-shaped parameters while strict is disabled; Responses keeps strict=false. Normalize cloned MCP API schemas like native registry schemas without changing original server records.
Dispatch, streaming, preview and deferred/search measurements consume the prepared projection. Blocking warnings and preview manifests retain exact omission counts with the existing 8x64 sanitized name sample, without schemas/refs/endpoints. Keep logical prefix fingerprints unchanged and explicitly separate from prepared wire hashes; consolidate the three default tool-choice callers.
Muse Wonder Full supplied the initial worker batch; its quota exhausted mid-edit. Root reviewed and completed the migration, bounded diagnostics, strict-route guards and existing consumer/assertion updates. No new repository tests or code comments. One unrelated pre-existing approval fixture formatting defect found by cargo fmt was corrected; review and comparison allowing whitespace/trailing commas confirm the same call and arguments, with no assertion or production change.
Verified: frozen 4,249-input native library build, 125 focused existing Engine cases, 56 schema sanitizer cases, 8 canonicalization cases and 30 logical-prefix cases: 219 passed, 0 failed. Source hashes stayed unchanged through build/checks. The later approval-fixture formatting/trailing-comma delta has its own receipt. cargo fmt --all -- --check and git diff --check pass. Existing checks cover all three dialects, prepared/preview identity, mixed strict catalogs, Kimi omission/forced-choice/warnings/deferred schemas and MCP authorization/catalog isolation.
Source and loopback fixture evidence; ordinary native CLI/product witness follows this commit. No real-provider acceptance/cache-savings, full-tool JSON-RPC admission, package, release or deployment claim.
Refs: #5633, CW-6256
Add format=md to the authenticated thread/turn receipt routes using the
existing shared Engine formatter. The default JSON response and access
boundary remain unchanged; unsupported format values fail with 400.
Desktop can display the same audit facts and coverage notes as the terminal
without deriving a second record from its activity labels.
Verified: existing authenticated receipt route case passed, including JSON,
Markdown content type/body, anonymous refusal, unknown format and foreign
turn refusal. Five existing SIWC cases also passed in the same frozen
batch. Ordinary native24 CLI compiled with all 4,249 inputs unchanged.
Mounted Electron/native24 displayed thread and selected-turn receipts with
exact one-run/one-approved/one-denied totals, and exercised grant revoke
and next-call approval. No final candidate, publication or deployment.
Refs CW-7679. Founder selected Engine audit receipt authority 2026-11-03.
Replace the generic JWT decode rejection with static signature, issuer,
audience, expiry, not-before and required-claim diagnostics. Claim names
are restricted to a fixed list; raw library errors and token values are
never interpolated. Preserve all signature/time/issuer/audience/nonce and
account checks, registration activation and plan-scope admission.
Update the existing signed identity case to pin the diagnostics, including
future not-before rejection. English/Chinese provider guidance separates
device-clock advice from plan permission and asks only for safe error text.
Verified: five existing SIWC cases passed; the same frozen batch's receipt
API case passed (six total, zero failures). Ordinary native24 macOS ARM64
CLI built with all 4,249 inputs unchanged and no test-support feature.
Windows report's exact rejected check remains unknown; no Windows fix,
package/release or live ChatGPT account acceptance is claimed.
Refs #7203, CW-7800.
Preserve Lstarsky0's contributor commits. Local macOS ARM64 review applied the exact three PR postimages on d7ff89d16c: 11 existing test-support cases passed, plus 1 existing audit isolation case passed. No Windows or exhaustive final-candidate validation is claimed. Normal-exit cleanup does not establish crash/SIGKILL cleanup. No dependent open PRs; source branch is preserved.
Canonicalize the selected plugin workspace before pool comparison, storage
and fork construction. Equivalent symlink paths retain the existing caller
and attachment guards instead of rejecting a real session.
Adapt the merged PR #7138 existing shutdown test to the extracted app-server
and runtime crate paths; production shutdown behavior is unchanged.
Verified on native23 ordinary macOS ARM64 debug build with all 4,249 source
inputs unchanged: sealed selected checks 389 passed, 0 failed; merged shutdown
completion cases 11 passed, 0 failed. Native22 real DeepSeek Flash symlink
workspace read completed successfully; production MCP postimage is identical.
Native23 P04 v4 and actual PostgreSQL pg7 joined observers passed. Actual
Stats observer passed on native22. These are local/source/provider receipts,
not Windows/package, release, or deployment qualification.
Refs CW-7764. Evidence: CW/artifacts/takeover-20261103/ and
CW/artifacts/engine-0103-execution-20261102/coherent-native23-existing-v1/.
AGENTS.md puts the reasoning in the commit message: the call is sound because
atexit only records the callback, which takes no arguments and stays valid for
the life of the process.
Signed-off-by: Lstarsky0 <[email protected]>
Updates CHANGELOG.md and crates/tui/CHANGELOG.md identically. Two new bullets carry a doubled blank line that was left as is because source edits were out of scope for this commit run.
Verification: whole-tree evidence only, not per group. A native21 build of the full dirty tree completed (patch sha ea25c782...), and an unsealed nextest rerun of all 389 selected existing checks passed 389/389 on it. The earlier sealed native20 run failed 369/20 before fixes and was not re-sealed. No CI run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Pins the first-party marketplace revision and the Computer Use plugin 0.16.0 archive. Release v0.16.0 of codewhale-hq/codewhale-cu-plugin was confirmed to exist and not be a draft; the sha256 was not independently recomputed.
Verification: whole-tree evidence only, not per group. A native21 build of the full dirty tree completed (patch sha ea25c782...), and an unsealed nextest rerun of all 389 selected existing checks passed 389/389 on it. The earlier sealed native20 run failed 369/20 before fixes and was not re-sealed. No CI run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Updates moved-file paths in the CI path filters, scripts, vitest config and editor/bridge sources; documents the Runtime API stdio/websocket controls and usage statistics (en and zh_hans); adds the usage-statistics and cache-call-receipts features to features.toml with regenerated web data; updates coarse-split design docs and crate maps, split scripts, the blocking-calls budget and release smoke scripts.
Verification: whole-tree evidence only, not per group. A native21 build of the full dirty tree completed (patch sha ea25c782...), and an unsealed nextest rerun of all 389 selected existing checks passed 389/389 on it. The earlier sealed native20 run failed 369/20 before fixes and was not re-sealed. No CI run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Moves the Runtime API, ACP and MCP servers, exec agent, eval, remote control/setup, chat relay and embedded web client from crates/tui/src into crates/app-server/src (git records the 57 moves as renames/deletes plus adds); adds initialized JSONL stdio, websocket, connection, owner_calls, daemon socket/client, turns and thread_control; TUI and CLI call sites switch to codewhale_app_server; bare codewhale app-server becomes stdio; LegacyHttp becomes Http. Also carries the command-contract cache-call facets, /cache calls, and codewhale metrics/stats rewrite. G4 (contract/cache/metrics) was folded into this commit because crates/cli/src/lib.rs, Cargo.toml and Cargo.lock interleave both slices and could not be split cleanly non-interactively. Includes rustfmt-only change in crates/mcp/src/config.rs and all Cargo.lock changes for the batch.
Verification: whole-tree evidence only, not per group. A native21 build of the full dirty tree completed (patch sha ea25c782...), and an unsealed nextest rerun of all 389 selected existing checks passed 389/389 on it. The earlier sealed native20 run failed 369/20 before fixes and was not re-sealed. No CI run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Routes NativeApprovalPublication through approval, handle, events, child_host and subagent paths; keeps accounting alive past the 64th model response and excludes usage-partition turns from receipts; exposes UsageSourceRevision accessors and a fleet ledger diagnostic reader; adds loaded-thread listing and canonical rename in the runtime_threads manager; includes related Engine tests and rustfmt-only changes in tool_preparation.rs.
Verification: whole-tree evidence only, not per group. A native21 build of the full dirty tree completed (patch sha ea25c782...), and an unsealed nextest rerun of all 389 selected existing checks passed 389/389 on it. The earlier sealed native20 run failed 369/20 before fixes and was not re-sealed. No CI run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds the JSON-RPC envelope and initialize lifecycle contracts (rpc.rs, lifecycle.rs) with pinned wire fixtures and tests, regenerates the runtime JSON schema, TypeScript and SDK typings, and ships the upstream license, NOTICE and UPSTREAM.md files alongside the derived code. Cargo.lock changes for this crate land with the host-move commit.
Verification: whole-tree evidence only, not per group. A native21 build of the full dirty tree completed (patch sha ea25c782...), and an unsealed nextest rerun of all 389 selected existing checks passed 389/389 on it. The earlier sealed native20 run failed 369/20 before fixes and was not re-sealed. No CI run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A tombstone retires the origin before physical cleanup. Expect snapshot reads to reject the retired session, keep checking that bytes remain intact until cleanup, and verify a deletion retry removes the snapshot while retaining the shared legacy checkpoint. The existing test previously expected retired data to remain loadable.
Validation: three focused session deletion tests passed (0 failed). Eleven run-completion tests and six other former CI failures passed on Windows MSVC Rust 1.98.0 using the repository CI stack setting. Workspace formatting and patch whitespace checks passed.
Signed-off-by: Codex <[email protected]>
Preserve the observed background run across the engine crate extraction and asynchronous spawn factory. Keep the current MCP auth context, shared exec event consumer, and test-support boundary. Focused validation is in progress before push.
Signed-off-by: Codex <[email protected]>
The escape arm consumed \{, \}, \$, \%, \#, \& and \_ and printed nothing, so
$50\%$ rendered as 50 and $\{1, 2\}$ as 1, 2. It prints the character now.
\left, \right and \big took the next character as the delimiter even when it
was a backslash, so \left\{ printed a bare backslash and \left\langle printed
as written. Leave a backslash for the main loop, which knows those commands.
That includes the spaced form, \left \langle, which lost its bracket when the
offset was fixed.
Signed-off-by: Lstarsky0 <[email protected]>
Integrate original PRs7193,7196,7195 and7200, preserving their original
merges and authors. Each exact head passed the configured required checks
before its normal public-main merge. Review auto-merged command save/load
paths and tab-selection expectations.
Resolve only the generated agent-docs conflict with the actual canonical
Node derive-facts producer, retaining the raw docs from both parents. The
existing check-facts validates facts, features and all derived documentation
against the merged source. Full git diff checks pass. No manual JSON merge
or side's source docs were discarded.
The combined Rust tree will be compiled and checked with the next coherent
accounting batch; this merge is local and held from push until that proof.
No new hosted CI, full release, provider or package qualification is claimed.
Signed-off-by: CodeWhale Bot <[email protected]>
Add authenticated typed watch ingress/recovery on the existing Runtime owner,
revision-guarded private Soul editing and per-turn personality capture, and
per-tool MCP Always/Ask/Off settings with signed retained-browser replacement.
Refresh the existing Core catalog after replacement. Refuse disabled tool
calls before preparation or approval while retaining eligible route precedence
and synthetic OAuth recovery. Keep consent, ACP, captured ceilings and nested
call gates. Update the existing fixtures and generate protocol/website outputs
with their actual producers.
Validation: ordinary native12 CLI built in 442.617s over 3028 unchanged inputs,
normal default Engine features. Actual Runtime same-Core rebind, zero-approval
Off and stale-Always refusal controls pass 3/3 on immutable 4cb7607d; owned
cleanup and all source/binary guards pass. Ten other scoped MCP controls retain
unchanged-function evidence from native11; these are not 13 fresh runs. Native11
watch admission/terminal/refusal/restart passes 4 phases with full-store equality.
Original batch coverage of 46 unique checks uses 38 retained plus 8 fresh passes, not 46 fresh.
Rust schema/SDK write/check and Node facts/docs derive/check pass.
Existing MCP/namespace/OAuth/nested/Computer Use checks: 14 passed, 0 failed,
0 ignored; actual execution 3.109s. Compile/list wall time 877.297s includes
waiting for the ordinary build. All source and executable guards pass.
Limits: local development source, not release qualification. Source65 and valid
pure-tool receipt completion defects are reproduced and remain next-slice work.
Soul HTTP/preview/first-wire controls do not establish the failed full TUI
context observation. No real provider, joined Platform/PostgreSQL, required
native-platform, installed package or full release CI claim. All original
failures and fixture corrections are retained in the external execution ledger.
No installed CLI replacement, signing, deployment or publication.
Resolve explicit save and load paths against the workspace using the existing tilde expansion helper. Absolute paths retain platform Path::join behavior; existing snapshot, publication and overwrite protection remain. Exact-head source review complete; all three required checks, Safety, DCO and Version drift pass. Contributor PTY observations remain separate from Root source review; broader Ubuntu still running, not claimed green. Preserve contributor source branch.
Clarify native and subsystem installation routes while retaining device qualification limits, unsupported-lock behavior and explicit source-stamp requirements in English and Chinese. Maintain the original contributor rewrite and subsequent repair history. Actual incumbent documentation producer regenerated 25 topics from committed public sources and PR docs, including seven inherited stale sources. All three required checks, Safety, DCO and Version drift pass; broader Ubuntu still running. No device run or native support qualification claimed. Preserve contributor source branch.
Keep currency amounts from consuming surrounding text as single-dollar math. Exact updated-head review preserves the separate Unicode crash fix and display-math paths; single-dollar multiline/leading-space cases now remain literal. All three required checks, Safety, DCO and Version drift pass. Broader Ubuntu is still running; no full-suite-green claim. Preserve contributor source branch.
Expand tabs in rendered replies and sent messages using existing grapheme widths. Whole-message copy/export keeps stored source; partial selections copy painted spaces. Exact-head source review complete; all three required checks, Safety, DCO and Version drift pass after normal main update. Broader Ubuntu still running, so no full-suite-green claim. Preserve contributor source branch.
Preserve the contributor rewrite while separating existing device probes from unverified TUI/editor/model workflows. Keep the unsupported-lock exception and describe explicit build stamps correctly in both languages.
Verified: nine sections in each language, source review, and actual incumbent deriveAgentDocs over 25 topics with every public input hash checked. Regeneration also refreshes seven inherited stale public doc inputs. No device run or native qualification claim.
Signed-off-by: CodeWhale Bot <[email protected]>
Size terminal table columns from parsed content and share remaining room among wider columns. Root reviewed allocation, cell/link offsets and border consumers. All three required checks, Safety, and MSRV pass. Broader Ubuntu: 19036 passed, 46 failed, 33 skipped; the 46 failure names exactly match the retained PR7192 baseline. Existing very-narrow-terminal overflow remains a disclosed limitation. Contributor source branch is preserved.
Read BM-prefixed text without misclassifying it as BMP. Root reviewed the image sniffer and changed fallback. All three required checks, Safety, and MSRV pass. Broader Ubuntu: 19037 passed, 45 failed, 33 skipped; all 45 failures also occur in the retained PR7192 baseline, with no whole-suite-green claim. Contributor source branch is preserved.
Fix non-ASCII LaTeX rendering and saved-session reopen crashes. Root reviewed UTF-8 traversal and whitespace offsets. All three required checks, Safety, and MSRV pass. Broader Ubuntu: 19037 passed, 45 failed, 33 skipped; all 45 failures also occur in the retained PR7192 baseline (46 failures), so no whole-suite-green claim. Exact review and failure comparison retained in the Engine 0.10.3 execution receipts. Preserve contributor source branch.
Keep the active transcript, draft and queue owner until the Engine confirms the requested session/control identity. Compare the expected snapshot in the existing mailbox, preserve refused or uncertain transitions, and retain edited input plus scoped Stop controls while pending. Reuse the existing event/persistence owners.
Migrate existing fixtures to real acknowledgment, persisted source capture and fixed-thread test-environment enrollment. The sampled queue-fixture deadlock and its failed run are retained.
Verified: ordinary CLI build; 25 unique existing checks (6 affected rerun, 19 unchanged passes reused); 10 fresh production Core/MCP/session groups; native plugin 46 assertions and 20 renderer comparisons; real TUI delayed acknowledgment 16 assertions and refusal/explicit retry 22 assertions, genuine child ownership and control-socket lifecycle. No external provider calls.
Evidence: CW/artifacts/engine-0103-execution-20261102/session-ack-landing-v1/QUALIFICATION.json. Production binary b57afe0d remains uninstalled while the founder records. Full 0.10.3/native/client/final gates remain open; no release-readiness claim.
The root lives in a static OnceLock, so Drop never runs, and nextest gives
every test its own process. Each run of the suite leaves thousands of
codewhale-tui-test-state-* directories in the system temp dir.
Register an atexit hook when the root is created. libtest reports a failed
test through exit, so the hook runs then too; an abort or a kill still leaves
that one process's directory. Staged plugin trees are non-writable on unix, so
a failed removal makes the directories writable and retries.
Move the unsealed audit log into the root so it goes with it, and check the
files in the engine's probe before it exits, since they no longer outlive it.
Signed-off-by: Lstarsky0 <[email protected]>
Preserve the original contributor history. Required Lint, Windows and npm-wrapper checks plus Safety passed. All23 selected checks passed; ordinary renderer20comparisons and the integrated macOS native plugin journey46checks passed with separately carried reload/stack corrections in release branch97e073ac795. Broader Ubuntu46failures and full0.10.3 native/final release gates remain recorded and open. No source branch deletion.
Keep required credential failures explicit before connection, propagate stored
OAuth initialization failures, and keep transient desktop browser servers
with their admitted workspace. Forward the shared runtime map to the existing
Core and child adapters, schedule late registrations through the existing
supervisor, and include them in the live manager snapshot. Preserve static
precedence, private-network admission, redaction and credential authority.
Avoid an unnecessary coordination transaction when a validated session owns
no agent or worker records. Preserve owned/read-only/failed-admission fences.
Supply the plugin reload workspace translation argument. Heap-pin eight
existing interactive awaits: the real plugin confirmation overflowed the
32 MiB owner stack; measured heavy frames shrink by 7,339,536 bytes without
changing the stack limit, confirmation or execution ownership.
Prepare consistent 0.10.3 development metadata with the incumbent release
preparer: 49 local lock records updated, all 698 external registry records
and the latest-published-release fact unchanged. This does not publish a
release or freeze a final candidate.
Verification: ordinary production CLI build PASS (56.31s, 3025 frozen inputs);
actual --version codewhale 0.10.3 (dev), binary e5fc64ca47e3. Nine scoped
Engine/MCP observations PASS through seven retained and two fresh controls
on the unchanged Engine cohort; all eight ordinary dependency fingerprints
match this TUI-only rebuild. Native plugin journey PASS 46/46 plus 20 public
renderer comparisons: exact two-step trust, literal argv, stale/prefix refusal,
trust remains disabled, no plugin execution, exit 0, actual terminal EOF and
owned cleanup. One owned catalog GET, zero inference POSTs. Earlier failed
runs are preserved. Modified Rust formatting and JSON parsing pass.
Receipts: CW/artifacts/engine-0103-execution-20261102/local-0103-rebuild/
LOCAL-QUALIFICATION-V5.json (4601029555d3) and the retained ordinary Native
receipt (368289757940). Full fresh-session ACK, actual Child/RLM, paired
Desktop, broader Ubuntu and native-platform/final release gates remain open.
/save took its path as written. `/save ~/x.json` created a directory named
`~` under the launch directory and reported success, and `/save backup.json`
with --workspace wrote to the launch directory, where /load, which joins bare
names to the workspace, couldn't find it.
Expand `~` and join to the workspace on both sides, as /config does for its
paths, so they agree and the receipt names the real file.
Signed-off-by: Lstarsky0 <[email protected]>
Retain original contributor history through07967b1f12f809f2356efa63cc29529dac33d1bf.
All10 non-root-changelog postimages match the original head exactly;
CHANGELOG preserves the qualified P04 notes and adds the credited PR note.
The exact parent ordinaryCLI build passed407.81s against2338unchanged
inputs. Public production-renderer baseline is retained separately:
400000periods/400024visible bytes in83307microseconds;12of20candidate
entity assertions fail as expected before this original contribution.
A test-only helper import in the earlier observation harness failedE0432;
v6 uses the exact public production equivalent without changing assertions.
Original head CI has all3required contexts andSafetyPASS. All23direct
selected cases passed inUbuntu, while the broader run has46unique failures.
Actual local candidate build, existing cases, public renderer and native
plugin confirmation qualification remain pending. This local merge does
not merge or close the contributor PR on GitHub and is not a ship claim.
No source deletion, protection bypass or harvested replacement commit.
Bind native usage revisions, frozen route prices and original spend owners
across Engine, child/review/Auto and native client projections. Preserve
unknown usage instead of deriving money from unverified legacy totals.
Keep Source ownership until real publication acknowledgment and retain
failed settlement for later admitted flush/recovery. Copy frozen pricing
audit labels into session snapshots without changing numeric corrections.
Store continuable interrupted workers as terminal Interrupted while keeping
the public waiting-for-follow-up view. This fixes a real workflow-child
HTTP500 completion hang without weakening publication/rollback guards,
replaying a request, discarding its checkpoint or changing live approvals.
Verification: 102 unique existing cases covered by source-scoped reuse:
80 v7, 17 v8, 4 actual v10 Engine passes and 1 actual v11 TUI pass.
V11 TUI-lib compile/list passed in450.30s; exact end-to-end case passed
1/1 against2338unchanged inputs and unchanged executable hash. Existing
Apple __eh_frame compact-unwind linker warning remains. Independent bounded
v9/v11 reviews justify reuse; this is not102fresh executions. Four obsolete
existing tests are renamed/updated; no additional cases or code comments.
Earlier ordinary production-library/CLI/Runtime HTTP observations retain
their exact receipts. V9 ordinaryCLI build passed and actual HTTP500 child
control exited naturally1 with task/run terminal failure,1POST/1fsynced
synthetic effect, retained Missingrev2/checkpoint, real pipe EOF, joined
handlers/closed sockets and no forced cleanup. Known/missingHTTP200 and
13CLI/3RuntimeHTTP controls retain earlier source identities. No external
provider calls or fresh v11 ordinaryCLI claim. All failed generations kept.
Receipt: CW/artifacts/engine-0103-execution-20261102/accounting-native-v11/RECEIPT.json
SHA256:832ecfe1c1548edc9712bc31bb78288b79a375dbc0d2d4ba0cae454e9179a2b9
FullP04 remains open: raw presence, physical-attempt durability, original
workspace, prompt layers, cache economics and finite retirement. Other
release packets, native/installed/final gates and CW7704 remain open.
Exclude the two temporary originalPR7187 web files; preserve their history.
A cell can hold several lines now that <br> breaks the line, and measuring
the whole cell made its column ask for the sum of them.
Signed-off-by: Lstarsky0 <[email protected]>
Keep Prose argv as lossless JSON across Note backslash preprocessing and Markdown. Encode markup and JSON quote/backslash escape tokens without changing the decoded arguments. Plain and InlineCode serialization remain unchanged. Update one existing review case to decode the same expected argument values.
Validation: exact two original-branch preimages; Rustfmt parse/format for both files; temporary-index merge and whitespace check. Existing native checks and ordinary rendering/plugin qualification remain pending; this is a source repair for authentic PR CI, not merge or release qualification.
Refs #7192
Resolve the two changelog insertions with all release notes and original contributor attribution. Preserve main image/badge and line-break handling while decoding the displayed image/link label once. All other paths are the mechanical merge.
Verified exact11-path delta,9artifact Rust format/parse checks and independent source review. Native renderer/plugin observations and actual required PR CI remain pending. No P04 working-tree changes are included.
The Windows all-target PR check reached a dead-code error in the stdio
fixture constructor. All nine test callers are cfg(unix); production uses
spawn_with_auth_context. Match the existing caller platform guard without
warning suppression or changing transport/auth/lifecycle behavior.
Verified all nine caller guards, parser/formatter output and Windows target
cfg. Unix test compilation is identical to the qualified002e053960 source;
its passing all-target Clippy evidence remains applicable. Fresh hosted
Windows validation is required; no Windows pass is claimed locally.
Migrate the remaining protocol parity integration import to its App Server
protocol owner. Box large private publication/Fleet payload variants and
preserve public outcomes. Normalize equivalent bounded clamps, pattern
matches and guarded control flow without suppressing warnings. Remove
orphaned recovery documentation left above the foreign-session importer.
Verified the exact 13-path batch with CI-equivalent workspace Clippy across
all targets/features, 18 existing protocol parity checks and 44 existing
diff-render/Unicode checks. cargo fmt --check, changelog sync check and
git diff --check pass. The compiler input inventory was unchanged during
Clippy and tests. Earlier failed/interrupted diagnostics are preserved.
Hosted Windows/Linux PR checks remain required on each updated original
branch. This is a shared baseline repair, not Engine 0.10.3 readiness.
Required PR CI exposed inherited rustfmt drift in14 Engine/TUI publication and fixture paths. Regenerate with existing cargo fmt; cargo fmt --all -- --check and git diff --check pass. Formatter-only correction; prior behavioral receipts remain scoped to their original source and current hosted CI runs qualify this source. No new tests/comments or native local build.
Preserve original React19.3.1/DOM and types19.3.0 intent, with current Stripe23.1.0 and Node types26.6.6. Structured three-way dependency delta, root/lock consistency, exact dependency pins and diff checks pass; hosted PR CI supplies runtime/build qualification.
Regenerate the existing TUI changelog slice from its root owner. Genuine PR CI exposed missing C5 and shared-protocol entries. Existing sync check, release check-versions --range-audit-advisory and diff check pass. No native build, release or publication claim.
`prev` was the last byte of whatever came before the `$`, so `$α$th` and
`$25°$C` read the `$` as an opener and printed as written. Look at the whole
character instead: a narrow one closes, as an ASCII letter does, and a wide
one still doesn't, because that's what `利率为$r$` needs after `$1000,`.
Signed-off-by: Lstarsky0 <[email protected]>
Refs CW-7704 and CW-7764.
Runtime SDK event/image/dynamic-tool declarations were separate from the
server structs. Move the existing 10-type Runtime family to the serde-only
App Server protocol leaf, migrate all 24 repository Rust consumers and delete
the old module. Keep native Engine/session/pricing/turn authority in place.
Reuse the existing Native host schema renderer for Rust-generated Runtime
JSON/TypeScript exports. Distinguish serialized output required/null fields
from valid input defaults; publish a separate serialized spec view of the
same Rust type. Native host generated output remains byte-identical. Move
the existing UI fixture helper onto App and migrate 23 callers so the
runtime-to-UI boundary ratchet passes without a new allowance.
Validation on 63 logical paths / 2338 frozen input hashes:
- Ordinary CLI build passed 331.682s; five affected lib test builds passed 420.276s.
- 10 moved serialization checks + 2 explicitly authorized schema cases passed.
- Both strict SDK type checks passed, including exactOptionalPropertyTypes.
- Existing focused checks passed: Engine 1, TUI 4, App Server 2; Native host
generated output is exact. Artifact runner's final kind-count was corrected
from 2 to 3 after independently verifying all actual subprocess exits/counts;
no passing test was reexecuted.
- Boundary/config/navigation/session source checks passed; config/navigation
use actual all-target normal Cargo trees. Prior failed invocations retained.
- Authenticated ordinary Runtime history/info + genuine SSE output-schema
control passed: 0 model POSTs, process/pipe/socket cleanup gates passed.
The finite SSE client closes deliberately; no server EOF/resume claim.
Full Codex methods/initialization/WS/tools/subscriptions/interactions,
P04 accounting, installed/native-platform/release evidence remain open.
No hosted CI, paid provider, installation, signing or publication ran.
Source receipt 80afa4de60858786bf67309666aab7447f28095f5a9c6950d91136377b33d015
Patch 0845ad69ed203637e1c7d6059120763b37772fde9481aeef46aab69c89a3d30f
Refs CW-7438, #6011 and #5581.
A refreshed MCP catalog can compare equal while its JSON property insertion
order differs. Returning unchanged after keeping that new Vec can alter wire
bytes without a declared tool-surface change. Restore the captured catalog
when neither values nor the active set changed; real edits still rebuild the
composition entry and declare the change.
Validation on frozen source20f8244a plus exactly2paths, 2319 input hashes:
ordinary CLI build passed336.295s; Engine lib test compile passed354.092s.
Five existing MCP replacement/deferral/schema and byte-stability checks:
test result: ok. 5 passed; 0 failed; 0 ignored.
Ordinary sandboxed CLI/MCP unchanged control passed:2 loopback synthetic
POSTs, identical2566-byte raw tools values, CLI exit0, real stdout/stderr
EOF, joined handlers/sockets, no forced cleanup and no owned MCP PID left.
MCP child self-exit/stdin-EOF lifecycle rows were not captured. This control
does not claim a new adversarial server key-order reproduction.0 paid or
CU calls; no new tests/comments, hosted CI, install or release.
Source receipt b9ab78b6eb25d56a47682c1bc931393acb3c13322338bb32a2a286cb715473b7
Patch182b5d77b67445dd0396342eb7057a82d198d05da841c5217483ec718bc48cd2
Bind automatic writes and conditional checkpoint retirement to the original
physical session/checkpoint pair and an opaque native producer. Keep SDK
checkpoint ACKs ordered through the existing actor and preserve newer
publications on stale, failed or closed producers. Protect history-operation
witness binding with its original reservation and protected re-read.
Keep readable transcripts available as data when optional accounting sidecars
are damaged, without granting a write receipt. Preserve display titles and
lifecycle metadata. Render exact typed compaction carriers as System receipts
on reopen so bare undo retains real request boundaries and model history.
Verification: normal CLI build 49.878s; affected TUI lib compile 81.067s.
59 Engine and121 TUI unique existing checks pass with frozen source-delta
reuse; all originally selected checks now pass. Nine ordinary Cargo-cohort
Manager controls pass. Authenticated local HTTP history/recovery/stale-source,
unknown-delivery and actual Native SDK checkpoint/cancel/next-user pass.
Real tmux TUI SDK/Actor journey passes with3 synthetic model POSTs,1 tool
execution, exact retained prefix, graceful exit and actual EOF. HTTP Native
SDK is separately3 POSTs; unknown-delivery is1. No external provider calls.
Receipts: CW/artifacts/engine-0103-execution-20261102/checkpoint-causal-v7/RECEIPT.json
and CHECK-COVERAGE.json; prior failures retained. This is a bounded local source
slice, not full P03/P04, native platform, installed-package or release readiness.
The read tool treated any file starting with `BM` as a bitmap, so notes that
open with "BM25 is..." came back as "[Image omitted...]" and the model never
saw them, while the read still counted for the freshness check on a later
write.
Also require the BMP header's four reserved bytes to be zero. Text doesn't
have NULs there and a real bitmap still gets the refusal.
Signed-off-by: Lstarsky0 <[email protected]>
try_render_env looks for \end{...} one byte at a time and slices the rest of
the string at each step, so the first multi-byte character in an environment
panics. A reply with \text{如果} or α in \begin{cases} or \begin{aligned} takes
the whole TUI down, and the saved reply does it again on --continue.
Step by whole characters. \left, \right and \big skip the delimiter after
trimming whitespace but add the length to the untrimmed offset, which lands
inside the next character for \left 如 and is why \left ( x renders as ((.
Measure from the untrimmed text.
Signed-off-by: Lstarsky0 <[email protected]>
Every column got an equal share of the terminal, so a column holding a
row number or a short flag took as much room as its neighbour, and the
long cell next to it wrapped with half the row empty.
Give each column the width its longest row needs. When they don't all
fit, the narrower columns keep what they need and the rest split what's
left, so a table that fits never wraps and one that doesn't wraps less.
A table narrower than the terminal now stops at its content.
Signed-off-by: Lstarsky0 <[email protected]>
The page led with what used to go wrong: which check failed on the hmdfs user
directory, what the old refusal said, why the wording was wrong. A reader who
wants to run Codewhale on a HarmonyOS PC had to work out which of those
paragraphs still applied.
It now states the two supported routes, the build and signing steps, how the
VS Code extension reaches the engine in CodeArts, and one table of what works on
a device. The user-directory section keeps only the facts that change a decision
there: where the store lives, how to get strict permission checks, and that a
workspace takes one driver.
The install guide gains a HarmonyOS row in the prerequisites and in the platform
table, so the native arm64 tarball and its signing requirement are visible where
people look for a download.
Verified: both languages carry the same nine sections and the same tables, the
in-document anchors resolve, the README translation and locale checks pass, and
the three new external links answer 200. No Rust or device run: Markdown only.
Signed-off-by: Ben Gao <[email protected]>
Use one capped encoder and writer for control and ACP output. Recover only
ACP typed input refusals after draining through the actual newline; keep
cancellation state and scheduler progress. Preserve physical write failure
through operation cleanup and stop before another frame. Retain exact
owner, permission, Core terminal and checkpoint authority. Compatibility
stdio/event/relay stdout now has the existing ACP 30-second write budget;
owner/readiness keeps five seconds. Reconcile English/Chinese Runtime API
docs and both changelogs, including the current bare listener's full router.
Validation: ordinary CLI build passed in 53.680 seconds; App Server/TUI test
binaries built in 338.734 seconds. All 31 existing checks passed: 15 App
Server + 14 TUI initially; two unchanged-binary TUI fixtures passed after
shortening only TMPDIR below the Unix-socket path limit. Original failures
retained. No new tests or code comments. Formatting and diff checks passed.
All 17 ordinary local CLI controls passed (one reused, 16 new). Actual
boundaries include exact 8 MiB input, cancellation/permission identity,
prewrite response/notification refusal, two physical 64 KiB partial writes
followed by terminal EOF/no second frame, and exact saved checkpoints.
Five local synthetic inference bodies, no external provider calls. Raw
outputs/checkpoints/teardown independently reviewed; no candidate forced
cleanup. Old relay failures and an 8,388,891-byte old output are retained
as baseline failures. All 2,319 guarded source/document inputs were frozen.
Limits: encoded-frame scope only, not global Value/history memory, all P01,
arbitrary-tool cancellation, primary P04, App Server v2 or release/native
platform/install qualification. Receipt and exact identities:
CW/artifacts/engine-0103-execution-20261102/bounded-io-v2/RECEIPT.json.
Report actual composition description/schema mutations through the existing
pending tool-surface reason before C5. Preserve equal schema values/order
and any prior declaration; keep C5 and conservative capability withdrawal.
Tell models nested results omit inline images, requiring direct discovery
and capture calls for visual input. Reconcile both changelogs.
Validation: ordinary CLI build passed283.450s; Engine unit build326.613s;
10existing focused checks passed,0failed/ignored. No new tests/comments.
Two ordinary CLI/MCP cases passed4actual local POSTs: unchanged tool
headers literally byte-equal; mtime-only config change retained bytes but
withdrew the batch onPOST2 under exact change:tool_surface repin, then
completed. Old baseline3POSTs reused, including exact pre-POST2 C5 failure.
Actual reader EOF, HTTP socket/handler joins and owned MCP process absence
settled both cases without forced cleanup; no external provider/CU call.
All2315 compiler inputs held unchanged. rustfmt/diff checks passed.
Limits: no original desktop/CU-error causality, broad schema-order cache
closure, provider-cache economics, native-platform/install or final release
claim. Immutable unsigned macOS ARM64 development CLI SHA256
3664afd40174030b82b033d56e9854052e1cbebf7bdb7a7be355862d11bddcd6.
Receipt: CW/artifacts/engine-0103-execution-20261102/
c5-composition-v1/RECEIPT.json.
A `$` opened inline math and closed at the next `$` that wasn't preceded
by whitespace or followed by a digit, however far away. `$5 a month` met
the closing `$` of a formula paragraphs later: the `$` went missing and
the paragraphs between ran together. `10$ per month and 100$ per year`
and `PATH=$HOME/bin:$PATH` lost theirs the same way.
An opening `$` now has to be followed by something other than whitespace,
and inline math stops at the end of the line. Reaching a `$` that opens
rather than closes gives up on the first `$`: one before `{`, or before a
letter or digit and not right after one or a closing bracket. `$n$th` and
`$(n+1)$th` still close.
Signed-off-by: Lstarsky0 <[email protected]>
Expanding tabs turned `-<TAB>-<TAB>-` into `- - -`, which the
list check took first. Check the rule first, which also makes `- - -` and
`* * *` rules instead of bullets.
Signed-off-by: Lstarsky0 <[email protected]>
A single successful MCP JSON text block now becomes the existing execute_tools result.content value, so composed programs read its fields directly. Normalize only a bare single block/envelope with absent or boolean-false isError. Keep mixed/rich/annotated/malformed envelopes intact. The original raw envelope size, spill decision and byte receipts remain authoritative; oversize results retain the raw prefix and full saved envelope. Describe the exact return shape to the model.
Coordinated source from the CU efficiency owner, root-reviewed at codemode.rs SHA256 c4b2893ce8b9205d1fe573e7f5ac07a5f6a1f3408b6178df6bcc84abf09b6e77. Reused the frozen ordinary CLI/unit builds and 16 passing existing codemode checks from the metadata integration batch; no checks repeated. An actual ordinary CLI + synthetic MCP + local SSE oracle passed 12 payload cases against both baseline and candidate expectations. It observed the two intended shape changes, preserved rich/metadata/malformed data and error handling, and verified the complete oversized spill payload. Each run made two local synthetic model requests, with zero real-provider or desktop calls. No new repository tests/comments.
Both changelogs updated. Native macOS synthetic evidence is distinct from installed CU/global app journeys and final release qualification. Existing macOS linker unwind-size warning remains; no hosted CI or publication.
Bound actual metadata inspection at16MiB with typed resource refusal and guarded regular-file reads. Reuse the parser for checkpoints, retain healthy picker rows with warnings, refuse incomplete strict catalogs, and keep potentially referenced stores/artifacts on uncertain inspection. Top-level key scanning cannot select nested metadata decoys.
Preserve lifecycle/title intent and resolve BOTH saved/checkpoint Runtime bindings under the existing per-session write admission before archival or replacement. Keep explicit rename/archive intent with held read-modify-write. Serialize checkpoint promotion, completion publication/clear and repaired-body writes at that same owner; keep failed-publication history in memory. Move the existing persistence actor's whole owned batch offTokio through bounded owner_work, without a second store/actor/queue. Add the compatible explicit-title marker and migrate existing constructors/fixtures.
Verified frozen18-path integration at aecf3b07d7 plus patch94a723c8d12fbdb3d0340927d1eb7a006eb86ab53af8bce17556738c07a84765;2312inputs unchanged. CLI and unit binaries built.64existing Engine metadata checks +58TUI checks passed;16separate codemode checks also passed; one intentional subprocess helper remained ignored at top level.26ordinary public-API native observations, actual held-IO PTY redraw/save and6ordinary authenticated HTTP observations passed. The baseline had15failing target observations and reproduced theUIstall. Budget632sites/171files, formatting/diff checks passed. Docs now name the observed413code. No new repository tests/comments; prior failed fixtures retained.
The compiled codemode companion is staged separately. macOS retains the __eh_frame size warning. Completed-turn/checkpoint causality before admission is still open; atomic clear is not that proof. Full-history/checkpoint reads, journal retention, aggregate scan growth and first/retried live-lease IO remain separate admitted work. Native controls use synthetic state/localSSE; no Windows, power-loss, live-provider, installed-package or finalCI claim.0.10.3 remains unpublished and NO-SHIP.
Finish inherited guidance changes: point the existing sole prompt, turn-loop and remember references at the extracted Engine. Preserve the Next.js guidance generated by the installed version rather than leaving its ordinary dev-server write dirty.
Verified all three Engine paths exist and the complete generated Next block matches the unescaped template in web/node_modules/next/dist/server/lib/generate-agent-files.js; git diff --check passed. Documentation only; no tests or builds repeated. Root CW operator controls were not copied.
CW-6944 D02-01/D02-10: publish one revisioned format3 worker authority under a short OS transaction lock, preserve foreign records/claims and fence stale per-writer checkpoints. Keep exact legacy recovery bytes and a tombstone against the inspected old publisher.
Retain pending terminal results and live ownership until durable publication; retry the exact winner and report failed delivery explicitly. Fleet reservations retain previous-attempt uncertainty until exact teardown or an authoritative receipt. Final stream tails, usage and the terminal receipt share the existing atomic ledger record; failed sync retries do not duplicate accounting. Carry the same executor and manager lock through bounded owner work, and migrate all callers.
Update existing fixtures for the real transaction/provenance contract; no new repository test cases. Correct architecture and both changelogs. Read-only peer review checked the final source and ownership boundaries.
Verified at base b8c5417927 plus patch 116d80bad7a2bb58d68b6ecda05c5566f9673c14de970d1da623ff86db7f54a7; 2312 source inputs unchanged through builds and observations. Ordinary CLI build and Engine/TUI unit-binary build passed; 77 existing Engine + 4 TUI checks passed. 24 ordinary public-API native macOS process/fault/restart observations passed, plus 2 ordinary CLI RLM/handle_read synthetic-model journeys. Prior failed revisions remain in the local receipts. Existing blocking budget and changed-file formatting/diff checks passed.
The macOS linker retains its __eh_frame size warning. Native observations use synthetic state/fake children with no provider/network; SIGKILL is not power-loss proof. Legacy admitted attempts without actual settlement fail closed. Native Windows, installed packages, hosted final CI, providers and release qualification remain separate. CW-6945 retry/replay policy and the wider 0.10.3 plan stay open; no publication.
ratatui drops control characters when it paints a line, so a tab in a
span takes no cells. Go and Makefile code blocks lost their indentation
and `a<TAB>b` printed as `ab`, while the wrapper counted each tab at the
next 8-column stop.
Expand tabs on each logical line before parsing, and on each line of a
plain message, at the same 8-column stops the wrapper already uses.
A partial mouse selection now copies the spaces on screen. Selecting a
whole message, /copy and /export still give the source. Two copy tests
pinned a tab taking no cell; they now aim at the cells it takes.
Signed-off-by: Lstarsky0 <[email protected]>
Refs #6554; CW-6943. Reuse the existing shared parser and CountingWriter before result clones. Bound 32 raw fields, 4096 path bytes, 128 steps, 1024 intermediate matches and 1 MiB aggregate serialized projected fields including escaped keys and delimiters.
Verification on unchanged 2312 recorded inputs: 27 existing Engine handle/fetch cases; 22 actual ordinary-Engine ToolSpec observations; 2 ordinary CLI journeys using real RLM FINAL handles (exact success and 1025-match refusal); 18 unchanged included-source controls reused. Ordinary CLI build and focused rustfmt pass. Fetch success controls seed the public production cache; no origin HTTP, installed-package or final-release claim. Existing Engine lib-test compact-unwind warning retained. Receipts: CW/artifacts/engine-0103-execution-20261102/jsonpath/RECEIPT.json.
Use the existing owned Engine retrieval boundary for asynchronous 128 KiB
retained-result windows, cancellation, integrity checks and contiguous byte
recovery. Delete the duplicate whole-file TUI readers. Bound output/diff rows
and charged cache allocations; retain exact source copy and decision values.
Reuse the ANSI scanner and bounded grapheme normalizer. Translate 29 typed
notices across all 15 complete language packs. Keep one runtime/store owner.
An ordinary question journey found that request_user_input skipped pre-fanout
output preservation. Its exact long label was lost in a truncated JSON reply
with no artifact. Route that branch through the existing preservation helper;
the inline model view stays bounded while owned retrieval returns exact bytes.
Verified local evidence (not final release/installed/Windows qualification):
- Five-crate existing library compilation; 334 distinct scoped cases passed.
Reuse unchanged source-owner checks after the final question-only correction.
- Fresh affected Engine compilation and 51 existing input/cancel/durability,
child-cap and spillover cases passed with the standard 16 MiB harness stack.
- Four fresh-library cache/LRU, ANSI, exact-scalar and oversized display probes
passed; charged cache peak 4,063,568 of 4,194,304 bytes, max four variants.
- 16 ordinary macOS CLI modes passed: 12 unchanged producer/window/refusal/
cancel/session modes reused plus four fresh affected question modes. Normal
4 MiB and 120,000-line output reconstructed exactly across all byte windows.
Each long-label answer retained 65,709 exact artifact bytes; real tool-search
and 17 retrieval windows recovered its original label/value/kind.
- Fresh ordinary CLI d6c478baec7b8a8f3b57f2d50257acd513ff9c4ab0e0febbfe0da94ca66622b5
compiled on 1,762 unchanged inputs. Formatting/diff checks pass. Blocking
budget ratcheted to 631 sites/171 files; dead-code remains 137.
Retain failed attempts and scope: capture timing/wrapping failures, original
answer-loss reproduction, initial compile corrections, default-stack harness
abort and bundled-changelog-only correction during Engine compilation. The
Engine compile passed; the corrected CLI freeze and compilation were separate.
No new repository test cases or code comments. No native Windows, external
provider, system clipboard, global memory bound or direct open-modal identity
change is implied. Root/packaged changelogs included; no CI/release action.
Receipts: CW/artifacts/engine-0103-qualification-20261101/pager-u04/
CLI-QUALIFICATION-MANIFEST-v2.json and answer-preservation/.
CW-6943; U04-01. Remaining umbrella findings stay open.
Maintainer follow-up on original PR #7192 by @Lstarsky0. Preserve original contributor head as a parent and merge current main; decode character references once in ordinary Markdown while leaving code/link trust/control guards intact. Bound trailing-punctuation entity lookup to the existing ten-byte entity window rather than rescanning the whole URL.
Use explicit Prose/InlineCode/Plain review contexts across every existing caller, preserving literal plugin names, paths and JSON argv through transcript note preprocessing and the approval pager. Remove argv filesystem classification: raw cwd was not canonical plugin authority and could mislabel process-CWD files or race between the two presentations; every argument remains positionally visible as lossless JSON. No new tests/comments. Root and packaged changelogs credit the contributor.
Verification: actual full TUI test-target build passed on1761 unchanged frozen source inputs.97 affected existing cases passed (21 plugin command/guided-review tests +76 command-contract tests). Unchanged corrected Markdown component216existing/55manual and combined pending-images+BR216existing/63comparisons reused with exact source hashes. Fresh actual complete-renderer component passed8Prose/Plain bundle observations across absent/files/directories/symlinks in isolated CWD;16argv values round-tripped32times. Original preserved binary reproduced four wrong plugin-path labels. Original quadratic400k punctuation1.947s vs corrected14.6ms counterexample retained. Nine qualified code postimages are exact; changelog-only additions follow compilation.
Limits: scoped macOS full-caller/component/command checks, not an ordinary mounted review-pane/CLI, platform, fullsuite or release pass. Protected checks at this repaired head remain required; no workflows launched/waited/repaired, no merge/protection change/branch deletion. No signing/deployment/publication.
Evidence: artifacts/engine-0103-qualification-20261101/prs/7192/live-qualification/{OVERLAY,source-before,build-result,existing-results,RESTORED}.json; correction/{MANIFEST,ARGV-IO-BUILD,ARGV-IO-OBSERVATIONS}.json; RECEIPT.json. Original contributor commit45e17fb279fae28e876816c7ce4db52aac7d0bd5 remains in this PR history.
Move the existing305-line avatar contract and its three existing tests intact into Protocol. Kit's existing public path reexports those same types; Engine's two owners and TUI's two data consumers use Protocol. Rendering/artwork/player sources remain unchanged, including their ownership checks; the existing Engine ownership fixture now creates a tiny valid pack rather than native art. Preserve donor MIT notice in packaged LICENSE.avatar. Remove Engine's direct kit dependency, add kit→Protocol, resolve both standalone lock graphs with original pet versions retained. No new tests/code comments/crate/second parser.
Release helper includes the kit's Protocol pin and both standalone Protocol lock records in the same bump/rollback transaction, preserving independent kit0.2.0. Minimum registry Protocol must become0.10.3 at final family preparation, before separate Protocol→kit→dependents publication. Reconcile stale root0.1.3 changelog text; root/packaged and kit changelogs/README/coarse design updated.
Verification: six-crate existing test-target build passed on1761 frozen inputs.7 affected existing cases passed: moved Protocol avatar3, actual Engine avatar ownership2, TUI avatar2. Real standalone kit lib locked/offline check and pet locked check passed with lock hashes unchanged; initial pet offline miss(lru0.18.4) retained and exact package downloaded without version churn. Existing prepare-release, version and45-crate publication-order helpers passed. Two actual maintained-fixture helper observations prove Protocol pin/two-lock bump, independent kit version retention and exact byte rollback on downstream failure (Cargo shim explicitly distinct from real locked checks). Protocol package inventory includes both avatar.rs and LICENSE.avatar. No unchanged artwork/runtime rendering tests rerun.
Limits: Engine retains direct ratatui/crossterm/Palette coupling; host move and StageB open. Local current family0.10.2 pin is implementation state, not an installable registry minimum. No kit/Protocol publication, hostedCI, nativeWindows, final artifacts, signing or deployment.
Evidence: artifacts/engine-0103-qualification-20261101/coarse-prereq-integration/{build-result,lower-existing-results,engine-existing-results,tui-existing-results,standalone-checks,pet-tui-online-check,release-helper-results,avatar-release-helper-observations}.json; protocol-package-list.log and prepare-release-test.log. Refs#5586,#5316; CW-7764.
Move the existing socket inputs/resolver/constants/error data and Unix store-derived sibling layout into Paths; AppServer reexports the same API and retains transport, credentials/leases/ACLs and Windows owner/pipe derivation. Migrate all internal layout consumers. Sandbox read_guard changes import only; remove its sole AppServer normal dependency. Existing comments/docs move with implementation; a narrow fulfilled missing_docs expectation preserves the moved enum's thirteen already-undocumented named fields. No new tests/comments, helper pool or copied policy.
Verification: six-crate library test-target build passed on1761unchanged frozeninputs;48focused existing cases passed (Paths4, Sandbox read_guard25, AppServer daemon_socket13, TUI selected socket6). Source comparison keeps resolver/store hash and both Windows ownership derivations exact. Actual locked48-workspace production graph, including optional/target edges, is acyclic and Engine/Sandbox no longer reach AppServer; Paths has zero internal dependencies. Existing45-crate publish-order and version helpers passed. Existing public aliases are exercised by AppServer tests. Root/packaged changelog and coarse design status updated.
Limits: this removes the cyclic dependency prerequisite; host-band move, avatar/terminal residuals and StageB remain separate. Native macOS proof does not establish Windows transport/reparse or final distribution acceptance. No fullsuite/hostedCI/signing/deployment/publication.
Evidence: artifacts/engine-0103-qualification-20261101/coarse-prereq-integration/{source-before,build-result,lower-existing-results,tui-existing-results,actual-graph,release-helper-results}.json and coarse/socket-layout/{STATIC-REVIEW,graph-receipt}.json. Refs#5586,#5316; CW-7764.
Use existing explicit-root ExtensionHostManager/TestManagerGuard for discovery and selected-retry fixtures; propagate the existing environment ticket through the actual spawn_blocking path in cfg(test) and retain the sealed home. Join/abort/drain the fixture's own manager and server before releasing its root. Request assertions and timers stay intact. No new repository tests/code comments and no production connection/credential-policy change.
Verification: current TUI MCP filter125/125passed, selected control-socket6/6 and avatar2/2passed separately (not counted in125). Engine MCP447passed/2ignored is reused from the prior fixture freeze: its fixture/owning MCP source stayed unchanged; the identical five failures had independently reproduced on the pre-MCP binary and all passed isolated. Current six-crate test-target build passed on1761unchanged frozeninputs. Actual native pagination sample previously caught the blocked worker at skills discovery/env mutex; receipts retain the original counterexample and intermediate TUI124pass/1failure. Root/packaged changelogs synchronized.
Limits: focused existing parallel fixtures, not a full suite, modern-MCP protocol, live OAuth custody, Windows, provider or release claim. The socket/avatar/security batch compiled together is split into coherent commits; no CI/signing/deployment/publication occurred.
Evidence: artifacts/engine-0103-qualification-20261101/coarse-prereq-integration/{source-before,build-result,tui-existing-results}.json; coarse-fixture-existing-results.json; mcp-failure-diagnosis/{RECEIPT,native-pagination/RECEIPT}.json. Refs#5637; CW-6257.
Active-session evidence now opens through the existing Runtime session-relative confined owner before using the same pinned file, stream and hash. Previously canonicalizing a linked session/artifacts directory promoted an outside directory into the authority root; an actual classic-reference ToolSpec request returned a synthetic outside payload. LegacyGlobal retains its existing confined opener and digest/origin checks. No pager feature, new parser, test or code comment.
Verification: all14 existing retrieval cases passed.12 actual current ToolSpec filesystem controls passed: relative/absolute/nested owned reads, deliberately relocated application sessions root and owned legacy sidecar returned exact bytes; session-parent/in-root-session/artifacts/leaf links, outside hardlink, foreign namespace and wrong legacy origin refused. Payload/metadata hashes stayed unchanged; denial output excluded the synthetic payload/outside path. Actual fresh Engine/AppServer dependency fingerprints pin the same Runtime owner. Six-crate existing test-target build passed on1761 unchanged frozen source inputs. Root and packaged changelogs synchronized.
Limits: scoped macOS synthetic filesystem and actual ToolSpec observations, not Windows reparse, kernel syscall interruption, real-user/provider, full-suite, hostedCI or release acceptance. Other coarse/fixture changes in the compiled batch remain separate commits. Signing/deployment/publication not performed.
Evidence: artifacts/engine-0103-qualification-20261101/retrieval/session-parent-fix/{RECEIPT,link-result,PREP}.json and coarse-prereq-integration/{source-before,build-result,engine-existing-results}.json. Historical counterexample: retrieval/pager-parent-symlink-baseline/RECEIPT.json. Refs#6149; CW-6943.
Move the existing owner_work and RuntimeControlFrontend into Runtime and migrate every internal fully qualified caller. Keep the two public App Server paths as aliases to the same function/type, preserving source compatibility and one static four-slot semaphore. Preserve captured closures, handles, permit lifetime and enum variants. Remove the direct Engine→AppServer manifest/lock edge; add the acyclic AppServer→Runtime edge. No new pool/runtime/session authority.
Verification: shared Runtime/AppServer/Engine/TUI existing test compilation passed on1756 unchanged frozen inputs; actual normal CLI --tests typecheck passed. The2 existing AppServer owner admission/bootstrap cases passed.2 actual component operations passed: metadata plus oldAppServeralias occupying4slots while newRuntimeowner and Engine retrieval both queue. Abortedwaiter retains workerpermit, dropped closure neverexecutes,5heartbeats, cancel18us, follow-on exact metadata1.44ms after one old-alias release, max4active and allworkers exited. Independent Cargo fingerprints pin one actual Runtime library across Engine/AppServer. Fullblocking635/172, locked45crate publication-order metadata, formatting and diff checks passed. No new tests/codecomments; existing docs move with implementation.
Limits: this is one coarse prerequisite, not StageA2/B completion. Production Engine→Sandbox→AppServer remains through the shared protected socket-layout owner, which must move intact before AppServer can depend on Engine. Avatar/kit decoupling, host-band move and StageB remain open. Companion existingMCPfixture repairs in the same compiled dirty batch are not staged here; their scoped receipts and remaining baseline failure are separate. No fullsuite/hostedCI/Windows/distribution/provider/publication claim. Debug unwind-table warning retained.
Evidence: artifacts/engine-0103-qualification-20261101/coarse-fixture-{source-before,build-result,cli-check-result,existing-results}.json and retrieval/owner-relocation/QUALIFICATION-RECEIPT.json. Root/packaged changelogs included. Refs#5586,#5316; CW-7764.
Use stable first-occurrence root deduplication once in the existing Fleet owner and a component-sorted covering-subtree scan for cross-task collision refusal. Preserve the existing normalizer/overlap predicate, scope admission and worker limits. Do not add an arbitrary task-count cap to the deliberate uncapped-agent policy. With multiple invalid claims the first reported pair/error can change; HTTP400/refusal and zero execution remain intact.
Verification:78 existing worker-runtime checks passed. Actual ordinary CLI matched all52 frozen HTTP observations (24semantic+28performance) against the preserved baseline: identical payload hashes and response/refusal categories, unchanged Fleet/thread/workspace projections, zero provider operations and zero workers. Largest1,968,597-byte request stayed under existing2MiB HTTP cap. At12,288 disjointtasks median wall1.6371s→0.08664s, processCPU1.64s→0.085s; one task12,288roots1.1924s→0.01940s, CPU1.19s→0.02s. These are scoped macOS preprocessing observations, not executed-agent or provider/task latency claims. Existing shared Engine/TUI/CLI compilation passed; no new repository tests/comments.
The attempted TUI filter parallel_write selected zero existing cases and is excluded. Actual HTTP controls cover punctuation/order/prefix/root/nested/same-owner/traversal/control-character semantics. Inherited concurrent MCP test failures are retained separately and not a Fleet gate claim. Root/packaged changelogs included; final supported-target/distribution qualification remains open.
Evidence: artifacts/engine-0103-qualification-20261101/unbounded-tail/fleet-cpu/{BASELINE-RECEIPT.json,CANDIDATE-RUN-RECEIPT.json,CANDIDATE-COMPARISON.json} and mcp-fleet-io-cli-build-result.json. Actual candidate binary SHA740e3a2a6f1938b44c252b86ba6059b2d46098dc9514780a3d75e530a3ef26d5. Refs#6554; CW-6943.
Reuse the existing AuthContext from Engine/thread-manager construction through pools, connection admission, pending/supervision/retry, reload/fork, child Engines, Rust/SDK stdio and HTTP/SSE, and OAuth discovery/login/retry setup. Missing injected values never consult ambient secrets. Legacy standalone constructors share one immutable zero-state process context; reviewed plugins retain exclusive captured HostEnvironment authority. Mask captured-environment Debug; reject foreign-context connections and warm handoffs, including placeholder-resolved stdio handoffs without a current resolution receipt. Preserve bootstrap allowlist and optional HTTP credential semantics.
Actual qualification:24 ordinary public-API observation groups plus6 separately labelled plugin controls passed, with20 independent run-oracles,118 local HTTP requests and18 stdio launches. Same-PID A/B scopes exclude globalPOISON across Rust/actual HostSDK stdio and HTTP/SSE, missing credentials, foreign admission/handoffs, reload/retry/fork/supervision, two real Engine owners and workspace pool rebuild. Required placeholders refuse before launch; optional HTTP inputs omit credentials and preserve remote401. Host had actual Node children with no fallback; all descendants exited. OAuth setup made no authorization/token exchange. Plugin-only existing test-support constructors seed synthetic captured environments before real discovery/trust/transport; ordinary profile has only default feature, no test-support.
Existing checks:442 EngineMCP +123TUIMCP +2plugin checks passed in concurrent batches; the7 initial failures passed individually. The pre-change retained Engine binary reproduced exactly the same5 concurrent Engine failures (442pass/5fail/2ignored), and all5 pass isolated on both binaries. This establishes inherited batch sensitivity, not a clean concurrent gate;2TUI isolatedpasses are not a baseline batch proof. Source/task-lifetime diagnosis and existing-fixture isolation remain open. No timer softening or new repository tests/comments. Fresh test/ordinaryCLI builds passed on1755 frozeninputs, with debug unwind warnings retained. Initial wrong import and fixture-only failures retained/corrected.
Limits: no durable OAuth token-store custody or live revocation/rotation guarantee; existing OAuth clients snapshot headers. No complete bootstrap/filesystem/tenant isolation, modern-protocol/provider/native-platform/final-release qualification. Source and root/packaged changelogs included. No signing/deploy/publication.
Evidence: artifacts/engine-0103-qualification-20261101/mcp-secrets/ACTUAL-RESULT-v2.json; mcp-failure-diagnosis/RECEIPT.json; mcp-fleet-io-{build-result,cli-build-result,existing-results}.json. Refs#5637; CW-6257.
Replace whole-file reads in all seven retrieval modes with one pinned confined regular-file stream, complete digest/count verification and bounded text/slice retention. Preserve session/legacy ownership, redaction, generation, expiry and corruption refusals; never return a partial success. Limits:512MiB/20s verification scan,1MiB raw text line,8MiB selection,4KiB query and32KiB reference. Byte mode preserves exact recovery for giant/binary lines.
Resolve/authorize/open/initial metadata and final metadata validation reuse the existing four-slot owner_work. Cancellation/drop remains prompt during admission/preparation; real worker retains permit/handles until exit. No new thread pool or evidence authority. Bound the shared legacy sidecar reader to64KiB and reject links/special files for both callers.
Verification:45 existing checks passed (retrieval14+truncate31), fresh shared Engine/TUI/plugin test compilation passed on1755unchanged frozen inputs. Actual current library:20 affected ToolSpec,4 real legacy publisher/ownership,7 shared-reader controls plus1 actual four-slot admission/cancel/drop/recovery operation passed.256MiB tail/bytes RSS about11.1MiB; queued cancellation21us with current-thread heartbeat and permit retention. Earlier91ToolSpec+5reader controls cover unchanged stream/selection logic with their own old-library receipts, not relabelled as current. All7 modes previously observed on32/128/256MiB, exact512MiB metadata/bytes accepted; current representative large controls passed. Full blocking budget635sites/172files, source hashes and diff check passed. No new repository tests/comments or external provider call.
Limits:20s timeout branch source-reviewed, not exercised; kernel filesystem syscalls cannot be forcibly interrupted and keep their permit until return. Existing TUI raw payload pager remains a separate unbounded-read frontier. No installed/native-Windows/final-release evidence. The separately dirty MCP batch has7 initially failing concurrent checks under investigation; this commit claims only its scoped45existing+32actual proof and does not ship those dirty files.
Evidence: artifacts/engine-0103-qualification-20261101/retrieval/{QUALIFICATION-RECEIPT.json,io-offload/RESULT.md,io-offload/observations/QUALIFICATION-RECEIPT.json}, mcp-fleet-io-build-result.json and mcp-fleet-io-existing-results.json. Root/packaged changelogs included. Refs#6554; CW-6943.
Render at most64 placeholder rows for legacy child-count summaries, retain omitted counts in the existing panel serialization, and mark missing outcomes/usage/elapsed Unknown. Preserve u64 totals through the final native workbar without narrowing on32-bit targets. No arbitrary cap on actual agent tasks.
This changes the public independent kit total API from usize to u64. Advance all Engine path pins/locks from unpublished0.1.3 to unpublished0.2.0, document the migration, and keep registry publication/package qualification separate. Published0.1.2 lacks the existing Role::Accent vendor API; a0.1.x upload would also incorrectly promise source compatibility. No package uploaded. Canonical sibling refs preserved in the qualification backup; no blind sibling replacement.
Verification:31 existing workflow-panel and128 existing vendored terminal-kit checks passed. Actual public NativeChrome component retained u64::MAX exactly at widths40/80/140, with empty unknown-outcome bar and no invented time/tokens. Source branch review proves at most64 placeholder rows and lossless persisted omitted counts. Full CLI with0.2metadata compiled against1755 unchanged frozen inputs; linker unwind-table warning retained. Versions/release-note receipts, full blocking budget and diff checks passed. No new repository tests or code comments.
Limits: a normal mounted producer of bare u64::MAX summary has not been established; failed seeded admission is retained and excluded. This is component/source evidence, not an ordinary mounted history or32-bit/native release qualification. Separate approval for kit0.2publication plus registry checksum and normalized Engine/TUI package checks remains required.
Evidence: artifacts/engine-0103-qualification-20261101/{bounded-existing-final.json,bounded-020-build-result.json,unbounded-tail/workflow-replay/QUALIFICATION-RECEIPT.json,unbounded-tail/ratatui-publication/PUBLICATION-PACKET.md}. Refs #6554; CW-6943.
Continuously drain the Python child stderr pipe into a 64 KiB diagnostic capture, counting omitted bytes, instead of waiting until the protocol round finishes. Bound telemetry admission to 512 events with reserved final-control capacity and read only its 256 KiB/512-record disk tail. Preserve opt-out, tombstone and FIFO persistence behavior.
Verification: 89 distinct existing checks passed (REPL36, telemetry53). Nineteen actual component observations passed:9 REPL,7 public buffer/RSS,3 exact actor-owner observations. The old1 MiB stderr deadlock reproduced; candidate2 MiB completed in23ms. A512 MiB sparse telemetry file retained64 records at6.4 MiB RSS. One consent fixture initially failed because the runner forced telemetry off; exact rerun without that override passed. No external network/provider call, new repository test or new code comment. Cargo fmt, staged-source blocking budget and diff check passed. Full-tree budget correctly holds the separate uncommitted saved-result preparation fix.
Evidence: artifacts/engine-0103-qualification-20261101/{bounded-existing-final.json,BOUNDED-RESULT.md,bounded-runtime-qualification.json,bounded-020-build-result.json}. Core source compiled with293 distinct existing checks across the frozen bounded batch; this commit claims only its89 covered checks. Remaining resource frontiers and final native/platform/release gates remain open. Refs #6554; CW-6943.
Ordinary undo could restore an older invocation after conversation-only
backtracking and a repeated request without current snapshots. Actual
terminal file loss was reproduced before this fix.
Route ordinary /undo and the new /undo select picker through one async
owner. Check the matching Engine conversation, persisted host UUIDs,
completed owned request/per-tool windows and live paths before restoring.
Hold managed workspace admission through guarded file rollback and
acknowledged Engine rewind/save; respect active goals, delegated work and
shell completion. Cancellation retains the guard inside the file worker.
Force never waives identity or live-file protection. Reuse the Runtime
transaction/history overlay; compatibility adapters lose physical restore
authority. Read-only and tool-free requests can rewind without Git.
Show Checking, Restoring, Saving and completion through command receipts,
with full refusal/save-failure rows and visible empty notices. Esc-Esc
continues to affect only conversation. Legacy/multipart/missing receipts
and blank-first-line legacy Git subjects refuse with recovery guidance.
Existing test expectations follow deferred ownership and safe refusals;
no new repository tests or code comments.
117 distinct existing checks passed: commands 28, mutation 38, Runtime
patch-undo 19, backtrack 23, toast 16, feature registry 2 (filters overlap;
counts deduplicated). Initial dead-code compile failure and incorrect
blank-first-line fixture expectation are retained; corrected case passed.
Reused locale parity for 15 unchanged 2753-key catalogs. Format, vocabulary,
versions, contributor credit and whitespace checks passed.
Actual default-Ask macOS PTY matrix 14/14: independent file hashes/journal
IDs; hard restart with zero automatic requests and explicit follow-up wire;
repeat prompts, stale single/range, force refusal, later edits, active turn,
cancel, multipart, legacy, no-Git chat/read and save failure. Actual terminal
frame replay proves all 3 progress phases then completion; repeated/empty
undo and empty selector are visible. v7 data-path receipts reused; v8 reran
range/single/display. CLI SHA256:
56d9a34e3df4991a3a825066f40d7750b163245849bb792a138bb0228b664975.
1755 compile inputs frozen; only clearer embedded release notes changed
at landing. Linker warned __eh_frame exceeded 16 MiB. Native Windows/final
artifact checks, full suite, CI and release remain unqualified.
Refs CW-7418/#6970. Thanks @luestr for the report.
Evidence: CW/artifacts/engine-0103-qualification-20261101/windows/
terminal-matrix-final.json, existing-checks-final.json, landing-source.json.
Keep Dependabot's original PR and branch. Official package integrity, 21 existing commerce cases, four zero-egress fake-Fetch requests and a combined project TypeScript overlay passed. Checkout create/retrieve serialization and signature acceptance/refusal remain compatible. The SDK default API version advances from 2026-09-30.endive to 2026-10-28.endive; account/webhook version and live activation remain separately unqualified. Required Lint, Windows tests and npm wrapper contexts passed; no open dependent PRs or substantive review findings. Optional Ubuntu Rust tests were still running at snapshot. No billing, webhook, activation, deployment or release action.
Keep Dependabot's original PR and branch. Exact package integrity and project-wide TypeScript overlay passed with zero diagnostics. Required Lint, Windows tests and npm wrapper contexts passed on this head; no open dependent PRs or substantive review findings. Optional Ubuntu Rust tests were still running at readback; this source-only type-declaration change does not qualify the final Engine release.
Backport upstream minimatch preprocessing and glob abort fixes while keeping
existing glob, ESTree and build-tool APIs. The rejected minimatch 11 override
silently enumerated no files with real glob 9/12 consumers. Preserve versions,
licences, original lock bytes and complete upstream audit findings.
Postinstall verifies exact versions and complete original/patched hashes for
all declared distributions and four bundled runtime copies. The audit checks
all concrete reachable advisory leaves and rejects unknown/missing findings,
changed sources and pure cycles. Legacy minimatch 3 lacks the reported path.
Verified fresh scripts-enabled npm ci (896 packages),7 existing security cases,
targeted ESLint, 54 manual preprocessing/abort/bundle/offline-load controls
and 7 real glob/ESTree/legacy consumer observations on Node 22.20.0. All 12
matching runtime files accounted for; official archives matched lock SRI.
Actual abort controls observed zero reads after rejection. Public NFT tracing
preserved 4 files/0 warnings; its private cancellation remains source/hash proof.
Raw audit retains 18 moderate package findings; verified mitigation wrapper
passes, alert #244 remains OPEN. No registry-fixed, alert-dismissal, full web validation,
hosted CI, provider, deployment or release claim. Refs CW-6949/#6560.
Evidence: CW/artifacts/engine-0103-qualification-20261101/minimatch/RECEIPT.json
Upstream: isaacs/minimatch 77a0f5c and isaacs/node-glob 4682e8c.
Co-authored-by: isaacs <[email protected]>
Keep React and React DOM on 19.3.1 in PR #7187. The Dependabot update
advanced React DOM while retaining React 19.3.0 in the lockfile, which
throws an incompatible-versions error before rendering in both modes.
Verified official npm tarballs against registry SHA-512 integrity and
the PR lockfile. On Node 22.20.0, the original version pair refuses
both development and production rendering; the aligned pair renders
the expected HTML in both modes. A read-only TypeScript 5.9.3 overlay
using @types/react-dom 19.3.0, @types/node 26.6.6 and Stripe 23.1.0
checked 458 web root files with zero diagnostics.
No installed dependency tree, source checkout, API, or webhook setting
was changed during qualification. Full web release gates and protected
checks for this repaired head remain separate.
The inline parser printed <, & and | as written, and a link
kept the & in its target, so the query string it opened was wrong.
Decode them in plain text, link labels and link targets, leaving code
spans alone. Plain text decodes the whole run instead of stopping at each
&, so Vec<u8> stays one word for the wrapper. The result is text, so
<br> prints as <br> and doesn't break the line.
A reference to a control or hidden character stays as written, by the same
hidden_chars check the GitHub report tool uses, so a reply can't put ESC or
a bidi override on screen this way. A reference at the end of a bare URL
stays outside it, as in GFM, so <https://a.test> links to
https://a.test.
escape_review_text now escapes & as well, since & has become markup and
plugin review text has to show what's in the file.
The names match decode_html_entities in the web scraper, which is in the
engine crate; tui-kit doesn't depend on it.
Signed-off-by: Lstarsky0 <[email protected]>
Refs #6554 and CW-6943. Reuse the release HTTP reader for OpenSandbox: reject success bodies over 8 MiB, cap errors at 4 KiB and sanitize remote controls before display. Parse failures expose location without echoing remote bytes. Share the existing 512 KiB skill reader across discovery and audit markers, preserving approved global symlinks and useful refusals. Reject aggregate web.run batches over 64 operations before cache or network effects.
Existing local checks passed: release 57, sandbox 104, skills 182, WebRun 34. Manual public-entry probes passed 11 sandbox wire cases, 11 skill filesystem cases and 10 WebRun cases. The latter independently recorded 72 loopback requests, zero rejected-operation requests, an unchanged turn counter and retained cached pages. No provider inference or deployment.
The shared HTTP extraction also uses saturating overflow accounting. The final release checks reran after that arithmetic guard; the unchanged finite-cap sandbox and skill behavior retains its original receipts. Full CLI compilation passed with all 1740 frozen source inputs unchanged. Native debug links emitted the recorded __eh_frame size warning.
Exact source fingerprints, commands, binary hashes, earlier failed controls and verification limits are retained under CW/artifacts/engine-0103-qualification-20261101/. This lands three bounded-read findings, not the entire audit umbrella or a frozen 0.10.3 release candidate. Remote exec cancellation and other admitted requirements remain open. The separate undo draft and its release note are excluded from this commit.
The stale singleton had no package locale, exceeded the one-installer schema limit and repeated ZIP installer identities. Match the actual portable x64 contract, VC++ dependency and codewhale command. Look up the exact compatibility asset in the release checksum manifest and keep the mirror byte-identical.
Native WinGet 1.29.380 validated the generated 0.10.1 and 0.10.2 manifests against the actual published checksums. Four invalid inputs preserved both outputs. Sixteen ordinary-user checks on Windows 11 ARM64 covered x64 0.10.1 install, upgrade to 0.10.2, payload hashes, candidate owner delegation, removal, and exact user PATH restoration. ShellCheck, bilingual guide rendering and embedded changelog sync passed. Distribution publication and native Windows 10 floor validation remain separate.
Preserve an existing trailing delimiter when appending the install directory. The existing exact-entry removal then reconstructs the original PATH, including its registry type. Remove only the two installer values from the shared product key and remove the parent only when empty.
In an ordinary Windows 11 ARM64 account, the original x64 installer lost the trailing separator and an independent product subkey. The repaired NSIS 3.13 setup passed 30 checks over two install and uninstall cycles with the same frozen x64 development CLI. Both command hashes, Apps registration, shortcut target, owner advice, PATH bytes and type, and unrelated settings and files were checked. All 18 existing PowerShell assertions passed. Native x64 hardware, the supported OS floor and final distribution artifacts remain separate. Refs CW-7542.
The bare-URL scanner consumed the break and the next word before the inline parser saw them. Stop at a recognized break while keeping encoded angle brackets and other existing URL syntax intact.
Verified against the original PR integration tree with 216 existing TUI-kit checks, seven inline-code cases and 13 focused renders, including multilingual URLs and tables at two widths. The submitted head reproduced the incorrect link target.
Signed-off-by: CodeWhale Bot <[email protected]>
Windows PowerShell in a detached process returned success but left the
renamed executable on disk. Use the SDK hidden-console flag for the
existing deferred helper. Keep the parent wait, receipt validation,
guarded rename and exact literal-path deletion.
Reproduced a 189,454,848-byte leftover in ordinary Windows Terminal on
Windows 11 ARM64. A three-mode ordinary-user probe isolated the creation
flag. A second reproduction and the paired corrected replay ran under
the guest agent. Two real archive install/uninstall cycles passed all 19
checks there, including both command names, receipt removal, dry runs,
unrelated files, workspace and user PATH preservation. All 4,200 source
inputs matched before and after the full ARM64 MSVC development build.
Formatting, version consistency, changelog sync and contributor credit
checks passed. Other architectures and final packages remain unqualified.
Refs #7091 (CW-7542).
The contributor PR merged as cfef335d1c. Its integrated renderer passed all 216 existing local kit checks and seven targeted rendering comparisons. All three contributor credit surfaces already include @Lstarsky0. The changelog mirror, credit, version, formatting and whitespace checks pass.
CW-7553: register Tokio's CtrlBreak and CtrlClose streams alongside CtrlC
and route all three through the existing bounded shutdown task. Preserve
its two-second Runtime API and persistence drain and second-signal escape.
Correct the existing claim that CtrlC also receives CtrlBreak.
Native Windows 11 Pro build 26300 ARM64/MSVC receipts: the exact adapter
excerpt handled CtrlC, CtrlBreak and real ConPTY close, 3/3 with exit 130.
The baseline handled CtrlC but default-terminated on the other two events
with 0xC000013A. All 15 receipt-program dependency versions and checksums
match the workspace lock. Rust 1.99.0; unchanged source hashes recorded.
Cargo format and diff checks pass; embedded changelog regenerated.
This proves Windows control-event dispatch, not full Engine persistence,
ordinary Windows Terminal acceptance, or the original Deepin report.
Those platform journeys remain release requirements.
A markdown table row has to fit on one source line, so <br> is how a
cell holds more than one line, and models write it often. The renderer
printed the tag as text: Default: 3<br>Set to 0 to turn retries off.
Parse <br>, <br/> and <br />, in any case, as a line break and end the
row there. A table cell lays out each part that fits straight from its
spans, so inline code keeps its leading spaces, and goes through the
wrapper if a part doesn't fit. This lives in the inline parser, so a tag
inside inline code stays as it is, **one<br>two** stays bold on both
rows, and a paragraph or list item breaks the same way.
Signed-off-by: Lstarsky0 <[email protected]>
When the active keyless local default still has an unverified context window, refresh its existing endpoint catalog before Auto classification and route admission. Use the bounded catalog read. A configured or known window skips the refresh.
Matched Ubuntu ARM64 terminal runs used separate real Ollama 0.40.11 daemons and the same installed models. Prior main sent one chat and refused turn two on an 8192-token fallback while the daemon served 32768. The candidate saved answers 16, 17 and 18 with windows 8192, 32768 and 32768. The third turn made no catalog call. A separate process retained its configured 24576-token window, and its offline Auto preview made zero requests. Rechecked 2208 frozen source inputs, format, changelog and the existing 636-site blocking-call budget. Refs #5848.
Reuse the ticketed startup probe for an explicit retry on the selected unresolved keyless local route. Recheck route and picker state on completion and keep explicit credentialed switches on their existing path.
On Ubuntu 24.04 ARM64, the prior terminal showed no composer echo during a 1,250 ms observation of a held catalog response. The candidate displayed the marker in 45 ms, kept the model unresolved on failure and accepted qwen3:4b on retry. An interacted picker retained its selection across a late success. Both binaries passed the authenticated catalog control. Rechecked 2,208 frozen source inputs, format, changelog and existing blocking/dead-code budgets. Refs #5848 and #7116.
Record the original #7184 follow-up from Lstarsky0. Matched native renderer observations at 80 and 100 columns covered two- and four-space indents, an isolated styled space, link hitboxes and unchanged narrow wrapping. All 216 existing TUI-kit cases passed on the submitted renderer. The contributor check found all 12 people across three surfaces. Version checks covered 39 linked references, the TUI changelog is synchronized and the generated contributor bytes match the authored source.
Credit Sh1Zuku's original PR #7183 and record the bare model's context and output limits. On the merged asset the offline catalog seed rendered exactly, matching the eight-spelling resolver input. Both owning cases passed on the original contributor Linux job. Verified all 12 contributors on three surfaces, 39 release-note references, workspace versions, web facts and the terminal changelog. The generated contributor document matches its authored bytes and digest.
CW-7640. The old fixture presented only a last model name, then expected that unverified name to change the budget. Main reproduced the same failure as contributor #7183. The existing case now checks the fallback for a partial receipt and the exact model and million-token window for a matched live receipt under a sealed home. The compaction case, the model-budget update and the Auto inspector case all passed in the native Cargo run. Runtime route validation is unchanged.
CW-7701, CW-7640. Move the existing read-only ambient-home tripwire back to the test binary containing its 11 terminal cases, including the renamed durable rewind. Report the extracted Engine as the owner of the relay template and use its base-prompt provenance in the manifest fixture. Refresh the three source-backed diagnostics baselines, public reference and generated docs. Regenerating the docs also synchronized the existing provider and contributor sources.
The deliberate context/compaction line accounts for all 18 additions across six existing turn-event fixtures. Stripping only those additions reproduces their previous bytes. The metadata block measured 396 bytes on native macOS. Its new 436-byte ceiling preserves the existing 10% tolerance. The 19 native TUI checks passed, including the sentinel and all six scripted turns, along with 22 Engine checks for manifests and the size ceiling. The ordinary incremental build reached its SSD guard after producing the TUI test executable. A serial Cargo run with incremental caching disabled for the test executables finished. Formatting, version, changelog, docs, contributor credit, dead-code and blocking-call checks passed.
Inline code opens with a run of backticks and closes at the next run of
the same length, which is how it carries a backtick: `` `Hello, ${name}` ``
is a JavaScript template literal. The renderer closed every span at the
next single backtick, so that one showed as Hello, ${name} without its
backticks, and `` ` `` turned the text after it into code up to the
next backtick.
Close a span at the next run as long as its opener, drop one space from
each end when both are there and the span isn't only spaces, and print
an opener that never closes as text. /receipts already writes a command
holding a backtick this way, through code_span.
Signed-off-by: Lstarsky0 <[email protected]>
CW-7640. A model switch from Sonnet to Haiku left the passive request manifest hashing generic enabled text while dispatch sent the 134,976-token auto-compaction threshold. Resolve the window and output cap in Engine preview and pass the validated route’s compaction configuration through its existing turn metadata path. Live dispatch retains its installed-route checks.
Five Ubuntu ARM64 terminal cases compared the complete preview hash with a captured loopback HTTP request. Both protocol controls and switches with default, custom 90,000-token and disabled compaction matched. Each made zero requests during preview and one after submit. The old switch case mismatched on macOS and Ubuntu. All 36 existing Engine preview cases passed in separate native macOS test processes, including all nine preview failures from the earlier Linux job. The dedicated native test executable reused locked dependencies with incremental output and debug symbols disabled after the normal build reached its storage guard. The version, changelog, contributor-credit, formatting, blocking-call and dead-code checks passed. The original Windows compaction report remains open.
Review feedback on the previous commits found two statements that claimed more
than the data supports.
- The test comment said a caller "cannot get two different windows from two
spellings". That holds for the two spellings the test compares, but the
`deepseek-ai/deepseek-v4.1-flash` twin is curated at 1048576 with no output
ceiling, so the absolute form is false. The comment now names that twin and
calls reconciling it a separate data question.
- The row's `source` said the bare spelling "resolves to the same facts". It
mirrors context, output, modalities and reasoning, but not `tool_call` or
`attachment`, and `family` differs from the canonical row. The string now
lists exactly what was carried over.
No behaviour change: values are unchanged and `python3
scripts/catalog_models_dev.py seed render --check` still reproduces the
committed bundle byte-for-byte apart from the OS path separator in
`_meta.generated_by`.
`deepseek-v4.1-flash` sat in `deepseek_v4_output_caps_require_exact_catalog_metadata`
among the spellings that must NOT resolve, because it had no model-only row and
took its ceiling from a provider offering instead. With the curated row added in
the previous commit the exact lookup succeeds, so the assertion moves to the
resolved list next to `deepseek-v4-flash` and `deepseek-v4-pro`.
The near-miss spellings stay unresolved: `deepseek-v4.1-flash-expires-on-0910`,
`deepseek-v4.1-pro`, `vendor/deepseek-v4.1-flash` and `deepseek-v4-flash-vendor`
still assert `None` on both lookups, which is what the test is actually about —
an exact row documents one identifier, never a family.
Gates: `cargo test -p codewhale-models` 108 passed. `cargo clippy -p
codewhale-models --all-targets -- -D warnings` clean. `cargo test -p
codewhale-config` and `-p codewhale-agent` still pass.
A custom `openai-compatible` endpoint names the model the way the endpoint
does — `model = "deepseek-v4.1-flash"`, no vendor prefix. The curated table
had rows for `deepseek-ai/deepseek-v4.1-flash` and a canonical Models.dev row
under `deepseek/deepseek-v4.1-flash`, but no row under the bare spelling, so
the lookup matched nothing and the route fell through the whole resolution
ladder to the static 128K window with an unknown output ceiling. The model
itself is catalogued as 1M context / 384K output.
Add the bare row, taking the facts from the canonical Models.dev row
(`context 1000000`, `output 384000`, reasoning, text+image). This follows the
existing pattern: `deepseek-v4-pro`, `deepseek-v4-flash` and `deepseek-flash`
already carry a bare row next to their namespaced spellings.
An earlier revision of this branch instead taught `intrinsic_model_in` to
adopt a namespaced row when exactly one carried the bare tail. That was
dropped: `deepseek-v4.1-flash` is already claimed by the `opencode-go` and
`opencode-zen` transport rows, so the shared-spelling rule stated by #6032
would have kept it unknown, and the fallback only appeared to work because
its guard did not read the transport table. Curating the row keeps the lookup
exact and leaves the resolver alone.
Gates: `cargo fmt --all -- --check` clean. `cargo test -p codewhale-config`
795 passed, 0 failed, 1 ignored (1 added). `cargo clippy -p codewhale-config
--all-targets -- -D warnings` clean. The new test asserts on the bundled
catalogue, not a fixture, and fails without the row.
Bring in the shared Engine move repairs for the provider registry and Windows test import. The submitted Markdown renderer is byte-identical. Its 216 existing TUI-kit checks and renderer observations cover the contributor change. The release still has separate preview, conformance and platform work.
Refs #7182
CW-7701. The Engine move left session_files imported by the Windows test module although only its Unix permission probe calls it. Reference the function inside that probe. Using the actual doctor test module, production formatter and real Windows dependency metadata, rustc --test --emit=metadata -Dwarnings on x86_64-pc-windows-gnu reproduced the exact unused import before the edit and passed after. The existing macOS permission probe passed on the prior build. Formatting passed. A complete Windows test-target cross-check stopped in the unrelated simdutf C++ dependency before checking the TUI. Native Windows remains a final qualification.
The documented local registry checker stopped with FileNotFoundError after the Engine extraction. Inspect readiness in the Engine and retain the TUI doctor source for presentation. The registry passed on current main and the config example retained all eight root keys. Removing each guard independently in memory produced its own expected failure. Refs CW-7701.
Confirm the idle Engine snapshot and selected prompt occurrence before rewinding. The picker now shares the acknowledged rewind, completed checkpoint, flush and Work projection used by /undo. Keep files in place and return the chosen prompt to the composer. Refuse an ambiguous boundary or a stale conversation.
CW-7418 / #6970. A five-turn Ubuntu ARM64 terminal previously reopened dropped turns after SIGKILL. An intermediate candidate also retained the selected turn when tool and generated context messages shifted the raw depth.
On the final frozen Rust sources four terminal journeys passed. A unique selection and both positions of an identical prompt survived SIGKILL with the expected A/B or A-D history on the next actual request. The journal retained the original entries and every workspace file kept its bytes. A forced save failure surfaced a visible error and made no request. Ordinary file-plus-conversation undo and picker cancellation passed on the preceding build. Cargo fmt, the blocking and dead-code budgets, version consistency and the embedded changelog passed.
The Runtime API's multi-turn file restore has separate Ubuntu evidence. Combined file-range progress in the terminal and native Windows package acceptance remain open.
Correct two unreleased notes against de99f0530a and 5fd52e87c8. Keep the original signal-handling credit and distinguish a periodic durable checkpoint from an immediate hard kill. The embedded changelog matches the generated 15-release window. Both web derivations, 12-contributor attribution and the version consistency gate passed.
CW-7553 and #7095. The recovery interval wrote only fully committed API messages, so a SIGKILL after the checkpoint advanced still lost the visible answer. For the active turn with the matching runtime ID and a user-message tail, append streaming assistant prose to the checkpoint journal copy using the existing interrupted-assistant role. The live journal stays with the Engine. Its normal completion replaces the checkpoint. Suppressed stream events, stale turns and completed messages cannot produce an extra tail.
On real Ubuntu ARM64 the baseline lost the visible reply immediately and after a 43.44-second checkpoint advance. The candidate recovered it after the checkpoint, resumed without a provider call and sent exactly one marked copy on explicit follow-up. A normally finished stream reopened as one completed message. SIGTERM retained the partial, /copy retained the completed answer, and the immediate SIGKILL still had only previously durable content. Four recovery journeys and five adjacent Auto-window journeys passed on afb29f01be10. One code block changed only by rustfmt after the binary freeze. Original Deepin and native Windows are separate qualifications.
CW-7640 and #7133. Keep the plan and the completed turn on the same resolved wire model. Store the matched provider identity, model, token window and provenance. A session projection carries the Auto selector, so only admitted and completed routes establish the actual model. Show a restored receipt separately from the prospective budget. Older, unknown and zero historical windows remain readable.
The baseline discarded the last window on restart. On the frozen Ubuntu ARM64 CLI five fresh-process journeys passed for provider-wide and exact-model overrides and for absent, unknown and zero historical metadata. The saved 135791-token window stayed historical. The next real turn used 75319 and resume issued no model request. The existing round-trip assertion checked all 53 released provider representations on the preceding wire-identical serde implementation. Workspace rustfmt and version gates passed. The separately reported Windows 40% compaction trigger remains open.
CW-6944. An array in a JSON object slot discarded version fields when serialized, and primitive mirror fields could be spread into a misleading release record. Refuse malformed matrix, mirror and cloud release shapes before staging any replacement. Continue rebuilding an absent derived release field on a valid matrix.
Four of eight process cases incorrectly reported success before this change. All eight pass now with byte-identical inputs on refusal. The earlier 13 consistency and repair cases still pass. The two public-surface and cloud-facts suites passed 41 checks together.
CW-6944 W02-09. Validate version, tag, date and URL on both checked-in release records and the version and URL of cloud facts. Permit the 24-hour grace only when all records describe the actual preceding published release. A retry also repairs a wrong URL or version on records with a current tag.
Eight of 13 local process cases failed before this repair. All 13 passed afterward including current data, mismatched URLs or versions, the legitimate post-publication grace, drift during grace, write repairs and GitHub failure. The two existing web suites passed 14/14. A read-only check against GitHub matched published v0.10.2 and the cloud-facts gate passed.
CW-7553. On a shutdown signal, cancel the admitted turn and bound the interactive snapshot and existing persistence flush by the shutdown deadline. Accept recovery messages only for the matching session, workspace and existing message prefix. Preserve the typed InterruptedAssistant tail through Escape and restoration without making it eligible for /copy.
Ubuntu ARM64 before: SIGTERM lost visible partial output on disk and resume. Four frozen source-built terminal runs passed: SIGTERM, SIGHUP, Escape followed by restart, and an explicit provider follow-up. Each resumed the marked reply exactly once, copied only the completed response and made no automatic request. The loopback peer observed one marked assistant copy on explicit follow-up. Signals completed in 80–171 ms. Formatting, blocking and dead-code budgets passed. The separate native libtest build stopped at the storage guard before running tests. Deepin, Windows and SIGKILL are outside this qualification.
CW-6943 R02-03. Stream release metadata within 8 MiB and read at most 4 KiB of error detail before sanitizing it. Successful assets retain their own 512 MiB ceiling. Parse failures report a location without echoing the input. Keep HTTP status retries, redirect host checks and signed asset verification on their existing paths. Publish the sanitizer before its new consumer.
81 focused existing release/CLI checks passed. All 26 real loopback cases passed for async discovery, CLI metadata, asset reads and malformed JSON. Before the change both declared and chunked 8 MiB-plus-one bodies were accepted, and both long 404s exposed a remote OSC sequence and tail. The 45-crate release order, formatting and diff checks passed. No candidate package or installation was published.
Since #7181 every table cell goes through the paragraph wrapper, which
drops spaces at the start and end of a row. A cell always starts a row,
so inline code that leads with spaces lost them, ` port: 8080` showed
as `port: 8080`, and a cell holding only a space, `` ` ` ``, came out
empty, even when the cell fit its column.
Lay out a cell that fits its column straight from its parsed spans, as
cells were laid out before #7181, recording its links the same way, and
wrap only the cells that don't fit.
Signed-off-by: Lstarsky0 <[email protected]>
Distinguish logical placeholders from concrete pending children. Apply each envelope through one slot update, claim an unknown child only into an available placeholder, and ignore late spawn notifications once the child is running or settled.
CW-6945 U05-04. A replay compiled from the owning source initially failed three of four ordering scenarios. All four passed after the fix, including exact logical-key priority. All 21 focused native mailbox, reconciliation, fanout, terminal-card and worker-view checks passed. No new tests or dependencies.
Route file-backed undo through the same acknowledged Engine rewind as conversation-only undo. Hold the TUI transcript until the Engine accepts the expected prefix. Distinguish a restored workspace from a refused conversation rewind and a failed durable save. Preserve the surviving journal branch and retry guards.
CW-7418, refs #6970. A fresh Ubuntu PTY reproduced eight stale active entries after file rollback. The repaired CLI restored an edit, removed a created file, preserved an unrelated file and retained eight recoverable journal entries with zero active messages across restart. Five portable command cases passed. The first native run passed 47 of 50 TUI checks. Three stale checks expected a soft sync or early truncation and passed after updating only those assertions. Native Windows and selected-range undo remain separate.
Both fresh channel packages completed npm installation and failed their first bridge-core import. Stage the adapter and shared package as siblings in the same atomic replacement. Preserve the previous service and npm paths and load the installed library before promotion.
CW-6950. ShellCheck and bash syntax passed. All 58 existing installed adapter cases passed on macOS and Ubuntu ARM64. Both real Node entry points reached credential validation without channel credentials. Broken core and lockfile upgrades left the prior package and neighboring channel intact. No service was deployed.
CW-6943 / #6554 X01-09. All five bridges use the same byte-bounded SSE reader. Handle LF, CRLF, CR, split UTF-8 and multiline data. Reject oversized, malformed or truncated frames and check thread identity and sequence before saving cursors or acting on approvals. A transport end preserves the observed turn. Feishu and WeCom retire only the matching completed turn, including when a newer one arrived during final delivery. Bound Runtime JSON and errors while preserving legacy empty and plain-text handling.
Reproduced zero events on CRLF, an accepted oversized frame and silent truncated EOF in the former reader. 168 existing channel/core checks passed, with one Windows-only skip. DingTalk syntax passed. 70,000 coalesced frames, adversarial framing, 11 response-bound cases and ten restart/readback channel cases passed. The frozen macOS Engine produced thread.started over authenticated loopback SSE and an observer with the wrong thread failed closed. No real channel credentials, delivery or inference used.
CW-6943 / #6554 W01-05. Read up to 32 recent digest keys from one bounded listing in groups of four. Reuse the signed-facts KV stream reader for strict UTF-8 and at most 128 KiB per record. Skip failed, mismatched, malformed or oversized entries independently. Verify reviewed language and cap sections and items before rendering. The weekly producer retains records for 90 days.
The former page issued 100 simultaneous text reads and one failed read blanked the archive. With 100 seeded records the renderer issued 32 streamed reads, at most four concurrent, canceled the oversized stream and rejected seven invalid record classes. Google Chrome displayed the 25 eligible articles in reviewed-language order. With one additional failed read, the other 24 still rendered. 80 existing signed-facts and review-state checks plus TypeScript passed. Verified locally.
CW-6945 / #6556 W02-05. A pending generator could put a discarded draft back on the review list. Save under the per-identity claim and retain the old decision until replacement writes succeed. Stage both weekly digest records together under that claim. Check the exact reviewed revision under the same claim before acting, including when a worker reads stale KV. Legacy records without a stored revision keep their reviewed-text check.
82 focused web checks and TypeScript passed. Replayed overlapping discard and failed reopen, then stale copies at both admin actions. Both stale actions returned 409 with zero outbound calls. Local workerd granted 1 of 8 concurrent claims and retained the revision after restart. Rechecked all 51 review-state cases after reusing the existing digest TTL. No hosted release validation or deployment.
An image, , went through the link arm with its `!` left in
the plain text before it, so it showed as "!alt". A linked image, the
badge form [](href), split at the image's `]`: the label
became "" printed as
text after it.
Show an image as its alt text linked to its source, and a linked image
as its alt text linked to the outer destination. The link arm now
takes an optional leading `!`, `![` ends a plain-text run, and the
label and destination parsing moved into split_markdown_link and
split_link_destination. Only one level is read, without recursion, so
a long run of `[![` in a reply can't grow the stack.
Signed-off-by: Lstarsky0 <[email protected]>
Keep the existing model, output limits, temperature, JSON and usage contracts for each caller. Read UTF-8 JSON through the existing 2 MiB outbound budget and cap provider error text at 4 KiB before diagnostics. Reject overflow while streaming even when the peer understates Content-Length.
CW-6943, audit W01-08, refs #6554. Five focused Vitest suites passed 71 cases. Twelve local checks through both entry points exercised successful usage, 429 diagnostics, declared and streamed overflow and cancellation of an understated body. Web TypeScript passed. No live inference or deployment.
Reuse the same durable lease as the six existing generation tasks. The manual facts route passes its binding and a missing lease refuses before fetching. Overlapping refreshes share one named writer through the existing hold period.
CW-6945, audit W02-05, refs #6556. Four focused Vitest suites passed 70 cases. A strengthened existing case confirmed two concurrent callers performed one revision fetch, wrote one drift log entry, and a missing binding made no request. Web TypeScript passed. Local fixtures only.
Keep Lstarsky0’s original change. Track the running display column without rescanning preceding spans. The existing wrapped-row case checks every one of 120 linked cells beside a wide character, using the inclusive column contract. The combined TUI-kit run passed 215 cases; the corrected assertion then passed separately. Ubuntu ARM64 PTY widths 80 and 120 each displayed both real OSC 8 targets.
Co-authored-by: Lstarsky0 <[email protected]>
Refs #7085 (CW-7532). The bundled descriptor marks this exact catalog base URL public. Clear configured headers and use anonymous auth only on the catalog client. Managed account, plugin, session-only credential, endpoint-scope and inferred key-health guards stay in place. The normal inference path still uses the configured credentials.
Existing descriptor and public-endpoint health checks passed. Clean macOS and Ubuntu ARM64 CLI installs each listed 127 models without credentials. The Linux set matched every ID from a simultaneous public endpoint read. An egress-denied macOS inference attempt failed at the missing configured key. No paid inference was attempted.
Refs #5848 and #7116 (CW-7584). The startup catalog can adopt an installed chat model on an explicitly selected, idle Ollama route. Re-selecting an already-resolved provider keeps the existing behavior. An unresolved local selection can retry discovery after its daemon starts.
Two affected existing checks passed. In an actual macOS terminal the startup-selected qwen3:4b answered 9+7 with 16 and persisted the turn. The separate candidate with a real late-starting private Ollama daemon stayed unknown for empty and embedding-only rosters, selected the chat tag when added, and restored it after restart. Desktop and Windows were not exercised.
Refs #7172 (CW-7651). Admit a continuation through the existing fresh-turn gates with typed runtime provenance. Keep the objective available again after a checkpoint, preserve explicit goal budgets and distinguish estimated conversation input from lifetime usage. An ordinary human message cannot suppress the authoritative objective.
14 goal-continuation and nine context-pressure cases passed. Actual macOS and Ubuntu ARM64 TUI requests to a scripted loopback provider sent a 1,238-byte objective on the first pass only. Repeated messages including turn metadata measured 3,076 and 3,055 bytes respectively. No provider billing was inferred. Combined validation also exposed an independent assertion in the separate #7181 overlay.
Preserve the original lockfile change and its registry checksum. Local frozen source verification: the descriptor contract passed and three actual CLI sessions loaded their nested TOML routes. The production CLI also built and ran on Ubuntu 24.04 ARM64. The combined run had an independent inclusive-column assertion failure in the separately overlaid #7181 test. No hosted release run.
Preserve the original lockfile change and its registry checksum. Local frozen source verification: HTML conversion and two clipboard Markdown cases passed. The production CLI also built and ran on Ubuntu 24.04 ARM64. The combined run had an independent inclusive-column assertion failure in the separately overlaid #7181 test. No hosted release run.
Preserve the original lockfile change and its registry checksum. Local frozen source verification: 14 session archive/export cases passed. The production CLI also built and ran on Ubuntu 24.04 ARM64. The combined run had an independent inclusive-column assertion failure in the separately overlaid #7181 test. No hosted release run.
Preserve the original lockfile change and its registry checksum. Local frozen source verification: 14 goal continuation cases passed and three real CLI sessions persisted distinct UUID v4 identifiers. The production CLI also built and ran on Ubuntu 24.04 ARM64. The combined run had an independent inclusive-column assertion failure in the separately overlaid #7181 test. No hosted release run.
For #5587, remove seven now-unfulfilled lint expectations, tighten the Windows-only test allocation and path import to their actual consumers, and read emitted web event types from their current Engine owner. Four open dependency PRs had recorded the same failures.
The frozen source built the macOS CLI and passed an all-features Engine cross-check for x86_64-pc-windows-gnu. The Rust workspace denies warnings. The existing web vocabulary case passed. Dead-code suppressions fell from 144 to 137 and the blocking-call budget passed. No native Windows execution.
Resolve release-note ordering and preserve both contributors. The code patch has the same stable patch ID as the qualified c80c16c14a candidate, including the Windows Console feature. Ordinary protected-branch checks and platform acceptance remain separate.
For #6555, persist each inbound claim before its side effect, join concurrent duplicates, and retain unfinished deliveries until the interruption notice succeeds. Keep live claims independent of the completed-message retention window. Reject Feishu messages without a stable ID before acting.
Verified the three bridge suites together: 112 passed and one skipped. A local SDK double through the shipping Feishu entry observed zero replayed effects, one action for concurrent deliveries, a retried failed notice, and retained in-flight claims under eviction. No live Feishu or Weixin call.
The locked registry archive matches its SHA-256 and declares Rust 1.88. The original head passed Linux, Windows, lint and Rust 1.89 checks. The Linux log records the signed identity signature, issuer, audience, expiry, nonce and account case passing. The consuming OAuth implementation is unchanged since that revision.
Gabriel-Degret reported #7179. A hosted output ceiling cannot establish the serving ceiling of a different endpoint. Preserve model generation defaults, declared route restrictions, context headroom and per-turn limits.
Verified 43 of 44 focused Engine, models and TUI checks on the first run. Updated the existing provenance assertion and its isolated rerun passed. Six real CLI requests over an operator-owned loopback transport sent 65536 automatic or 131072 explicit across three Qwen spellings on a declared 262144 window. The catalog spelling previously sent 32768. This does not qualify the reporter’s FreeBSD vLLM host. Formatting, version state and contributor credits passed.
Refs #7179
The shared turn driver already pins recorded host tools before building each request. Capture every definition on the wire. Remove the optional projection that classified deferred tools as missing binaries and could accept a missing fixture. All three existing cases passed on the populated developer PATH and the same three passed with an empty executable PATH. Thanks @SparkofSpike for the missing-fixture report. Refs #7174.
A table cell was wrapped as raw markdown and each wrapped row was then
parsed on its own, so a code span, bold run or link that crossed a row
break lost its pair: the backticks and asterisks showed, and a link
printed as its source with the URL split over two rows. Table rows
also carried no hyperlinks, even for a link that fit on one row.
Wrap each cell with render_line_with_links_tagged, the paragraph
wrapper, which parses first and records link columns, and shift those
columns to the cell's place in the row. wrap_cell_text and the
grapheme helper only it used are gone, along with the test that called
wrap_cell_text directly; the table tests still cover an overlong cell.
Signed-off-by: Lstarsky0 <[email protected]>
Keep the contributor's original commits and their Windows Console feature fix.
A stamp enriches an unshared user entry. Preserve authored payload, cache
markers, runtime ownership, conflicting stamps and preexisting branch history.
Use the same prefix semantics for saved outcomes and TUI refresh. Record
the strictly changed byte offset separately for token accounting.
42 focused tests passed. The macOS PTY sent the same text twice on purpose,
one Enter at a time. The loopback provider saw exactly two requests, the two
answers stayed on the active path, and it matched the persisted transcript.
The linked Windows incident has not been reproduced. Refs #7178.
Expose a compact named-step form on the already-eager execute_tools when one
ready server offers it. Use real same-server member schemas, current turn
visibility and declared bounds; name and count are checked after hooks.
Keep script and consent decisions direct, and route the batch through the
existing cancellable VM gate and bounded receipts. Exact-name regex discovery
now works. Bound ambient skill descriptions without changing authored
instructions or on-demand discovery.
Measure startup, turn setup, request preparation, gate planning and terminal
processing on demand. Compute stream executable identity in the background
from an opened file and keep its hash on the receipt.
CW-7649: the optimized candidate used the installed plugin and real DeepSeek.
Two ordinary goals chose the VM batch first and completed independently
verified background hover/drag: 4 calls/5 requests and 7 calls/8 requests.
A separate controlled recipe completed in one call/two requests. These are
device-specific observations, not a matched speedup or a one-call planning
guarantee. Initial attempts include a 14-call success and an incomplete run.
276 existing focused checks passed before the two-file bounds amendment.
27 affected checks passed on the measured source. Four existing provider-free
cases matched all 55 contract metrics; five-library Clippy, formatting,
documentation, version and blocking-budget checks passed. Both deferred and
pinned wire probes refused out-of-offer members, post-hook rewrites and a
three-step call to a two-step server before MCP dispatch. No hosted CI.
Raise the existing parent-surface ceiling by the same 256 bytes already measured and budgeted for #7177. The isolated canonical-main parent_agent_surface_stays_within_measured_ceiling check passed (1/1); all 1,947 frozen native inputs remained unchanged. Formatting and diff checks passed. No runtime change or release qualification.
The PR #7180 conflict was purely upstream churn: main moved
crates/tui/src/session_manager.rs to crates/engine/src/session_manager.rs and
bumped the windows crate to 0.62. Git's rename detection carried all three of
our edits onto the new paths, so the merge is content-identical to the
pre-merge branch plus upstream/main.
Verified: cargo check -p codewhale-runtime -p codewhale-engine --lib (13m07s,
no errors); cargo test -p codewhale-runtime --lib -- session_tree
(25 passed; 0 failed; 886 filtered out).
PR #7176 merged as be492e8767 with 66/66 focused Markdown checks and all-targets tui-kit Clippy passing on its exact approved source. Record the parser scope and unqualified Windows Terminal behavior. Existing contributor-credit guard passes across all three surfaces. Restore the 13 historical/index lines accidentally given the #7177 note by a27d80d96f9; keep #7176 and #7177 only in the Unreleased contributor entry, verified against the pre-edit source. No native inputs changed after passing checks.
Merge SparkofSpike's approved original PR. On canonical main 4b85ac2cc4 with the exact approved file applied: 66/66 existing markdown_render tests passed; tui-kit all-targets Clippy with -D warnings passed; cargo fmt and version checks passed. Frozen 1947 native source inputs were unchanged. This qualifies the parser fix locally; it does not qualify Windows Terminal font fallback or a release. No dependent PRs and no unresolved review threads; source branch deletion remains disabled. Contributor credit follows immediately.
Review follow-up on the rebranch rewrite. Behaviour is unchanged; every
point here is one the reviewer graded non-blocking but wrong.
- normalized_blocks returned a collected Vec, so the relaxed comparison
allocated twice per call while its doc claimed it allocated nothing. It
now yields a borrowing iterator and the doc says what the code does:
nothing is cloned, nothing is allocated.
- rebranch_active_messages still promised "the existing active branch stays
as evidence". The in-place refresh replaces that payload, so the doc now
states what is kept (entry id, created_at, parent link) and what is not
(the previous shape, which is not kept as a sibling).
- A missing id during the rewrite dropped a refresh silently. It now warns
with the entry id, so a divergence the caller cannot see becomes one it
can.
cargo test -p codewhale-runtime --lib -- session_tree
test result: ok. 25 passed; 0 failed; 0 ignored; 0 measured; 886 filtered out
Complete inherited public copy for provider routes, compaction recall, goal guidance, structured app metadata and llms.txt install/release facts. Move all English/Chinese FAQ answers into shared content so the page remains a single locale-aware renderer; update the existing source assertions to that owner.
Existing focused web checks: 39/40 initially passed after the move; the sole stale source-path assertion was corrected and passes on targeted replay (40 distinct passing checks). TypeScript and touched-file ESLint pass. Local browser preview opens all three new English FAQ answers and remains unchanged after the shared-content move. No deployment or published-release claim.
projects_to masked the <turn_meta> envelope for every entry kind. That
is only sound where the entry stores a message the engine may have
stamped. Compaction and BranchSummary project to text this crate
synthesizes, so a relaxed match could accept a live shape those entries
can never be rewritten into: the prefix would match, the refresh vector
would skip them for having no exact message, and the journal would keep a
payload the live transcript no longer has, with every later save forking
the same turn again.
Keep the mask on Message entries, which is the only kind the envelope
is written into, and compare the other kinds against their projection.
Use founder-selected DSH 0.2.1-rc.5 and Cordis 4.0.5. Refresh vendored
loader/include/group and pure hook helpers, npm pins, provenance and license
notices. Retain reviewed imports, non-writing composition and the explicitly
older preset adapter; Engine remains the only execution/prompt authority.
Compatibility v3 groups the same 17 injectable names into DSH, Cordis and
Codewhale sources. Boolean deferLoading is a no-op hint; non-booleans and when
are refused. A direct probe caught the inherited when acceptance gap and
verified its repair. The changed compatibility digest requires fresh Native
plugin review/trust; no live trust state was edited.
Local checks: typecheck/build pass; Node 413 pass, 7 skip; Bun 414 pass,
6 skip. Four direct admission/execution probes pass on each runtime. Twenty-five
vendored source/candidate hash pairs verified, plus unchanged built-in hashes
and the copied Rust compatibility asset. Native batch: 354/357 initially pass;
two goal goldens and stale notice headings repaired, then 3/3 affected checks
pass using the same binaries. Four metric tests pass; 55 budgets match exactly.
Engine/TUI/plugins clippy --tests passes, fmt and blocking/dead-code budgets
pass. Frozen native inputs remained unchanged. Six JS compiled-image cases
and two Rust compiled-image cases remain unqualified; native test-link unwind
warnings remain. No hosted CI, live-provider call or release qualification.
CW-7054. Receipts: artifacts/dsh-upgrade-takeover-20261031/REPORT.md.
Existing prompt metrics now use the recorded-environment guard with linux and /bin/bash on every platform. Correct the moved base-prompt provenance label. Re-record only the derived hashes, counts and bounded descriptions omitted from PR 7177; source descriptions and tool sets are unchanged.
Four existing metrics pass and all 55 ceilings match exactly. Prompt and event golden cases each pass on replay (the latter covers six scenarios). Engine/TUI/plugins clippy --tests passes with the existing allow-list; fmt passes. Evidence shares the frozen DSH source batch; no provider or Windows-runtime qualification is claimed.
Autosave compares the live transcript with the journal through
projects_to, which masks the engine-owned <turn_meta> envelope. The
comparison was right but the write-back was not: entries whose stored
form differs only by that envelope were neither refreshed nor reported,
so every autosave appended a fresh set of entries and orphaned the
assistant turn that belonged to the old ones.
Splitting the two jobs fixes it. projects_to keeps deciding identity,
rebranch_active_messages_stamped now refreshes a matched entry in place
when its exact message differs, and same_message_ignoring_turn_metadata
replaces same_content_ignoring_turn_metadata because ignoring the
envelope ignores exactly one part of the turn, not its content.
Also:
- is_complete_turn_metadata_block delegates to the same leading strip,
so a block that only starts and ends with the tags keeps its trailing
text instead of being dropped as metadata.
- normalized_blocks compares borrowed slices, one allocation per
comparison instead of two deep clones of the whole message.
- the refreshed filter requires an existing exact_message, so
Compaction and BranchSummary entries are no longer selected for a
rewrite they cannot receive, and the rewrite no longer promotes
User/Assistant into Message.
- journal_branch_to treats a turn-metadata-only difference as unchanged
so the strict-prefix walk stops at the same turn the journal does.
Correct the preceding merge message: its preparation script failed before applying credit or budget changes, so those are in this commit. Existing contributor-credit guard passes; all three surfaces credit SparkofSpike. Source-attributed schema delta is exactly 256 serialized bytes per tool surface. Native conformance and metric replay follow with the DSH batch.
Preserve the contributor PR and add its Unreleased entry and credit. Static review confirms all six golden description fields match source. Exact serialized descriptions add 256 bytes to each tool surface; update only those six schema ceilings, preserving counts and identities. Native conformance and metric replay follow with the DSH batch. No live-model qualification or hosted CI.
Clarify update_goal schema guidance for yielding while waiting for a user answer. Runtime semantics were already integrated; this PR changes two description strings and three conformance prompt goldens. Reviewed exact approved head 938b157a9f0c18891021d9d066b1f81051487cda; six golden fields match the source descriptions. No open dependent PRs or unresolved review threads. Full native conformance verification and exact schema-budget update are in the immediately following local batch.
A user message reaches the journal in two shapes: bare text, and text
carrying an engine `<turn_meta>` block. `update_session` matched the live
transcript against the persisted branch with relaxed equality, but
`rebranch_active_messages_stamped` only appended the relaxed suffix, so
every save forked a second copy of the same turn and the journal kept the
older shape.
Match the prefix on relaxed equality, then rewrite those entries in place
through `SessionEntryKind::replace_message`. Only messages past the prefix
are appended, so the entry count stays flat, the stored shape follows the
live transcript, and the `<turn_meta>` marker that decides runtime
ownership survives. `update_session` now delegates the whole branch to
`rebranch_active_messages` instead of hand-rolling branch_to plus skip.
Verified: cargo check -p codewhale-runtime, cargo check -p codewhale-tui
--lib, and cargo test -p codewhale-runtime --lib -- session_tree
(22 passed).
projects_to and the update_session common-prefix scan compared messages with structural equality. The same user message is persisted both bare and with a trailing <turn_meta> block, so one mismatch collapsed the shared prefix to zero: rebranch_active_messages_stamped cleared leaf_id, rebuilt the tree from the root, and left every earlier assistant entry on an orphaned branch. A session's active path could then hold 45 consecutive user messages and no assistant reply at all.
Compare content after stripping the turn-metadata block, keeping structural equality as the fast path.
Refs #7178
runtime_log.rs imports Win32::System::Console under cfg(windows), but the windows crate dependency never enabled that feature, so codewhale-runtime does not compile on Windows. CI is Linux-only, which is why this never surfaced.
The update_goal description and its status prose route a needed user
answer to yield instead of blocked. Nothing re-recorded the conformance
fixtures that pin the tool catalog, so they still carried the old text.
- prompt: tools_bytes 16349->16605 for the 13-tool catalog and
13849->14105 for the 11-tool one; tools_sha256 and prefix_sha256
recomputed from the new fixture text and cross-checked against the
pre-change values before writing.
- events: active_tool_catalog_sha256 d53e18df->84f64a5c and
payload_json_bytes 16349->16605 in all six fixtures. The digest is
recomputed independently as sha256 over name/description/canonical
input schema per tool, and it reproduces the recorded value exactly.
- events: update_goal's description now runs past the 512-character
capture bound, so it is recorded truncated at a true prefix.
`update_goal`'s description said "Mark blocked when progress requires user
input", which sends a question to the exit that needs an explicit `/goal
resume`. The continuation prompts added in #7175 name `yield` as the exit for
exactly that case, so the tool description was pointing the other way at the
cheapest moment to read it.
Blocked now means progress cannot continue at all. The yield trigger picks up
"you need the user's answer before the next pass can do anything useful",
matching the header wording.
The three prompt conformance goldens are hand-edited rather than re-recorded:
`CODEWHALE_CONFORMANCE_UPDATE=1` on a host with node or python writes that
host's probed tool definitions into a pin file that has never existed in the
repository (#7174). The description is byte-identical to `description()` in
all three.
StartReviewRequest is a RuntimeThreadManager request record, like
StartTurnRequest beside it; it lived in runtime_api/review.rs, the Axum
handler, so the engine-band manager depended on an app-server-band module.
It moves into runtime_threads/manager.rs; the handler and the chat relay
name it there. crate-map-coarse.toml assigns runtime_api/review to
app-server and the two units #7173 added (compaction_archive,
tools/context_archive) to the engine, so every top-level module of
crates/tui now sits wholly in one stage A crate.
Checks: crate_map.py check crate-map-coarse.toml: cycles=0 upward_edges=0
upward_refs=0 unassigned=0; dev-cargo check -p codewhale-tui --tests: ok, no
warnings; cargo test -p codewhale-tui --lib -- review: 528 passed;
runtime_chat_relay / start_review filters: 18 passed; fmt clean.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#7173 (merged as 8f8d4632b3, review fixes in 7d5464e3cb): changelog line
under Added with its bounds and stated limits (a /fork cannot read the
inherited archive; a bare secret absent from current configuration is not
masked), and the SparkofSpike credit entries extended. Web facts are
regenerated for the two new tools (79 -> 81) and the new features.toml row.
Checks: sync-changelog.sh; check-contributor-credit.py: 12 contributors,
all credited on all three surfaces; derive-facts + check-facts OK;
check-versions OK; cargo fmt --check clean.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Founder, 2026-10-31: Tideline is retired as a design direction (docs
only, keep code). Removes docs/design/TIDELINE_RATATUI_TRANSLATION.md
and docs/design/tideline-redesign.html, and drops the doc comment's
pointer to the deleted HTML. Copies are kept outside the repository.
tideline.rs and the settings shell code are unchanged.
Doc-comment and docs change only; no build or test affected.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review of #7173 (independent read-only review, Codex CLI gpt-6.1-sol, then
confirmed in source) found the archive could hand the model a credential
that `codewhale sessions scrub-secrets` was meant to remove:
- session_secret_scrub never visited <session>/artifacts/, where compaction
writes context-transfer-*.json. session_files now includes each session's
artifacts directory (transfer snapshots only), and scrub_files rewrites a
snapshot atomically without a session lock: compaction writes each one
once under a fresh name, and its stem is a checkpoint, not a session id,
so the existing lock path would have reported it clean and left it.
- Request-time masking sees only pages and snippets, where a page boundary
or the << >> marker can split a credential past the mask. compaction_archive
::load now masks each whole message with redact_model_bound_text (shapes
plus the configured secrets scrub-secrets uses) before search or paging.
- context_archive_search accepted an unbounded query that every snippet
repeats; queries are now limited to 200 characters.
- ACP registered both tools but its dispatch allowlist rejected them; the
allowlist now admits these two read-only tools.
Known limits, stated in compaction_archive's header: a bare secret absent
from current configuration is not masked, and a /fork cannot read the
archive its handoff note refers to.
Checks: codewhale-tui --lib filters conformance::, compaction,
session_secret_scrub, context_archive, tools::registry, hooks::executor,
tool_catalog: 440 passed, 0 failed, 1 ignored; tools::subagent::tests 601
passed, 1 failed (late_launch_permit_still_gets_the_full_work_budget, a
4s wall-clock test that made 0 model requests under the parallel run; it
passes alone, 1 passed); core::engine test_cases_10 and host_profile
included above; feature_registry 2 passed; check-blocking-calls-budget
578 sites within budget; cargo fmt --check clean. No new tests.
Refs #7173
Co-Authored-By: Claude Opus 5.5 <[email protected]>
feat(tui): let the model read the transcript compaction replaced
Maintainer additions in the merge, both forced by the two new model-visible
tools and approved by the founder (2026-10-31):
- The six event conformance goldens are re-recorded: only the frozen-prefix
hash in prefix_cache_change and the tool_request_snapshot catalog facts
change (21 lines), the one-time cached-prefix effect of adding tools.
- PARENT_SURFACE_BYTE_CEILING rises by the measured 751B (91,543B + the 13B
Linux margin = 91,556B).
The security and bounds fixes from review follow in the next commit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#7175 (@SparkofSpike, merged as 711075a941): goal continuation prompts
name the yield exit for a question only the user can answer. Changelog
line under Changed, with its stated limit (prompt steering, not
enforcement), and the existing SparkofSpike credit entries extended.
#7131 (@Felixkw12, merged as aafbf5f0ed): Opper as a bundled provider
row. Changelog line under Added; the contributor is new, so they join the
CHANGELOG Contributors block, docs/CONTRIBUTORS.md, UNRELEASED_CONTRIBUTORS
and .github/AUTHOR_MAP (their commit identity is [email protected]; the PR is
from the vendor's own engineer, and billing stays Unknown).
web/data/features.generated.json is regenerated: #7151 changed
docs/features.toml (HarmonyOS native row) without regenerating it.
Checks: sync-changelog.sh wrote crates/tui/CHANGELOG.md;
check-contributor-credit.py: 12 contributors since v0.10.2, all credited
on all three surfaces; derive-facts + check-facts OK; check-versions OK;
cargo fmt --check clean.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
feat(providers): add Opper as a bundled descriptor row
Maintainer conflict resolution: main had added Runcrate in the same places
since the PR's base. Both rows are kept in provider_descriptors.json,
PROVIDERS.md (EN and zh-Hans) and the unpriced coverage ledger (Opper is
recorded as the twelfth non-Baseten compatible default, routes sorted).
The bundled-descriptor count becomes 13 and the compatible-default count 13.
web/data/agent-docs.generated.json is regenerated with derive-facts.mjs from
the merged sources rather than hand-merged.
Checks on the merge: codewhale-config descriptor tests 14 passed;
pricing::default_coverage 1 passed; provider_picker 152 passed;
check-facts OK; cargo fmt --check clean. No live Opper call was made;
billing stays Unknown.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An impl-block scan across the stage A boundary found one split inherent
impl: core/protocol_parity.rs implements Event and Op, which the engine
band defines. Keeping the file with the engine keeps those impls legal.
crate_map.py check crate-map-coarse.toml: cycles=0 upward_edges=0
upward_refs=0 oversize=0 unassigned=0 duplicates=0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stage A of docs/design/COARSE_SPLIT.md (engine / app-server / tui) now
measures zero upward refs, so the engine band can move as one crate.
Checker (scripts/split/crate_map.py):
- PATH_RE matched `super` as a prefix of `supervisor`, so
`super::supervisor::X` resolved as `super::super` and was charged to the
crate root. Fixed with `super\b`.
- Grouped imports (`use crate::a::{b::C, d}`) are now expanded into one
path per leaf; before, only the prefix was counted. This exposed
task_manager -> RuntimeThreadManager and remote_control -> chat relay.
Band placement (crate-map-coarse*.toml): runtime_threads/manager and
tools/registry go with the engine, which constructs and drives them (as
Codex's ThreadManager lives in codex-core); remote_control with app-server;
doctor and doctor_fix with the TUI, where the doctor command lives.
Code:
- runtime_account_api_base and its validator move from runtime_api to
profile_constitution, the engine-side caller; runtime_api, its secrets
routes and its test call the new owner.
- client.rs names codewhale_runtime::config instead of the lib.rs re-export.
Checks:
- crate_map.py check crate-map-coarse.toml: cycles=0 upward_edges=0
upward_refs=0 oversize=0 unassigned=0 duplicates=0
- crate-map-coarse-split.toml: upward_edges=1 upward_refs=73
- crate-map.toml: upward_edges=120 upward_refs=411
- scripts/dev-cargo.sh check -p codewhale-tui --tests --locked: ok, 0 warnings
- cargo test -p codewhale-tui --lib -- account_api_base profile_constitution
secrets: 42 passed; -- runtime_account_api: 1 passed
- cargo fmt --all -- --check: clean
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A table cell holding the `*` keycap (U+002A U+FE0F U+20E3) rendered its row out
of place, and the row's trailing `|` landed outside the table. A keycap with a
digit base (`1` + U+FE0F + U+20E3) was fine, so only the `*` form broke.
Three places in `parse_inline_spans` cut a span in the middle of that cluster:
- The plain-text arm advances to the next inline marker. `*` is a marker but
does not open emphasis here (no closing `*` follows), so it stopped right
after the `*` and left the two zero-width marks in a span of their own.
- The backslash arm emitted the escaped character alone, so `\*` + marks split
the same way.
- The `*italic*` and `_italic_` arms accepted any `*`/`_` as a delimiter, so a
keycap `*` could open emphasis and eat the marks.
Each arm now works on grapheme boundaries: plain text steps to the end of the
grapheme that contains the marker offset, the escape arm carries the whole
cluster, and both emphasis arms require the delimiter to be a lone character.
This is the rule `ui_text::grapheme_display_width` already states for keycaps.
The regression test renders the table into a ratatui buffer and compares the
column of every `|`. That alone is not a useful assertion here: the split and
joined forms advance the buffer by the same number of columns, because the
zero-width marks are dropped either way. The test therefore also asserts that
the cell's symbol still contains U+20E3. Removing the plain-text fix turns that
assertion red with `the cell holds ["|", "*", "|", ...]`, which is the state the
reported bug shipped in.
Gates: `cargo fmt --all -- --check` clean. `cargo test -p codewhale-tui-kit`
216 passed, 0 failed, 1 ignored (3 added). `cargo clippy -p codewhale-tui-kit
--all-targets -- -D warnings` clean.
scripts/split/crate_map.py builds the crates/tui unit graph (module_graph +
count_lines strict production lines) and checks a unit->crate TOML for
unassigned/duplicate units, oversize crates, crate cycles and upward edges.
docs/design/crate-map.toml + CRATE_MAP.md: a 66-crate fine map from a
judge-panel workflow (4 architects iterating against the checker, 3 judges,
1 synthesizer), plus an independent read-only review (Codex CLI,
gpt-6.1-sol) listing 14 problems the checker cannot see. runtime-wire is
renamed app-server-protocol per the founder's App Server v2 decision.
docs/design/COARSE_SPLIT.md + crate-map-coarse*.toml: the founder's
2026-10-31 direction to make the top line of crates ~400-500k lines like
Codex (162 packages, core/tui ~463k) and Grok Build (104, up to 551k).
Stage A, engine / app-server / tui: 35 upward refs over 3 edges. Stage B
splits the engine band (225k + 69k production lines): 96 refs.
TUI_DECONSTRUCTION.md points its Target map at CRATE_MAP.md, applies the
rename, and corrects the billing/catalog size (11,091 strict production
lines; 24,352 was partition_report's file-level figure).
Checks run on f510a59d94: crate_map.py check reports
crate-map.toml: upward_edges=118 upward_refs=324 oversize=0 unassigned=0 duplicates=0
crate-map-coarse.toml: upward_edges=3 upward_refs=35 oversize=0 unassigned=0 duplicates=0
crate-map-coarse-split.toml: upward_edges=6 upward_refs=96 oversize=0 unassigned=0 duplicates=0
No Rust source changed; no cargo build run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#7168 (@gaord, merged as f36e46ddf9) shipped without a changelog line or
credit. Adds both, and regenerates the blocking-call budget for the merged
tree: external_credentials.rs 1 -> 2 (the credential-file open is a
synchronous helper; its std::fs sites are unchanged text but the counter
now sees one outside a blocking scope), remote_control.rs 7 -> 6 and
pet_watch/owner.rs 3 -> 2. Net 577 sites, one fewer than before.
Verified on origin/main 8202875251 + #7168 before merging: fmt clean;
clippy -D warnings (CI allow-list) clean on secrets, util, config, runtime,
memory, plugins, app-server and tui; cargo test on secrets, util, config,
runtime, memory, plugins and app-server 2,369 passed / 0 failed / 6
ignored; tui filters session_manager 137, control_socket 21,
remote_control 90, pet_watch 35, owner_bootstrap 7, acp_server 38,
runtime_chat_relay 18, github::report 9, all passing; dead-code, locale
and crate-boundary gates pass; contributor credit passes;
sync-changelog.sh reproduces crates/tui/CHANGELOG.md. Not verified: a real
HarmonyOS device or a Windows host (CI's Test (windows-latest) passed on
the PR head).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: CodeWhale Bot <[email protected]>
Review feedback: the trailing sentence in render_continuation_prompt was
wider than GOAL_CONTINUATION_PROMPT's ("needs an answer" vs "before the next
pass can do anything useful"), and being later in the same message it won.
Align the two, and swap "do not answer it yourself" for "do not invent an
answer" so deciding with a stated assumption stays allowed.
A continuation pass that needed an answer from the user asked in prose and
kept the goal Active: neither GOAL_CONTINUATION_PROMPT nor
render_continuation_prompt listed "I need the user's answer" among the exits,
and the trailing "Otherwise continue making progress" left self-answering as
the default. The next pass then started with the question still unanswered.
Widen the yield trigger to cover that case, and state on both surfaces that
asking without calling update_goal does not pause the goal.
No state-machine change: GoalPauseReason::Yielded already exists and the user's
reply already resumes the goal.
The "B billing" row said "not confirmed" and that the crate must not depend
on the runtime tier. Re-measured on 1b0e51005b with module_graph.load_graph
and partition_report --json:
- cost_status, pricing, route_billing, provider_catalog_live, provider_lake,
models_dev_live, codex_model_cache and reasoning_preference total 24,352
production lines inside the 59-module cycle.
- Their only production edges back into the cycle are 6 refs into `client`
(CodewhaleClient::for_catalog_refresh, client::ollama_catalog::load_model,
client::AvailableModel) and 1 into provider_readiness. cost_status alone
had 15 inbound modules and 3 outbound, which is what led here.
- 590 inbound production refs follow the move; tests reach out 176 times,
149 to test_support.
- All eight use codewhale_runtime::config, so the crate sits above
codewhale-runtime; the old constraint predates Config moving there.
It is not rename-only: catalog refresh must stay beside `client`, so
provider_lake and codex_model_cache split into a store and a refresh driver.
Open PRs that touch pricing/cost_status callers (session_manager.rs among
them) should merge first. Docs-only; lexicon check passes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: CodeWhale Bot <[email protected]>
Three @Lstarsky0 fixes merged as PRs (merges dc4870117b, 9986831e95,
b2db2c0467). #7171 carried its own changelog edit; #7169 and #7170 did not.
They join the existing @Lstarsky0 Markdown and footer-status entries rather
than adding new bullets, and the CONTRIBUTORS.md and Contributors section
lines name all three.
Verified before merging, on origin/main ecd256c904 with all three PRs merged
together locally: cargo fmt --check clean; cargo test -p codewhale-tui-kit
--lib 214 passed; cargo test -p codewhale-tui --lib (history:: markdown
stream_chunk) 186 passed; dead-code, blocking-calls and locale-parity gates
pass (run by hand, since CI's ratchet steps are advisory on fork PRs). No
test or fixture asserted on the removed "Stream chunk timeout set to" status.
After this edit: check-contributor-credit.py passes (11 contributors since
v0.10.2, all three surfaces) and scripts/sync-changelog.sh reproduces
crates/tui/CHANGELOG.md byte for byte.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: CodeWhale Bot <[email protected]>
`session.resumed` made `SessionRecovery`'s `repaired_call_count`,
`duplicate_result_count` and `orphan_result_count` live outside tests, so the
three `#[cfg_attr(not(test), expect(dead_code))]` attributes went with it. The
measured count is now 158 (allow 105, expect 53) against a ceiling of 162.
The gate may go down freely; lowering it locks the win in so a later change
cannot quietly re-add a suppression. Regenerated with `--update`.
Verified: `python3 scripts/check-dead-code-budget.py` reports
"158 suppressions (allow=105, expect=53), budget 158 (0 under)".
Signed-off-by: CodeWhale Bot <[email protected]>
The two archive tools add 2492 bytes and two tool names to the
model-visible catalog, so the pinned prompt goldens no longer matched.
Re-recorded with the host pins in force. system_sha256, system_bytes and
system_blocks are unchanged, so the system prompt itself is untouched;
only tools_sha256, tools_bytes, tool_count, tool_names and prefix_sha256
moved. Scanned the new goldens for host-specific text (powershell, pwsh,
user or drive paths, platform names) and found none.
Also drops the temporary conformance-refresh workflow.
Bold, italic and strikethrough spans pushed their inner text as one
plain run, so markup inside them reached the screen as written:
**`--force`** showed its backticks, and **[the guide](https://...)**
showed the brackets and the URL, with no hyperlink.
Parse the inner text with the emphasis style as its base, so code,
links, bare URLs and escapes inside it render as they do outside. Each
arm's inner text ends at the first closing delimiter of its own kind,
so a delimiter can't nest inside itself and the recursion stays a few
levels deep.
A note doubles its backslashes before it is rendered, but skipped any
line with `*` or `~~` because emphasis printed its text as written.
With escapes read inside emphasis, a note line like
**C:\Users\asus\.codewhale** would lose the backslash before the dot,
so the note escaper now skips only lines with inline code and table
rows. A line that mixes bold and a path, like
**Saved** to C:\Users\asus\.codewhale, keeps its backslash too.
Signed-off-by: Lstarsky0 <[email protected]>
#7117 merged with three blocking-call sites over budget:
crates/config/src/persistence.rs: thread_sleep 1 > budget 0
crates/runtime/src/settings.rs: thread_sleep 1 > budget 0
crates/runtime/src/settings.rs: std_fs 5 > budget 4
It merged because the CI step that runs this ratchet is
`continue-on-error` for pull requests from untrusted forks, so the failure
was advisory there and closed red on `main` instead — and a push to `main`
starts no workflow.
All three are genuinely synchronous. `atomic_write` is `pub fn atomic_write`
in `crates/config/src/persistence.rs:42`, and the settings one sits in a
Windows-only replace helper; both are reached from ordinary settings saves,
not from a runtime worker. The sleeps are the bounded retry that lets a write
succeed while another Codewhale instance holds the destination open for a
read, which is the whole point of the branch. `spawn_blocking` has no runtime
to yield to in a synchronous function, so this is the ratchet's documented
debt path.
Regenerated with `--update`: 578 sites across 170 files.
Verified: `python3 scripts/check-blocking-calls-budget.py` reports
"578 sites across 170 files, within budget".
Signed-off-by: CodeWhale Bot <[email protected]>
#7142 asked for a dated, attributable record of what happens to a
conversation, and #7095 is a report of history that vanished with nothing
written down. A resume is where a crash-repaired history becomes durable, and
`SessionRecovery` already carries the evidence: whether the document changed,
and how many calls were repaired, results deduplicated and results left
orphaned.
`resume_session` now writes one `session.resumed` audit event carrying those
counts plus the message count. It records counts and the session id, never
message bodies, so it stays a sensitive-safe event like
`session.window.rerooted`. The event covers every resume that goes through
this owner — `codewhale resume`, ACP's prefix resume and the Runtime API's
thread resume all land here.
This also makes the three counters live outside tests, so their
`expect(dead_code)` attributes are removed rather than left unfulfilled under
the workspace's `-D warnings`.
Verified on the debug build in an isolated `CODEWHALE_HOME` with a real saved
session (workspace retargeted to the fixture): the audit log gained
{"event":"session.resumed","details":{"session_id":"0061a59b-…",
"repaired":false,"repaired_call_count":0,"duplicate_result_count":0,
"orphan_result_count":0,"messages":90}}
`cargo check -p codewhale-tui --lib` clean, `cargo fmt --all --check` clean.
Not verified: a resume that actually repairs a damaged document, which needs a
corrupt fixture; the payload shape is proven, the non-zero counts are not.
Signed-off-by: CodeWhale Bot <[email protected]>
`rewrite_settings_in_place` is the last-resort `settings.toml` delivery when
every replacing call is refused, which only happens on Windows. The helper
itself was not gated, so on macOS and Linux it had no caller and the crate
denies dead code:
error: function `rewrite_settings_in_place` is never used
--> crates/runtime/src/settings.rs:2462:4
It now carries the same `#[cfg(windows)]` as its only caller. Nothing about the
Windows behaviour changes.
Verified: `cargo check -p codewhale-config -p codewhale-runtime -p
codewhale-tui` clean; `cargo test -p codewhale-runtime --lib settings` 100
passed / 1 ignored; `cargo test -p codewhale-config --lib persistence` 31
passed; `cargo fmt --all --check` clean.
Signed-off-by: CodeWhale Bot <[email protected]>
Conflict resolution for #7117 after rebasing onto current `main`. The branch's
five commits each rewrote the same two changelog files from an older base, so
this lands the three entries that are genuinely new to `main` under `### Fixed`
and leaves both files otherwise byte-identical to `main`:
- transcript notes no longer lose a backslash to CommonMark escaping, which
broke the one copyable path in a Windows setup failure;
- setup state survives a second running instance whose read handle makes
Windows refuse the replacing rename;
- `doctor` no longer reports a user's own `.tmp`-named scratch file as a
half-applied setup.
Entries for the branch's other two changes were already in `main` and are not
duplicated. The contributor's five commits keep their authorship.
Verified: `diff` against `origin/main` for both files shows three added lines
and no deletions; no conflict markers remain.
Signed-off-by: CodeWhale Bot <[email protected]>
`/config pet_mode --save` answered "无法删除要被替换的文件。(os error 1175)", and the
same refusal hit every other settings toggle — "无法保存确认。脱敏仍然开启。".
A retry does not help: on this machine the home directory refuses every
directory-entry rename for as long as it is held, and the refusal does not
expire. Measured directly, with the same file and the same ACLs:
D:\CodeWhaleData ReplaceFileW 1175, MoveFileExW 32, rename 失败
D:\ same
D:\CodeWhaleData\<any sub> OK
C:\Users\asus\Documents OK
`DeleteFileW` succeeds in the refused directories, and rewriting the file in
place succeeds, so the bytes can be delivered without a rename. Both writers now
do that as a last resort, because a setting that never saves is worse than one
whose write is briefly non-atomic.
- `crates/config/src/persistence.rs`: `atomic_write` keeps the retry over the
contention codes, then calls `rewrite_in_place` when the refusal is one of
them.
- `crates/runtime/src/settings.rs`: `atomically_replace_settings_file` keeps the
`ReplaceFileW` retry, then calls `rewrite_settings_in_place`.
The fallback is narrow on purpose. Only the contention codes fall back — 5, 32,
33, 1175 — so a full disk or a real permission bit still reports its own error
instead of being retried into an in-place attempt that would fail the same way.
`ReplaceFileW`'s 1176 and 1177 stay out of the list: they can leave the
replacement consumed or altered, which a retry cannot undo.
The in-place write truncates and rewrites, so an interruption can leave a
partial file; `Settings::load_persisted` already tolerates a corrupt document by
falling back to defaults with `load_error` set, which bounds the damage.
Verified on Windows against the affected directory:
- Before: `config set pet_mode false` -> exit 1, `settings.toml` mtime unchanged.
- After: `config set pet_mode false` -> `set pet_mode in
C:\Users\asus\.codewhale\settings.toml`, mtime advanced, value read back.
- `cargo test -p codewhale-config --lib -- persistence` -> 30 passed, including
the new `an_in_place_rewrite_replaces_a_longer_document`, which proves the
rewrite drops a longer document's tail.
`/config pet_mode --save` answered "无法删除要被替换的文件。(os error 1175)":
`ReplaceFileW` must delete the destination before it can publish the
replacement, and a handle that holds that destination without
`FILE_SHARE_DELETE` refuses the deletion. The previous fix covered
`atomic_write`'s `MoveFileExW` and its three contention codes; `settings.toml`
goes through `ReplaceFileW`, which reports its own fourth.
Retry the same way, over one allow-list now shared by both calls:
- 5 `ERROR_ACCESS_DENIED`
- 32 `ERROR_SHARING_VIOLATION`
- 33 `ERROR_LOCK_VIOLATION`
- 1175 `ERROR_UNABLE_TO_REMOVE_REPLACED`
Six attempts, `n * 50ms`, 750ms in total — up from 225ms, which a reviewer
called short for the scanned or slow filesystem this exists for.
Why 1175 belongs on the list and 1176/1177 do not: `ReplaceFileW` documents
1175 as "the destination file is not modified, and the replacement file still
exists under its original name", so a retry starts from the state it just read.
1176 and 1177 can leave the replacement renamed away, or holding the old file's
streams with its contents no longer guaranteed, so those surface at once.
Also: `SetFileAttributesW` failing is about the *source*, not a held
destination, so it is never retried; and the failure path still restores
`FILE_ATTRIBUTE_TEMPORARY` before each retry, so `TempPath` keeps its cleanup
behavior if the last attempt loses too.
Verified locally on Windows:
- `cargo test -p codewhale-runtime --lib -- only_a_held_destination` -> 1 passed
- `cargo test -p codewhale-config --lib -- only_a_contended` -> 1 passed
What I could reproduce of the mechanism: a destination held by an ordinary
reader makes `ReplaceFileW` fail with 32, and the same call succeeds once the
handle is closed, which is what the retry buys. A handle opened *with*
`FILE_SHARE_DELETE` plus a read-only mapped view did not reproduce 1175 — the
replace simply succeeded — so 1175's presence on this list rests on the
documented post-failure state rather than on a local repro.
At 120 columns the wrapper soft-wraps inside the path, so the string the case
asserts on is split across two lines and the assertion fails on the wrapper
rather than on the escape. The case is about the backslash, not the wrapper:
400 columns puts the whole note on one line.
A note is machine-generated and names paths, but it went through the markdown
renderer, which honours CommonMark §2.4: a backslash before ASCII punctuation
is an escape, so `\.` yields `.`. A note naming
`C:\Users\asus\.codewhale\setup_state.json` therefore printed as
`C:\Users\asus.codewhale\setup_state.json` — a directory that does not exist,
in the one line a user would copy to find the real one.
The renderer is right; a producer that wants a literal backslash escapes it.
The escape happens where notes are rendered, so every note is covered:
`escape_note_backslashes` doubles a bare backslash in note prose.
Three regions are left exactly as written, because the renderer already copies
them verbatim and doubling inside one would *print* the extra backslash:
- a fenced block, closed only by a run at least as long as its opener;
- a line carrying a delimiter the renderer treats as a passthrough (backtick,
`*`, `~~`) — inline code, bold, italic and strikethrough are the shapes a
note's third-party text (MCP guidance, extension prompts) brings with it;
- a table row.
`_`-delimited emphasis is deliberately not a passthrough trigger: `_` is
everywhere in identifiers (`setup_state.json`), and a false positive would
leave the paths this pass exists to protect untouched.
Five cases pinned: a plain Windows path survives intact; a fenced path is not
doubled; a fence shorter than its opener does not close it; inline code, bold,
strikethrough and a table row are passed through; and a prose path that also
carries an underscore is still doubled.
Two Codewhale windows share one home and both rewrite `setup_state.json` when
the provider or model changes. `atomic_write` staged the write correctly and
then failed at the replacing rename, because the loser of that race finds the
destination held open for a read and Windows refuses the rename outright while
the reader's handle lives (no `FILE_SHARE_DELETE`). The user saw
"Setup provider/model state was not saved" for a write that was momentary.
Retry only that condition, briefly: six attempts, `n * 15ms`, giving up after
75ms. A missing directory or a full disk still surfaces at once. On Windows the
retryable codes are 5 (access denied), 32 (sharing violation) and 33 (lock
violation); elsewhere it is `PermissionDenied`.
Two related defects found in the same report:
- The message used `{err}`, which prints only the outermost context and drops
the cause, so the path was the only thing left to look at. It now uses
`{err:#}`.
- `doctor`'s `stale_setup_temp_files` check matched any name starting with
`.tmp`, so a user's own scratch logs (`.tmp-build-release.log`) made a clean
home report as half-applied. It now matches what a staged write leaves behind
when it never lands: `.tmpXXXXXX` with no extension.
Gating the lease predicate test to Unix left `LeaseUnavailability::new` with no
caller on Windows, and the crate denies dead code:
error: associated function `new` is never used
--> crates\runtime\src\workspace_lease.rs:148:8
Its only caller builds `LeaseAttempt::Unsupported` on the "this mount has no
advisory locks" branch, which is `#[cfg(unix)]` because Windows takes real
`LockFileEx` locks. The constructor now carries the same gate. The type and its
`Display` stay available on every target, so callers that only match on an
`Unsupported` lease are unaffected.
Verified: `cargo clippy -p codewhale-runtime --lib -- -D warnings` clean with the
CI allow-list; `cargo fmt --all --check` clean. Not verified locally: the Windows
compile, which needs a Windows toolchain for `ring`; CI's
`Test (windows-latest)` is the check.
Signed-off-by: CodeWhale Bot <[email protected]>
`only_a_held_lock_is_contention_and_only_a_lockless_mount_is_unsupported`
asserts Unix errno semantics through `libc`, which this crate takes only under
`[target.'cfg(unix)'.dependencies]`:
error[E0433]: cannot find module or crate `libc` in this scope
error[E0425]: cannot find function `advisory_locking_unavailable`
--> crates\runtime\src\workspace_lease.rs:733:36
The predicate it calls is `#[cfg(unix)]` in `codewhale-util`, and production only
reaches it inside a `#[cfg(unix)] if` because Windows takes real `LockFileEx`
locks through `fd-lock`, so a failed lock there is contention rather than a
mount without locks. The test is Unix-only for the same reason, so it now says
so. Nothing about the Unix behaviour changes.
Verified: `cargo check -p codewhale-runtime -p codewhale-util` clean;
`cargo fmt --all --check` clean. Not verified locally: the Windows test build,
which needs a Windows toolchain for `ring`; CI's `Test (windows-latest)` is the
check.
Signed-off-by: CodeWhale Bot <[email protected]>
`check-blocking-calls-budget.py` failed on `main`:
crates/cli/src/update/uninstall.rs: std_fs sites 11 > budget 6
The five new sites are the shell-integration half of uninstall: reading a
profile, staging its replacement, adopting the profile's mode, and unlinking
the completion files. They are genuinely synchronous.
`update::uninstall::run` is reached from `fn run() -> Result<()>`
(`crates/cli/src/lib.rs:2353`), the synchronous CLI entry point, before any
Tokio runtime exists — the same reason the existing six sites in this file are
unbudgeted. `spawn_blocking` has no runtime to yield to here, and `tokio::fs`
is not available to a synchronous function, so the ratchet's documented
alternative applies: raise the file's budget and say why. None of the five
sites runs on a runtime worker.
Regenerated with `--update`; the diff touches only this one entry (6 -> 11).
`path_canonicalize` stays at 8.
Verified: `python3 scripts/check-blocking-calls-budget.py` reports
"575 sites across 169 files, within budget".
Signed-off-by: CodeWhale Bot <[email protected]>
Two call sites in the Windows owner-only test still used the old two-argument
`validate_owned_file_handle`, which now takes the private guard on every
platform:
error[E0061]: this function takes 3 arguments but 2 arguments were supplied
--> crates\config\src\xai_credentials.rs:930:9
--> crates\config\src\xai_credentials.rs:941:9
Both pass `store.private.guard()`, the same value the production path at
`xai_credentials.rs:257` already passes, so on Windows the guard is
`PrivateGuard::Required` and the ownership proof is unchanged.
Verified: `cargo clippy -p codewhale-config --lib -- -D warnings` clean on the
host with the CI allow-list; `cargo fmt --all --check` clean. Not verified
locally: the Windows compile itself, which needs a Windows toolchain for
`ring`; CI's `Test (windows-latest)` is the check.
Signed-off-by: CodeWhale Bot <[email protected]>
`stage_profile` ended its `#[cfg(unix)]` block with `return`, which is a
`needless_return` once that block is the last thing in the function on Unix —
the block's value is already the return value, and the `#[cfg(not(unix))]`
arm removes itself. The workspace lints with `-D warnings`, so the release
Lint gate failed:
error: unneeded `return` statement
--> crates/cli/src/update/uninstall.rs:655:9
This was not caught when it landed. Pushes to `main` deliberately start no
GitHub workflow, and GitHub lints a pull request's *merge* commit, so the
error surfaced only on an unrelated contributor PR whose CI checked out
`main` plus its own changes.
No behavior change: on Unix the block still yields the reopened staged file,
and on other targets `Ok(staged)` is still the tail.
Verified: `cargo clippy -p codewhale-cli --lib -- -D warnings
-A clippy::uninlined_format_args -A clippy::too_many_arguments
-A clippy::unnecessary_map_or` clean (the exact allow-list the CI Lint job
uses); `cargo test -p codewhale-cli --lib update::` 109 passed / 0 failed;
`cargo fmt --all --check` clean.
Signed-off-by: CodeWhale Bot <[email protected]>
`codewhale exec` with no cloud key adopts a local Ollama model and printed
`... nothing was saved.` That line means the adoption did not write a config
change, but read on its own it says the run was not saved — and in the same
output the run does save a session, which `codewhale sessions list` then shows.
A message that contradicts the session id printed beside it is worse than no
message, especially in a release whose open bugs include lost conversation
history.
Both sites now say `no configuration was changed`, which is exactly what the
adoption path does: `adopt_live_local_ollama_for_run(.., false)` mutates the
in-memory config for the run and persists nothing. `codewhale doctor --probe-local`
carries the same correction.
The unrelated `nothing was saved` in `crates/runtime/src/config_keys.rs` is
correct there — a rejected `config set` really does persist nothing — and is
left alone, including its assertion.
Verified: `cargo fmt --all --check` clean; `cargo check -p codewhale-tui --lib`
clean. Observed before the change on the installed binary: an interrupted
`codewhale exec` printed `nothing was saved` and still listed session
`5a37341f` as resumable. Not verified after: the reworded line on a rebuilt
binary, which lands with the next release build.
Signed-off-by: CodeWhale Bot <[email protected]>
The HarmonyOS change added Windows arms but left the Unix originals
ungated, so `codewhale-config` did not build for Windows, and one call site
still passed the pre-change arity:
error[E0592]: duplicate definitions with name `open_trusting_mount`
--> crates/config/src/private_directory.rs:95:5
error[E0592]: duplicate definitions with name `admit_trusting_mount`
error[E0592]: duplicate definitions with name `inspect_trusting_mount`
error[E0599]: no associated function `with_mount_tolerance`
error[E0061]: this function takes 2 arguments but 3 were supplied
--> crates/config/src/xai_credentials.rs:257:13
`open_trusting_mount`, `admit_trusting_mount` and `inspect_trusting_mount` now
carry `#[cfg(unix)]` on the bodies that call `with_mount_tolerance`, leaving the
existing `#[cfg(not(unix))]` arms as the only definitions there.
`validate_owned_file_handle` keeps its three-argument shape on every platform:
Windows and the remaining fallback take the guard and ignore it, because the
Windows ownership guard is the security descriptor rather than the mount
footing. The Windows-only callers that never had a guard to pass use a
two-argument internal helper instead.
Also replaces `u32::from(metadata.st_mode)` with a cast behind a scoped
`#[allow(clippy::unnecessary_cast)]`: `mode_t` is `u16` on Apple and `u32` on
Linux, so `as` is the widening there and a no-op here, and no single conversion
is lint-clean on both — `u32::from` failed Linux with
`clippy::useless_conversion` after `as` had failed it with
`clippy::unnecessary_cast`.
Verified on 36c038a28e plus this change: `cargo clippy -p codewhale-config
--lib -- -D warnings` clean on the host, `cargo check -p codewhale-secrets
--target x86_64-pc-windows-msvc` passing from the previous commit, `cargo test
-p codewhale-secrets` 55 passed / 1 ignored / 1 passed, `cargo fmt --all
--check` clean. Not verified locally: `codewhale-config` for Windows, whose
dependency graph reaches `ring`, and `ring`'s build script needs a Windows
toolchain this machine does not have. The Windows compile is left to CI.
Signed-off-by: CodeWhale Bot <[email protected]>
Plan active 13 -> 15, Plan full 30 -> 32, Act/Operate active 15 -> 17,
Act/Operate full 51 -> 53. The two new schemas are the only source
change; no tool left a surface.
--update also absorbed drift from the prompt and tool commits merged
after the 2026-10-26 lock (4cf2d2cff ran with [skip ci]), which is why
the representative stages and system-prompt byte counts move by 3-5
bytes with no matching change in this slice. The growth note in the
file records both.
Every Dependabot PR carries one permanently red check. `installation` mints a
GitHub App token from `vars.CODEWHALE_APP_ID` and
`secrets.CODEWHALE_APP_PRIVATE_KEY`, and GitHub withholds repository secrets
from workflows triggered by Dependabot, so the step fails before it starts:
Error: The 'private-key' input must be set to a non-empty string.
Both the variable and the secret are configured on this repository; the value
is simply not delivered to a `dependabot[bot]` run. The same job is skipped on
fork PRs and passes on same-repository human PRs, which is why the condition
below only needed to exclude the bot.
`installation` and `review` now skip when the actor is `dependabot[bot]`. Those
PRs have nothing for this reviewer to install a token for, and a check that can
never pass trains people to ignore a red X — it was the only failing check on
all nine open dependency PRs, which is exactly how a real failure would hide.
Verified: `actionlint .github/workflows/codewhale-review.yml` clean; the parsed
conditions keep `workflow_dispatch`, same-repository non-draft `pull_request`
and the `@codewhale` comment path unchanged. Not verified: the skip itself,
which needs a Dependabot run to observe — the next bump will show `skipping`
where it currently shows `fail`.
Signed-off-by: CodeWhale Bot <[email protected]>
Two build breaks in the previous commit, both invisible on macOS.
`StoreFile` and `advisory_locking_unavailable` were `#[cfg(unix)]`, but
`open_private` takes the role on every target and the write path calls the
predicate everywhere, so `codewhale-secrets` did not compile for Windows:
error[E0433]: cannot find `StoreFile` in `file_lock`
--> crates/secrets/src/lib.rs:627:73
error[E0425]: cannot find type `StoreFile` in this scope
--> crates/secrets/src/file_lock.rs:10:11
error[E0433]: cannot find type `StoreFile` in this scope
--> crates/secrets/src/file_lock.rs:242:72
error[E0425]: cannot find function `advisory_locking_unavailable`
--> crates/secrets/src/file_lock.rs:252:23
The role is a plain enum and is meaningful wherever it is passed, so it is no
longer gated. Windows takes real `LockFileEx` locks through `fd-lock`, so a
failed write lock there is genuine contention and the predicate answers false
rather than falling back to the in-process guard. `require_private_mode` stays
Unix-only, with a `let _ = role` on the other targets where nothing reads it.
`libc::stat.st_mode` is `mode_t`, which is `u16` on Apple and `u32` on Linux, so
`as u32` was a widening cast on macOS and a no-op on Linux that clippy rejects
under the workspace's `-D warnings`:
error: casting to the same type is unnecessary (`u32` -> `u32`)
--> crates/config/src/private_directory.rs
`u32::from` is total for both widths, and for the 32-bit Android target that is
`u16` too.
Verified on refs/pull/7168/head 95783672bf with both changes applied:
`cargo check -p codewhale-secrets --lib --target x86_64-pc-windows-msvc` fails
with the four errors above without them and passes with them;
`cargo clippy -p codewhale-secrets -p codewhale-config --lib -- -D warnings`
passes on the host; `cargo test -p codewhale-secrets` 55 passed, 1 ignored, 1
passed; `cargo fmt --all --check` clean. The Linux clippy run itself was not
executed here — cross-compiling this workspace needs a Linux `libdbus-sys`
build script — so the cast fix rests on `mode_t`'s declared width in libc
0.2.191 (`bsd/apple: u16`, `linux_like/linux: u32`) rather than a Linux run.
Signed-off-by: CodeWhale Bot <[email protected]>
- length-prefix JSON numbers in the archive hash so one field's text
cannot be absorbed by the field before it
- state that the closing paragraph of the compacted block is a host
instruction, not the previous model's summary
- describe the read tools' per-call and per-turn bounds honestly
- register the archive tools in the ACP host profile as well
- sync the tool-surface docs and public-surface facts
The number-framing test is a data-loss regression lock for the 304-byte
and 295-byte values the reviewer constructed; it is the only new test in
this slice.
A `[text](url)` link ended at the first `)`, so a destination with
parentheses of its own, like a Wikipedia article ending in
`_(programming_language)` or an old Microsoft Learn page ending in
`(v=vs.85)`, lost its last `)`. The hyperlink pointed at a page that
doesn't exist and the `)` showed after the label. Bare URLs already keep
balanced parentheses (bare_http_links_preserve_balanced_parentheses_in_target).
End the destination at the first `)` without a matching `(`, using the
has_unmatched_closing_delimiter check the bare URL path trims with. A
destination whose parentheses don't balance is no longer a link, as in
CommonMark, and shows as written.
Signed-off-by: Lstarsky0 <[email protected]>
`/config stream_chunk_timeout_secs <n>` answers in the transcript with
the new value and whether it was saved, then sends
Op::SetStreamChunkTimeout. The engine answered the op with "Stream
chunk timeout set to <n>s", which repeated that note in the footer and
stayed there; at 80 columns it also pushed the Shift+Tab hint off the
row. It is the same kind of ack that #7152 removed for SetFleetRoster
and 04de8eb67 for SetCompaction.
Store the timeout without sending a status. Nothing reads that message,
and a runtime thread's config reload, the other sender, already skips
status events that arrive before a turn starts.
Signed-off-by: Lstarsky0 <[email protected]>
A receipted release copy of `codewhale uninstall --yes` removed its program
files but left the user to hand-delete the PATH lines and completion files the
installer had told them to create. #7091 asks for a complete, verifiable
uninstall; this completes the shell half of it.
Uninstall now plans and applies two extra removals, both scoped to this
install and gated on the same receipted copy that owns the program files:
- This install's exact three-line `# >>> codewhale PATH >>>` block, in the
same profile list the old report walked. The block is only matched when its
middle line names this install directory, so another copy's block and a bare
`export PATH=…` line the user wrote are never touched. The profile is edited
by staging replacement bytes and renaming the original aside, so a failure
installing the replacement restores the user's own file by name; a symlinked,
unreadable or non-UTF-8 profile is reported and skipped. The profile's mode
is adopted before the guard captures it, since `GuardedFile` pins ctime.
- The Codewhale-generated completion files at their documented paths, honouring
`XDG_DATA_HOME` and `XDG_CONFIG_HOME`. They are kept while another Codewhale
copy remains on PATH, because a second install still wants them.
Cleanup failures after the program is already gone are reported individually
and exit non-zero rather than being silently swallowed. `--help`/dry-run
prints `Would remove` and changes nothing.
The installers still never edit shell profiles themselves; they now print the
PATH line as that marked block, so there is something provably ours to remove.
A bare `export PATH=…` line from an older install remains the user's.
Verified: `cargo test -p codewhale-cli --lib update::` 109 passed / 0 failed
(269 filtered); `vitest run lib/public-installer-contract.test.ts` 25 passed,
covering the printed marker block for zsh/bash/dash and the
unrecognised-shell case. Release build of `codewhale-cli` clean, no warnings.
Not verified: no real Windows host, no installed release artifact, and no run
against a profile this repository does not own. Refs #7091
The second review round reported five gaps in the compaction archive
tools; four held up and one did not survive measurement.
- Archive ids now hash a length-prefixed key. Hashing `id="ab" name="c"`
and `id="a" name="bc"` produced the same digest, so two distinct turns
could collide onto one archive id. `hash_json` stays for single values.
- `SUMMARY_HEADER` orders provenance before instruction: host facts and
the host's own directives render first, and everything below is
labelled as the model's summary rather than the host's claim.
- `bounded()` records the measured ceiling instead of implying one that
does not bind: `read_budget_bytes` exempts these two tools from the
route layer's three-percent rule, so `MAX_GET_CHARS` is the only real
bound (12000 chars, about 36 KB of CJK).
- The hook tool classifier learns both names. It gates shell and file
tools by path; these consume a hash id or a phrase and open no new
gate surface, which is why they land in `other`.
- Seven exact-surface assertions learn the two tools. Five were real
regressions I introduced: every named role, both small surfaces, the
machine-readonly evidence catalog and the machine-verifier catalog now
list them. Reading the archive is what any child that compacts needs,
so the surface grew rather than the tool being excluded.
This contradicts the review's N4 finding, which claimed child role
surfaces lack the tools and the summary header therefore issues a dead
instruction. Measurement shows all eight named roles and both small
surfaces expose them.
Verification: `cargo fmt --all -- --check` clean; the five surface tests
and the compaction/hooks/telemetry sets pass. `cargo test -p
codewhale-tui --lib` reports 12840 passed and 131 failed on this Windows
box; 5 of those were the surface assertions above and are now fixed. The
remaining 126 break down as 85 runtime_api (this checkout's `.codewhale`
is a reparse point, which the xAI OAuth path check refuses), 7
runtime_threads (deadline), 6 where a POSIX tool the test shells out to
is absent, 10 needing live MCP/network children, 6 platform sandbox and
encoding limits, and 12 that pass when run alone. None touch the archive
code.
`ApprovalRequest::impacts_for_locale` returned the English impact lines for every
locale except Simplified Chinese, which had its own parallel builder
(`build_impact_summary_zh_hans`) with hardcoded prefixes. The per-category
sentence was already a typed message (ApprovalImpactSafe ... ApprovalImpactUnknown)
with a value in all 15 packs; only the dispatch was zh-only.
One locale-aware `build_impact_summary(locale, ...)` now builds the lines for all
locales and replaces both builders (English and zh-Hans outputs are unchanged:
the English messages equal the old literals, and zh-Hans keeps its prefixes
verbatim). The six prefixed rows are new typed messages with a `{value}` slot in
all 15 packs: ApprovalImpactReads, Writes, Target, ConnectedApp, AgentType and
PrimaryInput. English still builds the stored `impacts` with the elevated
workflow-plan sentences; other locales get the generic agent line for a workflow
plan, as Simplified Chinese already did.
Observed with a temporary probe (removed): English and zh-Hans lines identical
to the old builders for read, write, fetch, shell, MCP and agent calls; German,
Japanese and French now show the translated sentence plus the prefixed row.
Verified (macOS arm64): `check-tui-locale-parity.py` PASS; codewhale-localization
54 passed, 0 failed; codewhale-tui approval/widgets/workflow/destructive/
decision_band/save_preview/ask_rule/impact filters 863 passed, 0 failed, 2
ignored (including the live-vs-legacy paint parity test over every shipped
locale); `preflight.sh --check` OK.
Observed once and not diagnosed: in the first batch run
`core::engine::approval::tests::full_access_fixture_uses_advance_authority_without_fabricated_approval_receipts`
failed with "fixture turn deadline: Elapsed" (its 5 s turn timeout). It passed 5/5
in isolation, 863/863 on two identical reruns, and 64/64 as 12 parallel
processes, so it did not reproduce; treat it as a load-sensitive timing flake
until it recurs with a captured iteration label.
Not covered: the 14 non-English translations were written without native-speaker
review; a workflow plan's own impact lines and detail labels
(`localize_detail_label`) and the delegated-authority rows are still English
outside Simplified Chinese.
Refs #7071
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Approval cards showed English, or Simplified Chinese only, for the "Do you want
to proceed?" question, the Why and Stop explanations on destructive actions,
the Approve / Edit plan / Cancel choices on workflow plans, the Save row (label,
"always allow / always ask first / never allow", "+N more") and the details
pager's "Tool Params:" title. Those were renderer-local `match locale` branches
and string literals. They are now 15 typed MessageIds (ApprovalProceedQuestion,
ApprovalFieldWhy/Stop/Save, ApprovalWhyBody, ApprovalStopBody,
ApprovalWhyCompactBody, ApprovalWorkflowApprove/EditPlan/Cancel,
ApprovalSaveSummaryAllow/Ask/Deny, ApprovalSaveMore, ApprovalToolParamsTitle)
with a value in every pack. Simplified Chinese keeps its existing wording
verbatim; English output is byte-identical. `PermissionRuleSavePreview::summary`
now takes the locale.
The frozen legacy approval painter (test counterpart only) keeps its own save-row
code, so it now reads the same localized label and "more" text, and its compact
entry budget uses the kit's `2 + label + summary + separator + more` formula
instead of a hardcoded 10; its duplicate proceed/semantics/workflow helpers are
deleted so it reuses the live ones. `tiny_localized_approval_keeps_every_action_and_hitbox`
asserted the literal English "Save:" in every locale; it now looks for the
locale's own label.
Known limits: the 14 non-English translations were written without native-speaker
review. Both painters budget the compact Save row by characters, not display
columns, so a CJK label can wrap it onto a second line (layout accounts for the
extra row). Still English outside Simplified Chinese: the card's impact summary
(`impacts_for_locale`) and the workflow plan's detail labels
(`localize_detail_label`).
Verified (macOS arm64): `check-tui-locale-parity.py` 2747/2747 keys in all 15
packs; codewhale-localization 54 passed, 0 failed; codewhale-tui approval,
widgets, workflow, destructive, decision_band, save_preview and ask_rule filters
863 passed, 0 failed, 2 ignored, including the live-vs-legacy paint parity test
across every shipped locale; `preflight.sh --check` OK.
Refs #7071
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A GUI start on HarmonyOS hmdfs failed one feature at a time: the workspace drive
lease, then the owner control socket as `failed to build daemon state`, then the
OAuth credential directory as `Codewhale credentials directory must be owned by
the current user`. Each of those admitted a directory by proving it was
current-user-owned and closed to group and other, and on that mount no directory
can: it reports the logged-in user's uid and a group-writable mode for every
object, whatever uid created it, so the calling process is never the reported
owner and `chmod` answers EPERM. Each fix revealed the next, because the same
mechanism sits under every private directory Codewhale keeps.
The mechanism now names the condition once. `private_footing` creates one `0o700`
directory under the target, reads back what the mount made of it, and — when that
is not the private owner and mode it asked for — asks the mount to narrow it and
looks again. Only a mount that refuses, or that keeps its own values after being
asked, is synthesising; a filesystem that applies an unexpected mode at creation
but honours the correction stores modes and keeps the full guard, so a default
ACL cannot silently relax a check. The probe runs only after a strict check has
already failed, and its answer (with its one warning) is remembered per
directory, so no write path probes a healthy filesystem.
Where the mount is synthesising, the private object is admitted with the
ownership guard recorded as unverifiable. The no-follow component walk, the
single-link check, atomic publication and the retained handle all still apply,
and the mount's own access control governs the rest.
Two capabilities are kept apart, because conflating them is what made the
original failure look like a missing feature. Exclusivity is the kernel lock, not
the ownership proof: the workspace lease is still taken, and a second driver is
still refused with the holder's pid, client, store and age. Only a filesystem
that refuses advisory locks themselves (`EPERM`, `EACCES`, `ENOTSUP`, `EINVAL`,
`ENOSYS`, and never `EWOULDBLOCK`) is reported as unable to provide exclusivity,
once per admitted workspace scope. The owner control socket has nothing else to
fall back on for discovery, so it degrades to HTTP only and says so; a
daemon-only run and ACP publication still require it and name the reason.
Narrowing a file or directory after writing it goes through one helper that
attempts the `chmod`, keeps the error on a filesystem that stores modes, and
excuses only a mount that cannot apply them — applied to the handle that was
opened where one exists, so a substituted path cannot be the object narrowed.
Checks that repeated the ownership proof on an object already admitted (the
retained-directory identity comparison, an owner receipt's mode, a published
socket's owner) now follow the guard it was admitted with.
`scripts/ohos-sign-binary.sh` cross-builds, strips and self-signs the Engine and
verifies the signature before handing the file back;
`scripts/ohos-lease-footing-probe.sh` reports what a directory on a device does
with ownership, mode and advisory locks; `scripts/ohos-secrets-store-smoke.sh`
exercises the secret store's degraded path against a real mount.
A file-backed secret store could not be written on HarmonyOS hmdfs, the 2in1
user directory. Every object there is reported as belonging to the logged-in
user whatever uid created it, so a process is never the owner of what it
creates: `chmod` answers EPERM, and the base mode is `0660` for every file.
Three checks assumed otherwise — the `0600` gate on `secrets.json` and its
`.lock`, the narrowing before the atomic rename, and the cross-process write
lock itself.
The mode gate now decides from evidence rather than from the mode alone: it
creates one probe file at `0600` in the store's directory, asks the mount to
narrow it, and reads the mode back. Only a mount that refuses, or that keeps
its own mode after being asked, is treated as one where the mode says nothing
about who may read the file; such a mount is accepted with a warning, because
the alternative is a plaintext key in `config.toml` — a wider exposure. A
filesystem that stores mode bits keeps the strict refusal, so a store left
readable by group or other is still refused rather than silently repaired:
those bytes may no longer be ours, and re-narrowing the file would hide the
question. The `.lock` sibling holds no secret and is repaired in place instead.
When the filesystem reports no advisory locks (EPERM, EACCES, ENOTSUP, EINVAL,
ENOSYS), secret-store writes are serialised within the process and the
fallback is logged. A lock that is genuinely held (EWOULDBLOCK) is unaffected.
Two Codewhale processes whose snapshots overlap can lose one write on such a
mount, which needs both to rewrite the store in the same instant.
The memory database's mode check takes the same evidence-based rule, so a
default ACL on a mode-storing filesystem no longer counts as a mount that
cannot hold a permission.
The credential and lease sections described the 2in1 user directory as a mount
that "refuses `chmod` on files the caller owns" and "refuses `flock` and
`fcntl(F_SETLK)`". Measurement on a HarmonyOS PC (`HPR-W72`, API 26) says
otherwise in a way that matters:
touch ~/.codewhale/x -> mode=0640 uid=20001006
chmod 600 ~/.codewhale/x -> EPERM, mode unchanged
mkdir -m 700 ~/.codewhale/d -> mode=2771 uid=20001006
Every object is reported as belonging to the logged-in user whatever uid created
it, so the calling process is never the reported owner — and `chmod` fails for
that reason, not because the mount has no mode support. This also separates two
capabilities the old text ran together: the ownership *proof* can never hold
there, while the kernel lock is a different thing and the lease is still taken.
Exclusivity is therefore not lost; only a filesystem that refuses advisory locks
themselves makes the Engine run without it.
The "can this directory hold a private file" recipe is corrected the same way: a
mode read back in a shell does not answer it, because the `chmod` that would
narrow the probe is refused on exactly the mounts the recipe was meant to catch.
It now points at the warning the store itself logs.
The store-side evidence probe this text describes lands in a separate change,
which is why the wording states the behaviour rather than the mechanism.
Changelog line and @gannp credit on CHANGELOG `### Contributors`,
docs/CONTRIBUTORS.md and UNRELEASED_CONTRIBUTORS. Regenerates
web/data/features.generated.json (the `local-model-reasoning` registry row from
#7125 and the shifted registry lines) and web/data/agent-docs.generated.json
(the earlier credit commit left CONTRIBUTORS.md stale in it).
Verified: `node web/scripts/check-facts.mjs` OK, `scripts/check-contributor-credit.py`
reports every contributor credited on all three surfaces, `scripts/preflight.sh
--check` passes.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
PR #7125 added the reviewed vLLM template registry and shapes the wire body from
it, but two earlier stages still treated every vLLM model as low/medium/high:
- `ReasoningEffort::normalize_for_route_identity` fell through to
`Low | Medium => High` for any provider without catalog metadata, and
`plan_turn_route` runs it before the shaper, so a saved `low` or `medium`
reached `apply_local_reasoning_controls` already collapsed to `high`.
- `route_picker_efforts` returned the generic Auto/Off/High/Max list, so the
picker could not offer a model's own tiers.
Both now consult `local_reasoning_spec(wire_model)` for `ProviderKind::Vllm`:
normalization uses the registry's `normalize` (tiers plus legacy aliases), and
the picker offers Auto plus the registry's tiers. Models without a registry
entry, and every other provider, take the existing paths unchanged. Both review
threads on #7125 (Copilot) named these two sites.
Observed with temporary probes (removed): on the merged tree before this change
every vLLM model mapped low->high and medium->high. After it, Qwen3.8-27B keeps
low and medium (picker: auto, off, low, medium, max), GLM-5.3-Flash and
DeepSeek-V4-Flash-Vision-Exp show their reviewed ladders, and an unreviewed
model is unchanged. Existing checks: codewhale-config `local_reasoning`
6 passed, 0 failed; codewhale-tui filtered reasoning/picker/route-plan/vllm
98 passed, 0 failed.
Not observed: a real vLLM or SGLang server.
Refs #7124
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Changelog lines for #7147, #7146, #7156, #7157, #7158, #7152 and #7150, and
credit for the contributors across CHANGELOG `### Contributors`,
docs/CONTRIBUTORS.md and UNRELEASED_CONTRIBUTORS (adds @luochen211, whose #7150
and #7149 landed). #7149 is a dead-code chore and has no changelog line.
Verified: `python3 scripts/check-contributor-credit.py` reports every contributor
in the window credited on all three surfaces; `scripts/preflight.sh --check`
passes; `sh scripts/sync-changelog.sh` regenerated crates/tui/CHANGELOG.md.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The Simplified Chinese guide's "upgrade / uninstall" bullets still told users to
`rm` the two binaries, while docs/INSTALL.md and the zh-Hans section 12 describe
the receipt-owned `codewhale uninstall` preview and `--yes`. The upgrade bullet
now matches the English text too (automatic for this install, or run
`codewhale update` now).
Verified: `node web/scripts/derive-install.mjs` renders both guides with no
broken internal link. Docs only; no installed-artifact or Windows evidence.
Refs #7091
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
fix(tui): render ~~~ fences as code blocks
Conflict with #7146 resolved in the ParseState doc comment only: the carry is
now `open_fence`, `code_language`, `code_block_id`, `in_list`. All code hunks
merged cleanly.
- feishu-bridge, wecom-bridge: follow-redirects 1.16.1 -> 1.16.2
(GHSA-mrwp-f3q8-3g9p, high, patched in 1.16.2). Lockfile-only; `npm audit`
reports 0 vulnerabilities in both; their node tests pass (19 and 17).
- vscode extension: override minimatch to ^11.0.1 (GHSA-g3hv-75wg-m249, DoS,
fixed in 11.0.1). Only @vscode/vsce, a build-time packager, depends on it and
calls the standard minimatch() export. `npm run audit:dependencies` reports 0
findings, `vsce ls` still enumerates the package, and the extension tests
pass (47).
web/ is intentionally untouched: its 18 moderate findings are the same minimatch
advisory plus glob (GHSA-3g3m-2fjr-6pw4) in the eslint and OpenNext build
toolchain, with no non-breaking fix, and web/docs/DEPENDENCY_SECURITY.md forbids
broadening the admitted advisory list. Its production tree audits clean.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The previous commit's one-line `let narrow = if ... { 40 } else { 80 };`
failed `cargo fmt --check` in the main landing preflight and the hosted Lint job.
Whitespace only; `cargo fmt --all -- --check` is clean.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
- remote-control footer test: a host with no sandbox backend (all Windows)
paints `files: workspace (unenforced)` in the posture chip, which at width 40
leaves the right slot no budget, so `/rc status` is shed by design. The test
now uses width 80 when no backend is present. Reproduced on macOS by pinning
sandbox_backend = None.
- extension-host disabling-mid-call test: a 150 ms wait for the call to be in
flight lost the race on a slow Windows runner, yielding NotAvailable instead
of Cancelled. Wait 1500 ms (the slow_wait tool runs far longer).
- native runtime library inspection (otool -l) deadline 3 s -> 10 s so a loaded
macOS runner does not trip the fail-closed probe.
Verified locally on macOS: the three tests pass (3 passed, 0 failed). Windows
legs not re-run; hosted CI not re-run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The first attempt at the compaction archive left 22 findings from an
adversarial review. This addresses them.
Archived ids now hash the message structure -- role plus each block's kind,
provider id and canonicalised input -- instead of the rendered text. Two
separate tool calls that happened to render identically used to collapse into
one entry and orphan the second call's result; a message written to several
cumulative snapshots still resolves to one id. `hash_json` sorts keys and
length-prefixes strings itself, because `serde_json` here has `preserve_order`
on and `Value::to_string()` therefore does not give a stable key order.
The tools now bound themselves. `context_archive_get` pages by character with
`offset` / `max_chars` (default 4000, hard cap 12000) and refuses an offset past
the end instead of silently returning nothing; `context_archive_search` pages
with `skip` and returns, per hit, the archive id, the character offset of the
match and a short snippet window (80 characters each side). Both declare their
own output size through `read_budget_bytes`, because the routing layer truncates
anything larger than the declared budget and a cut JSON document would arrive
with a `next_offset` that already pointed past the page. Every load, search and
slice runs inside `spawn_blocking`; the previous version read and parsed
snapshots inline on the runtime.
Reachability: the two tools are registered for Plan as well as Work, and both
names are in `DEFAULT_ACTIVE_NATIVE_TOOLS` so the pinned catalog carries their
schemas. The handoff note names them, and a model cannot call a tool whose
schema it has never seen.
Smaller fixes from the same pass: `snapshot_dir` refuses a session id that
`is_valid_session_id` rejects; `render_message` tags tool calls as
`[tool_use name#id]` and images as `[image] <url>` rather than dropping them
silently; the summary header no longer claims the note is unverified without
saying which claims that covers, and names the
`artifacts/context-transfer-*.json` path it had dropped.
Three findings are documented as known limits in the module header instead of
being fixed, because fixing them changes behaviour beyond this change: a
sub-agent shares its caller's namespace, so one archive can hold snapshots from
several engines of the same session tree; a tool result that a later pruning
pass rewrote to a stub is archived a second time under a new id; and a snippet's
`<<` / `>>` markers read ambiguously when the original text already contains
them, which is why `match_offset` is the precise locator.
Verified: `cargo fmt --all -- --check`; `cargo check --locked -p codewhale-tui
-p codewhale-telemetry`; `cargo test --locked -p codewhale-tui --test
feature_registry` -> 2 passed; `RUST_MIN_STACK=16777216 cargo test --locked
-p codewhale-tui --lib compaction` -> 183 passed, 0 failed. The stack variable
is the CI one (ci.yml exports 16777216 for test threads).
Compaction already persisted the full pre-compaction history to
artifacts/context-transfer-<checkpoint>.json before swapping in the
summary, and nothing could read it back. A detail the summary dropped
was gone for the rest of the session.
Two read-only tools close that gap:
- context_archive_search finds a phrase across every reachable
snapshot, case-insensitively, and returns short snippets carrying the
archive id of each hit plus the total match count.
- context_archive_get reads one message by id, with unique-prefix
matching and char-offset paging for long messages.
Entry ids derive from content: sha256 over role, a NUL separator and the
rendered text, first 12 hex characters. Snapshots are cumulative, so the
same message appears in many of them; the content-derived id is what
collapses those duplicates into one addressable entry.
Case folding is ASCII-only on both sides of the search. Unicode
lowercasing can change byte length, which would desynchronize the hit
offset from the text and could slice outside a char boundary.
The summary header now names both tools instead of pointing at a file
the model has no tool to open.
Known limits, recorded in the module header: thinking blocks are not
archived, only the newest 64 MiB of snapshots load, and every call
reloads and re-parses the snapshots, so paging through one large entry
repeats that work.
A bare URL ran to the next whitespace, and Chinese and Japanese text
doesn't put a space after one. In
`项目地址:https://github.com/codewhale-hq/Codewhale。克隆后运行 cargo build`
the link took in `。克隆后运行` as well, so the rest of the clause was
styled as a link and the hyperlink target pointed at a page that
doesn't exist. A URL in full-width parentheses kept the closing `)`
the same way, and one in curly quotes kept the closing `”`.
A bare URL now also ends at full-width sentence and clause punctuation
and brackets (。,、;:!?()「」『』【】《》〈〉) and at curly quotes
(“”‘’). Ideographs and kana still belong to the URL, so
`https://zh.wikipedia.org/wiki/北京` links whole, and trailing ASCII
punctuation is trimmed as before.
Signed-off-by: Lstarsky0 <[email protected]>
`*` and `**` opened emphasis wherever a matching run came later on the
line, so prose with spaced asterisks lost them: `*args and **kwargs`
rendered as "args and *kwargs" with "args and " in italics,
`5 * 60 * 60` as "5 60 60", `*.rs and *.toml` as ".rs and .toml", and
`x ** 2 + y ** 2` as "x 2 + y 2".
An emphasis run now needs text right inside both delimiters, so it
can't start or end with whitespace. That is the whitespace half of
CommonMark's flanking rule; the `_` arm already checks it on the
opening side (#6042). `*real*` and `**this**` render as before.
Signed-off-by: Lstarsky0 <[email protected]>
`an_undeliverable_user_input_request_fails_fast` asserts that a pending
user-input wait has not completed by probing it with a 2 ms timeout while the
user-input timer is set to 10 ms. That leaves 8 ms of headroom, which Windows'
default ~15.6 ms timer granularity exceeds: the user-input timer wins, the wait
resolves, and the "still waiting" assertion inverts. It failed on the Windows
leg of the release validation while passing on Linux and macOS.
The timer is now 200 ms and the probe 50 ms. The intent — the wait has not
finished immediately — is unchanged; only the margin is now larger than any
scheduler granularity.
Verified: the case passes locally. Windows itself is verified only by the CI job
that reported it.
Not established: whether this is the only timer-margin assertion of its kind;
a scan for similarly tight probes was not run.
Three cases failed in the full `Test (ubuntu-latest)` leg of the release
validation. Two are the fixture class da026a2436 introduced; one is mine.
- `route::export::tests::golden_providers_export_matches_registry`
(codewhale-config): regenerated with `WRITE_GOLDEN=1`. The golden now lists
exactly the four routes the catalog refresh added — `kimi-k3.1-pro`,
`kimi-k3.1-pro-highspeed`, `MiniMax-M3.1-Flash` (twice: direct and
Anthropic-compatible) and `gpt-6.1-terra`. This is the one that was mine: the
catalog change landed without its golden because
`scripts/preflight.sh --check` does not run the version-stamped fixtures
(`--full` does).
- `tui::app::tests::test_update_model_compaction_budget`: pinned
`active_route_limits = None` but kept the window `App::new` had resolved, so
`deepseek-v4-pro` was budgeted from the wrong model's window and came out
*below* the unknown-model fallback (61440 vs 62976). The fixture now clears
`active_context_window` too, which is what production does through
`set_model_selection`.
- `commands::config_policy_status_tests::tests::status_report_localizes_the_complete_japanese_surface`:
set `active_context_window_source = ProviderReported` without the paired
window, so the installed window's own source won and the Japanese
"provider reported" string never rendered. The fixture now installs both.
Verified: all three pass locally under RUSTFLAGS="-D warnings"; `scripts/
preflight.sh --full` passes its version-stamped fixture step (the other
`--full` ratchet then aborted only because it refuses to measure a dirty tree,
which is resolved by this commit).
Native HarmonyOS users had to install a Rust toolchain and an OpenHarmony
SDK to get a working `codewhale`; a release could not serve them, and npm
handed them the static Linux build, which a device refuses to start.
Releases now build `aarch64-unknown-linux-ohos` as an eighth target and
publish `codewhale-ohos-arm64` with its `codew` alias and archive. The leg
signs the binary with the SDK's own signer and refuses to stage it unless
the signature reads back, because an unsigned ELF is refused at execve and
would be a dead download. `npm i -g codewhale` on that platform now picks
the native asset instead of the Linux one.
The asset inventory stays exact and enforced: 38 files across eight
targets, with the workflow, packaging and runbooks updated together so a
candidate that misses the HarmonyOS leg fails instead of shipping quietly.
Signed-off-by: Ben Gao <[email protected]>
Following the HarmonyOS page from a clean checkout failed before it produced
anything: the documented `cargo check` for the OpenHarmony target died inside
`aws-lc-sys`' build script with `Required build dependency is missing. Halting
build.` — a message that names neither cmake nor the target. `ohos-env.sh` now
puts the SDK's own `build-tools/cmake/bin` on `PATH` and exports `CMAKE`, so the
build no longer depends on what the host happens to have installed.
With that fixed, the check reached a second, unrelated failure (an ungated
`mod hashing`); that one is already fixed on main by fd9f06d87, so this commit
does not repeat it.
The page also gained what it was missing, verified on hardware (HPR-W72,
HarmonyOS 7.0.0.111, API 26, 2in1): only signed code runs at all, so `chmod +x`
is irrelevant; `hdc shell` cannot execute a binary under /data/local/tmp
because of its security label, which means a correctly signed binary still
reports `Permission denied` there and must be started from HiShell; and an
emulator is more permissive than a retail device. Signing with the SDK's own
`binary-sign-tool` is now part of the instructions, along with the two things a
self-signature does not grant (permissions and JIT) and the credential-storage
behaviour of the 2in1 user directory.
The capability table separates what was observed from what the target's cfg
gates make unreachable, and says plainly that the interactive TUI has not been
drawn on a device yet. The Chinese mirror matches it section for section.
Signed-off-by: Ben Gao <[email protected]>
`crates/cli/src/lib.rs` declares `mod update` as
`#[cfg(not(target_env = "ohos"))]`, but `mod hashing` — whose only consumer in
the crate is `update.rs` — was not gated. On the OpenHarmony target `update` is
not compiled, so `sha256_hex` has no users there and the OHOS
`cargo check` job fails under `-D warnings` with
`function sha256_hex is never used`.
Gating `hashing` exactly as its only user removes the dead item on that target
and changes nothing on any other, where both modules still compile.
Verified: `cargo check -p codewhale-cli --lib` under RUSTFLAGS="-D warnings"
passes. The OHOS target toolchain is not installed here, so the OHOS build
itself is verified only by the CI job that reported the failure.
Not established: whether this failure is older than this session's changes; the
module split that produced it predates them (`9a88b2fd88`), and the signature
is a target-gating omission rather than anything the S2 moves touched.
The markdown renderer only knew backtick fences, so a block opened with
~~~ rendered as prose: a `# comment` line became a heading with a rule
under it, indentation was dropped, and `*x*` turned italic. The rest of
the codebase already treats ~~~ as a fence (memory import, the agent
roster, the plan handoff title).
The fence carry now records the character along with the length. A
fence closes only on the same character and at least the opener's
length, so a ``` line inside a ~~~ block is code, and the other way
round. Backtick fences parse as before. The streaming thinking preview
mirrors the parser's fence rule to find a safe cut, so it follows.
Signed-off-by: Lstarsky0 <[email protected]>
`ci.yml`'s Documentation job runs `cargo doc --workspace --no-deps` under
`RUSTDOCFLAGS="-D warnings"`, which makes `rustdoc::private-intra-doc-links` and
`unresolved link` errors fatal. It has been failing on every recent run, across
eight crates, none of which this session's work touched:
- `tui-kit` referenced private `MAX_QUOTE_DEPTH` and `TAIL_SENTINEL`.
- `skills` referenced private `SUPERSEDED_BODIES`.
- `plugins` referenced private `stage`/`tarball`/`place` and an unimported
`PluginInstallSource`.
- `snapshot` referenced private `WorkspaceGate::describe`,
`MAX_SNAPSHOT_SIZE_MB` and several `Self::` methods that only resolve inside
their own item.
- `skill-install` referenced private `CacheMeta` and `candidate_urls`.
- `runtime` referenced private `InMemoryCredentialStore`, `Workspace::
follow_links`, `apply_env_overrides`, `parse_config_file` and a
`codewhale_tui::` path that does not resolve from this crate.
- `models` referenced private `SUMMARY_HEADER`.
- `config` referenced private `SetupTransaction`, `ConfigStore`,
`DEFAULT_TELEMETRY_ENDPOINT` and a `ConfigToml::` method from outside its impl.
A public doc comment cannot link to a private item, and none of these links were
load-bearing: they name implementation details, so they become plain code spans
rather than being made public. One link (`PluginInstallSource`) was a real
missing import in the link, so it gained an explicit path.
Verified: `RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps` passes;
`scripts/preflight.sh --check` passes.
`ci.yml`'s Version drift job runs `scripts/release/crates.sh`'s order through
`validate-crate-publish-order.test.sh` and it has been failing on every run —
all five recent CI runs, covering both pull requests and dispatch — with:
codewhale-runtime (position 28) depends on codewhale-util (position 34)
`codewhale-util` moved earlier in the dependency graph when `runtime` began
using it for the shared test-environment barrier (6dd9af54c6), and again for
nothing else: `dependencies`, `lsp`, `plugins`, `skills`, `skill-install`,
`snapshot` and `tui` already depended on it while sitting after it in the list.
Publishing in that order would have made `cargo publish` fail on the first
dependent crate, so this was a release blocker, not a lint.
`codewhale-util` now publishes right after `codewhale-memory` and before
`codewhale-runtime`, which is the first of its dependents.
Verified: `scripts/release/validate-crate-publish-order.test.sh` passes;
`scripts/release/prepare-release.test.sh` still passes;
`scripts/preflight.sh --check` passes.
Refs CW-6129
`workspace_lease` (the cross-process drive-holder record and shared lease)
moves to `codewhale-runtime`. It depended on `codewhale_snapshot::paths` for
`strip_worktree_suffix` and `stable_hex`, and `codewhale-snapshot` depends on
`codewhale-runtime`, so moving the module first would have created
`snapshot -> runtime -> snapshot`.
Those two helpers are pure `std` and are the shared project/worktree split both
crates derive their on-disk layout from — `stable_hex`'s own doc said two copies
"would be two things to keep in step". They move down to `codewhale-paths`, which
sits below both, and `snapshot::paths` re-exports them so its callers and its
`paths::` tests are unchanged.
Verified: `cargo check -p codewhale-paths -p codewhale-snapshot
-p codewhale-runtime -p codewhale-tui --lib` and `--tests` pass under
RUSTFLAGS="-D warnings"; `codewhale-runtime --lib workspace_lease` runs 9 cases,
9 pass, including `lease_path_is_stable_and_worktree_aware` — the case that
exercises the relocated helpers through a real lease path; `codewhale-snapshot
--lib paths::` runs 4 cases, 4 pass through the re-export;
`scripts/preflight.sh --check` passes (the path-keyed blocking-calls budget is
unchanged at 570 sites).
Measured: tui 570,786 -> 570,347 [M] production lines, runtime 35,167 ->
35,608. Cumulative across the three S2 moves: tui 571,736 -> 570,347
(-1,389), runtime 32,101 -> 35,608.
Not established: no cross-process lease contention was exercised beyond the
module's own cases.
Refs CW-6129
Three more TUI modules that are Engine state rather than presentation move to
`codewhale-runtime`:
- `workspace_trust`: the per-workspace list of trusted paths, persisted beside
the config. A headless Engine needs to answer "may this be read" without a
terminal.
- `delivery_files`: the guarded open/write path used when Codewhale delivers a
file, including the symlink/hardlink/ownership checks. `crates/cli` reaches it
for update and uninstall, so it was already an API two crates needed.
- `session_diagnostics`: the redacted summary of a session's failure signals.
`delivery_files` referenced `codewhale_runtime::hashing` from the outside and now
calls `crate::hashing`; the TUI and `crates/cli` call sites were rewritten by
path. `codewhale-error-taxonomy` is added to runtime's dependencies for
`session_diagnostics`; that crate depends only on `codewhale-tools`, which
depends only on `codewhale-protocol`, which has no workspace dependencies, so no
cycle is introduced. Five `session_diagnostics` items became `pub` because
`pub(crate)` does not cross a crate boundary. One `expect(dead_code)` on
`WorkspaceTrust::permits` was removed: a `pub` item in a private module is
dead-code-checked, but the same item in a public module is not, so the lint
expectation became unfulfilled.
Verified: `cargo check -p codewhale-runtime -p codewhale-tui -p codewhale-cli
--lib` and `--tests` pass under RUSTFLAGS="-D warnings";
`codewhale-runtime --lib workspace_trust delivery_files session_diagnostics` runs
22 cases, 22 pass; `scripts/preflight.sh --check` passes, which is what catches
the path-keyed blocking-calls budget the previous module move silently broke
(`delivery_files` and `workspace_trust` entries moved crates with identical
counts).
Measured: tui 571,427 -> 570,786 [M] production lines, runtime 34,158 -> 35,167.
As with the first S2 move this is a small slice justified by ownership, not by
the line count.
Not established: no end-to-end delivery or trust decision was exercised through
a real turn after the move, only the modules' own cases and the crate checks.
Refs CW-6129
GitHub does not evaluate a workflow on the push that introduces it, so the
first run would otherwise wait for the next landing and the gate itself would
go unproven. workflow_dispatch also lets a maintainer re-run it after an
infrastructure failure without an empty commit.
`ci.yml` triggers only on `pull_request` and `workflow_dispatch`, so nothing
validated a landing. On 2026-10-30 four commits landed red behind `[skip ci]`
and all nine open PRs then inherited the failure: a dead enum variant that only
fails under `-D warnings`, a clippy error in `codewhale-skills` fixed later, and
a blocking-calls budget left stale for a file that had moved crates.
`scripts/preflight.sh --check` already encodes exactly that class of breakage —
rustfmt, the TUI changelog slice, README locale stamps and links, the dead-code
and blocking-calls budgets, generated-asset drift and the feature receipts. It
is the script the repo already documents for "run it before you push", measures
45 s locally, and needed no new tooling here.
Deliberately narrow: no test suite, no clippy, no multi-OS matrix. Those stay
pull-request and release gates. This job exists to catch a red landing in
minutes, not to re-run the release gate on every push — `ci.yml` remains the
place full validation happens, via `workflow_dispatch -f expected_sha=<sha>`.
Verified: the YAML parses and resolves to one `push` job with three steps.
`scripts/preflight.sh --check` passes locally on this commit (exit 0).
`release-workflows.test.js` and `test_ci_migration_wiring.py` read only
`ci.yml`, which is unchanged; the latter already fails one case on main before
this workflow exists (recorded in PAINPOINTS).
Not established: the job has not run on GitHub yet.
Two checks that only `scripts/preflight.sh` runs caught fallout from the module
moves, after the crate checks were already green — the crate checks were not
enough evidence and the earlier commit said "verified" without them.
- `cargo fmt`: the longer `codewhale_runtime::artifacts::…` paths pushed 27 TUI
rewrite sites past the line width. Mechanical; `cargo fmt --all`.
- `scripts/check-blocking-calls-budget.py`: the budget is keyed by file path, so
moving `artifacts.rs` into `codewhale-runtime` left a stale `tui` entry and an
unaccounted `runtime` one. The counts are identical (`std_fs: 2`) — a
relocation, not a new blocking call.
The same run also surfaced `crates/runtime/src/working_set.rs` (`std_fs: 1`,
`path_canonicalize: 1`), which arrived in e5fa43aa82 and never had its budget
entry updated because that commit carried `[skip ci]`. It is recorded here
rather than left to fail the next PR.
Verified: `scripts/preflight.sh --check` is the check that failed before this
commit; the blocking-calls and rustfmt checks are re-run in the next step.
Every fleet change sends Op::SetFleetRoster, and the engine answered
each one with "Fleet roster refreshed for subsequent turns". Each of
those changes already writes its own receipt: the /fleet add|remove
note in the transcript, the toasts from the route editors, the
"Team ... profile saved" status. The engine's line arrived after it and
took over the footer, and in a Chinese UI it left an English sentence
there. 04de8eb67 fixed the same kind of ack replacing a host receipt for
SetCompaction.
Store the roster without sending a status. Nothing else reads that
message.
Signed-off-by: Lstarsky0 <[email protected]>
`artifacts` (the session artifact store: immutable writes, relative-path
resolution, the tool-call id that names each record) and
`tool_output_receipts` (the `/status` reading of raw tool-output pressure over
those records) were TUI modules that a headless Engine also needs. They move to
`codewhale-runtime`, which now sits above `codewhale-util` after 6dd9af54c6.
`artifacts` depended on the TUI only through `crate::test_support::
unsealed_state_dir`, which `codewhale-runtime` already provides, so the module
moves with no behavioural change. Its test seams (`TEST_ARTIFACT_SESSIONS_ROOT`,
`TEST_ARTIFACT_SESSIONS_GUARD`, `set_test_artifact_sessions_root`) are gated
`any(test, feature = "test-support")` so the TUI's own tests keep reaching them
across the crate boundary — the same pattern `codewhale-runtime` already uses
for `audit` and `config`. Three functions and two statics became `pub` because
`pub(crate)` is visible only within one crate; nothing new is re-exported.
182 references across 44 TUI files were rewritten mechanically by path.
Verified: `cargo check -p codewhale-tui -p codewhale-runtime --lib` and
`--tests` pass under RUSTFLAGS="-D warnings"; `codewhale-runtime --lib
artifacts` runs 9 cases, 9 pass.
Measured, not assumed: this moves 309 production lines out of `tui`
(571,736 -> 571,427 [M] strict) and into `runtime` (34,158, up from 32,101).
That is far below the ~10k-per-crate target and does not on its own justify
the slice; the reason to take it is that these two modules are Engine state
that a client without a terminal needs, not TUI presentation.
Not established: no end-to-end artifact write/read was exercised through a
real turn after the move, only the module's own cases and the crate checks.
Refs CW-6129
A chat turn stopped dead after a tool returned and produced nothing for 56
minutes: no assistant reply, no status line, no stall record, no cancellation.
The user interrupted it by hand.
Two owners each believed the other held that wait:
- `tool_batch.rs` entered `TurnPhase::Tools` with `None` as the bound, on the
comment that "per-tool timeouts, approvals, and the UI tool-hang watchdog own
a tool batch's bound". Those own a *running* tool. The engine's own work
after a result comes back — result processing, scheduling, the next request —
runs in the same phase and no per-tool timeout sees it.
- With no bound, `TurnHeartbeat::engine_owns_live_wait()` still returned true
(phase not Idle, `stall` empty), so `tui/ui/session_state.rs` declined to
fire its own watchdog. And `spawn_turn_stall_watchdog` could not fire either,
because `detect_stall_at` returns `None` when `bound` is `None`. Nothing was
watching.
`TOOLS_PHASE_BOUND` (1800s) gives that phase an owner, generous enough that a
legitimate long build stays far below it. The watchdog now also takes the
engine's cancellation token and cancels on the same report it used to only
describe: a status line that scrolls away while the turn still never ends is
the failure this watchdog exists to prevent.
Verified: `cargo check -p codewhale-tui --lib` under RUSTFLAGS="-D warnings"
passes; the four `turn_heartbeat` cases pass, including the new assertion that
a reported stall leaves the token cancelled. The existing
`stall_parked_phase_is_never_reported` case still passes, so a genuinely parked
phase is unaffected.
Not established: the original wedge is not reproduced (its cause was never
logged), so this bounds the class rather than proving that specific incident
fixed; 1800s is a judgement, not a measurement.
Refs #6184
The zh-Hans pack addressed the user as 你 in 43 strings and 您 in 6,
mixing both inside TelemetryNoticeBody. It also translated "provider"
as 服务商 in 4 strings against 提供商 in 90, and wrote usage tokens as
"Token" in 3 strings against "token" in 40.
Align the outliers with the dominant forms, and drop two stray spaces
after full-width punctuation in the telemetry notices. 令牌 is kept
where it means a credential (OAuth/access token).
Refs #6804
Signed-off-by: luochen211 <[email protected]>
Remove the file-level #![allow(dead_code)] from fleet/alerts.rs and
tui/pet_watch/worker.rs and let -D warnings adjudicate:
- pet_watch/worker.rs is already declared #[cfg(test)] in its parent,
so the allow suppressed nothing; removing it needs no other change.
- FleetAlertEvent::stale_from_worker_event and ::verifier_failed have
no production caller; only the module's tests build events with
them, so they move into the tests module with their imports.
- FleetAlertEvent::run_completed has no caller in any build and is
deleted.
Lower scripts/dead-code-budget.json with --update: total 164 -> 162.
Checks: cargo check -p codewhale-tui --lib --tests (warnings denied),
clippy -p codewhale-tui --lib --tests -D warnings clean, and
cargo test -p codewhale-tui --lib -- fleet::alerts pet_watch::worker:
10 passed; 0 failed.
Refs #5587
Signed-off-by: luochen211 <[email protected]>
The compatibility asset lists 17 `provided_services` and 22
`refused_services`, and 14 names appear in both. Read as two verdicts on the
same service that looks like a contradiction, and it cost a review pass to
establish that it is not.
They answer different questions. `provided_services` is what a plugin may
inject (`root.ts:715` fails activation on anything else); `refused_services` is
what a plugin may not provide (`root.ts:225` refuses `provide` for those names,
except the host's own shim). `tools` is both injectable and unprovidable by
design — a plugin uses the Engine's registry through the host shim and cannot
install a second one. The same section names `events`, `logger`, `reflect` and
`registry` as Cordis built-ins rather than Codewhale shims, and `loader` as
injectable without being advertised.
Documentation only; no behaviour, no asset change (the asset is trust-pinned, so
editing it would invalidate every installed DSH plugin's trust for no gain).
Verified by reading both enforcement sites and the `shimClasses` registrations.
Refs CW-6129
`codewhale-util` carried an optional `codewhale-runtime` dependency behind its
`test-support` feature, purely so `util::utils` could enroll a spawned blocking
thread in the caller's sealed test environment. That prospective edge is what
blocked the S2 module moves: those modules need `codewhale-runtime` to depend on
`codewhale-util`, and util depending back on runtime is a cycle.
`test_env_lock` is 295 lines of `std` only — a `Mutex`, a `OnceLock` and thread
ids — so it moves down to `codewhale-util` unchanged and keeps one static for
every crate that uses it. `runtime::test_support` re-exports the same names, so
no caller outside `crates/util` changes. `util` loses the optional dependency;
its `test-support` feature remains as an empty switch, because the `cfg` in
`utils.rs` must still be enabled when another crate builds util as a dependency
(tui, plugins and skills all enable it). Each of those three already turns on
`codewhale-runtime/test-support` itself, so nothing relied on the transitive
edge being removed.
Verified: the workspace normal-dependency graph now has `runtime -> util` and no
path from `util` back to `runtime` (checked by walking every `[dependencies]`
table). `cargo check -p codewhale-util -p codewhale-runtime -p codewhale-snapshot
-p codewhale-notify --lib` and `--tests` for util and runtime pass under
RUSTFLAGS="-D warnings"; `codewhale-util --lib` runs 51 cases, 51 pass;
`codewhale-snapshot --lib first_init_waits_for_the_side_repo_write_lock`, which
uses `env_scope_ticket`/`join_env_scope` from the new location, passes, which is
what proves the static is shared rather than duplicated. The `Cargo.lock` diff is
one added line (`codewhale-util` under runtime).
Refs CW-6129
`save_and_resume_keep_ownership_honest` failed deterministically, on clean
main as well as with this session's work: `/undo` on a thread resumed from a
saved document answered 400 "Cannot identify an exact saved-history boundary
for this backtrack" where the case expects 409 `restore_point_unavailable`.
Cause, measured rather than inferred. `projected_user_text` splits a message
into its text blocks and runs `extract_user_prompt` on each. That helper strips
a *leading* `<turn_meta>…</turn_meta>`, which is the shape a saved document
stores. A thread rebuilt from turn records via
`reconstruct_messages_from_turns_with` keeps its metadata in the *trailing*
shape, so nothing was stripped there. The same prompt therefore projected to
272 bytes from the document and 609 from the records (measured), and
`saved_history_boundary` — which walks one and compares against the other —
could never match. The first comparison already failed, so every backtrack of a
resumed thread refused.
`strip_turn_metadata_envelope` now removes one complete envelope from either
end after the per-block pass. The envelope is engine metadata, never prompt
text, so neither side may carry it into the comparison; the guard itself is
unchanged and still refuses when the prompts genuinely disagree.
Verified on 0d8b664a7a + this path: the failing case passes; the
`runtime_threads`, `saved_history_boundary`, `undo`, `restore_point` and `fork`
selection runs 393 cases with 3 failures, all three "Timed out waiting for
turn" under full parallel load, and all three pass when run alone (the same
robustness gap recorded in PAINPOINTS P7). `cargo check -p codewhale-tui --lib
--locked` under RUSTFLAGS="-D warnings" and rustfmt pass.
Not established: no end-to-end restore of a real multi-turn session was
exercised beyond the existing cases, and the two `sse_turn_recovery` cases that
fail in that selection also fail on clean HEAD.
A server that does not speak the 2026-07-28 revision can answer the
`server/discover` probe in two shapes: a bare transport status, or a
well-formed JSON-RPC error. Codewhale only recognised the first. Its modern
refusal gate accepted 404/405/406/415 and treated everything else as a hard
failure, so a server replying `400` with `{"error":{"code":-32600,
"message":"Unsupported protocol version"}}` stopped the connection instead of
negotiating the legacy `initialize` handshake it was plainly offering. The
same narrow test sat on the legacy side and on the extension host's two
allow-lists, so the refusal never reached the transport either.
`mcp::wire::is_json_rpc_error_body` parses the excerpt for an `error.code` and
both the modern and legacy refusal gates now accept `400` carrying one. The
vendored builtin module's two status lists gain `400` to match, since Rust is
the authority that issues the grant.
Verified on 0e3677b50d + this change: `mcp.deepwiki.com`, which answers the
probe with exactly that 400, now connects and lists its three tools, where
before it failed with `MCP Streamable HTTP rejected ... status=400`. Linear is
unchanged (`mcp tools linear` lists 77). `mcp_http`, `mcp::tests::` and
`table_matches_the_host_build` run 300 cases, 300 pass. The two
`sse_turn_recovery` cases in the same filter selection fail with and without
this change on clean HEAD, so they are not part of this slice.
Not established: no server that returns 400 for a reason other than a probe
refusal was exercised; the excerpt bound and the parse keep a non-JSON or
error-free body on the hard-failure path.
Removing a selected attachment, vim `x` and vim `dd` edited the composer
without clearing the recalled history entry, so the next Up/Down went on
from the stale index and silently brought the original entry back. The
other deletes already clear it (see the note on insert_str).
Signed-off-by: Lstarsky0 <[email protected]>
Co-authored-by: Hunter Bown <[email protected]>
Models.dev already lists these rows, so a live refresh shows them; the bundled
seed is what a first run, an offline start and a deep-offline install read, and
it stopped at `kimi-k3`, `MiniMax-M3` and `gpt-6-astra`.
`scripts/catalog/models_dev_seed.toml` now names `kimi-k3.1-pro`,
`kimi-k3.1-pro-highspeed`, `MiniMax-M3.1-Flash` (both the direct and the
Anthropic-compatible MiniMax entry, which share one upstream roster) and
`gpt-6.1-terra`. `seed lock` re-pinned the referenced rows and `seed render`
regenerated the bundled asset.
Verified: `seed render --check` reports the bundled asset matches the spec and
lock; `seed age --max-days 30` passes at 0.0 days; `defaults audit` reports
"0 stale"; `cargo test -p codewhale-config --lib --locked catalog` runs 71
cases, 71 pass. The four pre-existing rows for each provider are still present
in the rendered asset.
Not established: no provider call exercised any of these new ids, and their
limits and prices are whatever Models.dev publishes.
da026a2436 made the resolved `ContextWindowResolution` authoritative for the
effective window: `effective_budget_route` prefers the installed window, then
the route limits, then the catalog. Three fixtures still installed only one
half of the pair, so they exercised the fallback branch instead of the branch
they name:
- `inspector_uses_effective_auto_model_context_window` set
`last_effective_model` with no `last_effective_budget`, so Auto resolved
`deepseek-v4-pro`'s catalog window (128000) rather than the 1,000,000 the
case is about.
- `config_context_window_query_shows_override_and_effective_source` and
`status_report_names_context_window_source_and_override_key` set
`active_route_limits` and the source but not `active_context_window`, so
`/config context_window` reported "effective 128000 from fallback
(unverified)" and `/status` reported the catalog provenance.
Each fixture now installs the window beside its source, which is what the
production writers do. No production behaviour changed.
Verified on e5fa43aa82 + this path: `cargo test -p codewhale-tui --lib
--locked -- context_window inspector_uses_effective_auto` under
RUSTFLAGS="-D warnings" runs 15 cases, 15 pass, including all three repaired
ones.
The extension host forwards the SDK's own framing headers to Rust, and the
Rust send path then added the same two headers again through
`RequestBuilder::header`, which appends rather than replaces. Every POST and
the GET channel open therefore arrived with two `Accept` and two
`Content-Type` values.
A server that validates those headers refuses the duplicate. Linear answers
`415 Unsupported Media Type`, and Codewhale read a transport-shaped refusal
as "this endpoint is not Streamable HTTP", minted a legacy-negotiation grant,
and re-sent `initialize` over the deprecated SSE transport. Linear refuses
`GET` on `/mcp` with `405`, so the connection died as
`SSE error: Non-200 status code (405)` with no hint that the real cause was a
duplicated header. `linear` was unusable in every session that used it.
`send_mcp_request` now collapses each framing header to its first value
immediately before the request is built, so exactly one value reaches the
wire whatever the layers above supplied.
Verified on e5fa43aa82 + this path: reproduced the duplicate first with a
local logging relay and confirmed the 415 -> SSE -> 405 chain, then confirmed
by hand that two `Content-Type` lines alone produce Linear's 415 and one line
produces 200. After the fix, `codewhale mcp connect linear` reports connected
and `codewhale mcp tools linear` lists 77 tools; `mcp_http` (4 cases),
`mcp::tests` auth cases and the `-D warnings` lib check pass.
Known remaining, not fixed here: a server that rejects the modern
`server/discover` probe with 400 and JSON-RPC code -32600 is still not
treated as legacy-negotiation evidence, so such a server fails instead of
falling back (reproduced against mcp.deepwiki.com).
`session.window.rerooted` said only "first_message_changed", so a re-root
caused by restoring a session and one caused by the Engine replacing the
projection looked identical in the audit trail. The snapshot now records a
`cause`: `engine_projection` when the API projection replaced a non-empty
transcript from an empty first message, `session_restore` when a restore
installed the messages, and `unattributed` otherwise. The audit entry also
fires when a journal with entries has no active path, which previously
produced no record at all.
`App` keeps the cause in `window_reroot_cause` until the next snapshot takes
it, so the attribution survives the path from the writer to the snapshot.
Verified on e5fa43aa82 with these five paths applied: `cargo check -p
codewhale-tui --lib --locked` under RUSTFLAGS="-D warnings" passes, and the
reroot/session_state/api_messages/journal filters pass (14 + 3 + 44 across
session_state, api_messages and journal; `reroot` selects no case because no
existing test names it). The `restore` filter is red on one unrelated case
that also fails without this patch (`save_and_resume_keep_ownership_honest`,
covered separately).
Refs #7142
Closes the RS-10 leaf moves for #6937 (CW-6129 decomposition, slices S1 and
S3, built on a Boat VM against a frozen snapshot and reviewed here).
- runtime_log moves to codewhale-runtime (adds tracing-subscriber there);
its four consumers and the docs path are rewritten by
scripts/split/move-modules.py with no re-export facade. The one test that
reached into tui state helpers moves to tui's session_manager test owner.
- working_set moves to codewhale-runtime. summary_block now takes a required
read_allowed predicate with no default, so the credential-path denylist
cannot be skipped; tui passes tools::file::working_set_read_allowed, a
one-line wrapper over the unchanged enforce_read_denylist(path,
"working_set"). The estimate_tokens test moves to compaction's test owner.
Two methods widen to pub and the test-only items become
any(test, feature = "test-support") for tui's tests.
- Generated agent-docs data and the blocking-calls budget json follow.
Checks, frozen 2,190 tracked inputs on 1480e84fd3 plus these paths, unchanged
across the runs: `cargo check -p codewhale-runtime -p codewhale-tui --lib
--tests --locked` with RUSTFLAGS="-D warnings" exit 0; runtime runtime_log and
working_set cases 40 of 40; 312 of 312 focused tui cases (summary_block,
file_mention, mention_completion, runtime_api::workspace, core::engine::preview,
session_manager, compaction, persistence_actor, the two relocated tests);
137 of 137 session_manager cases after restoring one include!d test file to its
unformatted-by-rustfmt original. dead-code budget 164 (at budget), blocking
calls 569 sites / 169 files. Strict production lines (VM count_lines.py):
tui 573,775 -> 572,080, runtime 32,101 -> 33,815.
Not run: clippy, the full suite, the module-graph ratchet, hosted CI.
S2 (artifacts, workspace_trust and friends) is blocked by a runtime<->util
cargo cycle and is not part of this commit.
Main went red for an hour because deleting a last caller left dead code that only fails under CI's -D warnings. cw-gates now tells maintainers to run the CI-flag lib check after removing or rewiring a call site, and notes that changing RUSTFLAGS rebuilds every dependency.
Main failed `cargo check -p codewhale-tui --lib` under -D warnings (the
Lint job): PersistRequest::ClearCheckpoint lost its last production sender
when the reset path started sending one CompletedCommit (c4a4f42e9c,
6a08f03124), and four route-budget wrappers lost their last production
caller when the resolved window replaced them (da026a2436).
- Delete PersistRequest::ClearCheckpoint, its coalescing branch, the
checkpoint_clears set and its flush loop. A completed commit still clears
its own checkpoint only after its snapshot save succeeds. Tests that only
exercised the standalone clear are removed or trimmed in the same slice;
the compound-commit, failed-save and reset-lifecycle cases remain.
- Gate compact_tool_result_for_route, tool_result_context_view,
route_inline_char_budget_for_route and the route_context_budget_for_route
import behind cfg(test); production uses the *_with_window forms.
Refs #7148, #7133, #7095. Frozen 2,192 tracked inputs on b3f1e25ced plus
these 5 paths, unchanged across the runs: `cargo check -p codewhale-tui
--lib --locked` with RUSTFLAGS="-D warnings" exit 0; 112 of 112 focused
existing cases passed on a -D warnings test build (the 113-case CW-7640 set,
less the deleted clear test, plus every persistence_actor test), rustfmt
check clean. Clippy and the full test suite were not run.
Known leftover: PendingSession::Snapshot and ::Completion are now
equivalent at every use and can be collapsed in a follow-up.
cw-orient and cw-handoff drop worktree/branch lanes and the leave-dirt-alone
rule; handoff defaults to verified pushes on main with tag/release/publish/
deploy/merge/cleanup needing approval. cw-gates and cw-dogfood route native
builds through dev-cargo.sh/dev-test.sh and the build lock, and point release
work to RELEASE_CHECKLIST.md. Retire gh-credit-harvest, gh-treasure-hunt and
gh-compile-issues (merged into the read-only gh-find-prs queue skill).
gh-close-issues follows the Refs/Fixes/release-step closure rule.
codew-release-qa-sweep becomes a pointer; cw-dogfood carries the regression
refs. gh-file-issue drops provider-specific wording. RELEASE_CHECKLIST uses
the own-everything wording. Docs only; no product checks run. The frozen
crates/skills copies and the catalog_matrix negative list are untouched.
cw-land step 2 now makes the CHANGELOG [Unreleased] line and the three
credit surfaces part of the same commit as a user-visible fix or feature,
regenerates crates/tui/CHANGELOG.md, and runs a drift command that lists
feat/fix commits since the last CHANGELOG commit (no existing check covers
fixes). A catch-up changelog commit is named as the failure to avoid.
Also corrected in cw-land, per the skills audit: a push is not a CI request
(hosted CI runs once at the release step), the harvest example makes the
contributor the commit author with the maintainer as co-author, step 3 is
the single owner of credit mechanics, and the stale release-branch
mergeability step is removed. cw-handoff gains the same drift command as a
backstop before a handoff is written.
Replaces the uncommitted draft paragraph in cw-land; no new skill stage.
The effective context window (provider facts, operator configuration or
fallback) and its source are now one ContextWindowResolution value kept
beside, never inside, the provider's RouteLimits. Turn planning, the
Engine, compaction (parent, headless, runtime-thread and worker), tool
output sizing, output caps, child agents, the context inspector, the model
picker and the client's alternate-model rebind all read it. Before, an
exact custom model with no context metadata planned compaction against
1,048,576 tokens while its route resolved the 128,000 fallback.
Op::SetModel clears the installed window only when the model or limits
change, so the same-route syncs hosts send on load, reset and re-sync keep
it. Alternate-model rebinding derives its window and limits from the
client's frozen declarations and captured overrides rather than a later
mutable configuration. Accepted-route restoration in the session
projection keeps the paired window across the model canonicalization.
Also: a checkpoint or reset save with no pending Work update now reports an
unavailable persistence actor instead of dropping the request silently.
Refs #7133 (CW-7640), #7095 (CW-7553). Frozen 2,192 tracked inputs on
adcf3f5856 plus 42 dirty source paths, unchanged across the run: 113 of 113
focused existing cases passed (the 94 planner, route install, projection,
picker, budget, tool-output and pressure cases, 18 persistence/backlog/
lifecycle cases, and the two existing SetModel cases), including the
alternate-declared-model frozen-limit case that failed on the previous
candidate. rustfmt, sync-changelog --check and git diff --check pass.
Not established: the original Windows report is not reproduced; a restored
Auto session has no persisted window receipt and uses the fallback until
its next turn selects a route; the out-of-process protocol and provider
rollback paths are reviewed, not exercised end to end; no real-provider,
hosted CI or platform proof.
Nine user-visible fixes landed on main today without an [Unreleased] line.
Adds Fixed entries for:
- #7095 save and recovery-checkpoint coalescing and reset-failure fixes
(4d974f0c6d, b42759ee9b, c4a4f42e9c, 6a08f03124) and interrupted
deletion retry (16d42ab17d), stating the original report is not yet
reproduced;
- #7133 context inspector effective/zero threshold and disabled state
(0bf31581c0, e908528439);
- #7115 read-only doctor --probe-local credential check (14a5723b75);
- #7144 blockquote quote rail on wrapped rows, crediting @Lstarsky0 in the
Fixed entry, the CHANGELOG Contributors block and docs/CONTRIBUTORS.md.
crates/tui/CHANGELOG.md regenerated with scripts/sync-changelog.sh;
sync-changelog --check passes; check-contributor-credit.py: 9 contributors
since v0.10.2 credited on all three surfaces; git diff --check clean.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A saved session repeats its own conversation while it carries a journal:
`messages` and the journal's active branch hold the same turns, so the file
on disk is up to twice the history it holds. The fork writer measured that
stored shape against the 8 MiB canonical bound, while the reader that opens
the same document judges it by `SavedSession::transport_form`. A
conversation whose file passed 8 MiB could therefore be opened, saved and
continued for turns on end, yet never forked — and `/undo`, retry and
fork-at-turn are all forks, so a client answered "full history exceeds byte
transport bound" and left the turn in place (reported from the VS Code
client on a 9.75 MiB conversation whose carried form was 5.2 MiB).
Both writers now go through one `ensure_history_transport_fits`, so the
writer's rule and the reader's cannot drift apart again.
Forking also stopped finding the saved-history boundary at all in
conversations that had received engine traffic. The boundary walk reads a
saved transcript as "the prompts a person typed", but two kinds of engine
message were counted as prompts: runtime-owned control messages (the
MCP-server notice, shell-completion handoffs, the deferred-toolbox
snapshot) and the `<image path=…>` tags bracketing an attachment. Neither
is recorded on a turn, so no turn could ever account for them and every
such conversation refused to be cut. Both are now skipped, by the same
recognition the session views already use — a runtime provenance line in
the `<turn_meta>` envelope, which a person's own prompt does not carry.
Keep the prior recovery checkpoint if the session store or replacement snapshot cannot be constructed, and surface the localized save failure in the transcript and status. An existing lifecycle case reproduced the lost checkpoint before the fix.
Verified: 18 focused persistence, backlog, and lifecycle checks passed on 2,759 frozen inputs; rustfmt and diff checks passed. Local tests do not establish the original Deepin report or rendered platform acceptance. CW-7553.
Queue a constructed reset snapshot as one save-then-clear commit, and make coalesced snapshot/clear requests use the same conditional clear. This preserves the recovery checkpoint if the document write fails and avoids a race between separate requests.
Existing save-failure case first failed with the checkpoint removed; 17 persistence, backlog and session lifecycle cases passed on frozen b42759ee9b plus these files, with 2,759 inputs unchanged. Snapshot-construction failure still follows the prior fallback. Linker unwind warning was nonfatal.
Keep the origin of a pending snapshot when a completed commit is replaced, so an explicit clear discards the old completion but retains a newer reset snapshot, including when an in-flight checkpoint intervenes.
The existing coalescing case first failed for completed commit, reset snapshot, and clear. On frozen 4d974f0c6d plus these files, 16 persistence and backlog cases passed. Two existing comments were corrected afterward; all other 2,759 inputs matched the freeze. A failed reset-document save followed by checkpoint clear remains a separate unresolved case; linker unwind warning was nonfatal.
When coalescing a completed turn with a newer in-flight checkpoint, save the completed document without clearing the new checkpoint. Preserve explicit-clear ordering and distinguish a newer reset snapshot from a displaced completion.
The existing coalescing case first failed with the completed document absent. On frozen 1014538dde plus these two files, persistence actor cases passed 14/14 and backlog cases passed 2/2; 2,759 inputs were unchanged. A separate completed-commit, reset-snapshot, clear ordering still fails and is recorded for follow-up. The linker emitted a nonfatal unwind-table warning.
The runtime-contract checker invokes Cargo by default, so do not list it as a seconds-long drift check. Describe receipt reuse and the required locked SSD build workflow.
Verified against scripts/check-runtime-contract-budget.py and scripts/measure-runtime-contract.py; diff check passed.
Replace the fake 900k bill on a tiny first prompt with a growing transcript and request-sized billing. Keep the existing assertions for compaction, checkpoint continuation, complete transcript, accounting and a billed request above the former 100k limit.
On frozen e636d71ad8 plus this fixture, the existing worker compaction case passed 1/1; 2,759 inputs remained unchanged. The first compile attempt had a u32/u64 mismatch, corrected before this run. The linker reported a nonfatal unwind-table size warning.
The home card also contains the word resume, so wait for the dialog buttons and for cancellation to dismiss it before the next interaction.
Frozen e908528439 plus this fixture change: three existing long-running terminal cases passed across all five sizes using the explicitly built CLI. The original target failed 0/1 with a missing cancel target before this change.
Use the resolved threshold even when it is zero, keeping the inspector aligned with the effective trigger.
The existing inspector_rows_name_compaction_and_anchors check passed on the frozen mixed source at 57e957f186, including the zero-threshold assertion.
Retain sentinel and consent assertions while expecting schema 4 and notice 6 introduced in e908f3f736.
Frozen mixed source at 57e957f186: TUI telemetry sentinel passed against the explicitly built CLI, and the CLI default-on test passed 1/1. The initial CLI test failed with schema 4 versus 3; the original TUI failure used an older binary. No production telemetry policy changed.
Skip credential inspection when an explicit route rules out Ollama adoption. For diagnostic adoption, resolve credential presence through the existing read-only path; exec keeps its ordinary resolver. Preserve the legacy secret store on both configured and unconfigured routes.
Refs #7115 (CW-7583). Frozen mixed tree at 57e957f186: explicitly built CLI SHA256 35de7b5ec4e7a3f3191fd5770384c2a09fee357aec59ab30bc8f7bb07ca40ad3; the existing diagnostic case passed with configured loopback and unconfigured legacy-key scenarios; 9 local Ollama cases and one route diagnostic case passed. Format and diff checks passed. A previous failing rerun used a stale CLI binary and is not proof against this change; real Ollama and release qualification remain open.
Preserve the admitted 0.10.3 scope and crate-size targets while repairing joined words and dates. Existing documentation check passed for 25 topics; diff check passed.
Offer Runcrate through the existing OpenAI-compatible descriptor, picker and named provider path. Keep billing unclassified until a reviewed price and product are available. The issue reporter disclosed working on Runcrate.
Refs #7085 (CW-7532). Vendor public model listing returned the three documented examples without credentials; no metered call was made. On frozen mixed source, 8 existing descriptor cases, 4 picker cases, 1 pricing coverage case, and 2 feature registry cases passed; web facts 30 passed, provider registry and docs checks passed. Live inference and release qualification remain unverified. Inherited source prepared by Claude Code and independently reviewed.
Use the current route threshold in the context inspector and show automatic compaction as off when disabled. Correct the Work rail guidance to point to manual /compact instead of claiming emergency compaction remains enabled.
Refs #7133 (CW-7640). On frozen f20a0d7144 plus 13 inventoried dirty paths, 18 existing inspector, rendered context rail and route-window cases passed; format and diff checks passed. The reported Windows trigger has not been reproduced.
Preserve the same owned global root classification while satisfying the existing lint.
On the preceding frozen mixed tree, codewhale-skills lib: 182 passed; targeted skills/localization clippy passed. The skills source hash remains unchanged since that run; no workspace clippy or release gate is claimed.
Refresh existing event and prompt goldens for the landed write line-ending schema and update the status and legacy effort expectations for the current auto default. Correct the stale fallback documentation.
The prior frozen mixed-tree conformance verification ran 33 existing cases: 33 passed. Current frozen mixed-tree nextest covered the status and effort baselines: 2 passed. Format and diff checks passed. These fixtures do not establish provider or release qualification.
Allow retry after a deletion tombstone while retaining the legacy single-slot checkpoint, which older binaries can write outside the per-session lock. The existing lifecycle case now checks its bytes remain intact and the retired checkpoint stays hidden.
Refs #7095 (CW-7553). On frozen 896e781fbb plus 29 inventoried dirty paths, 33 existing checkpoint, deletion, retention and late-usage cases passed; rustfmt and diff check passed. This does not reproduce the original Deepin report or qualify older concurrent binaries.
Use the shared locale catalog for grouped activity, tool status and file mutation receipts. Rebuild cached activity summaries when the UI locale changes and retain raw paths and diffs.
Refs #7071 (CW-7527). On frozen main 9fc5c898c7 plus 62 inventoried dirty paths: TUI lib and feature registry compiled; 197 focused nextest cases passed, including locale cache and receipt cases. Localization lib: 54 passed on unchanged localization inputs in the preceding frozen run. All 15 packs have 2732 keys; diff check and changelog sync passed. Earlier broader mixed-tree run had 2118 passed and one unrelated compaction failure. No native-speaker, real-provider, Windows, installed-artifact or release CI qualification is claimed.
Wrapped rows of a blockquote now keep the quote rail on every row and report a zero copy prefix, because the transcript rail scan already strips each leading rail span.
Contributed by @Lstarsky0 in #7144. Reviewed against compute_rail_prefix_width, which accumulates leading rail spans on every row, so selection copy stays correct; the diff applies cleanly to main with no drift in markdown_render.rs. Required checks Lint, Test (windows-latest) and npm wrapper smoke were green on the PR head; they were not re-run on current main and no local test run covers this merge.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Refs CW-6129 / CW-7525. Remove the superseded instruction to freeze or
retire the native App and extract toward the deleted Tauri host. Keep
GPUI/weGPUI native and browser ownership distinct. Replace stale execution
status, the old release ladder and historical RS-14 choices with the
existing Linear owner and current structural dependency order. Recognize
the actual runtime config and skills owners; preserve the full0.10.3
scope,50k ceiling, measurement distinctions and authority boundaries.
Make sandbox isolation a future qualification target rather than a local
checkout/build-cache exception. Preserve the architectural map and dated
reference evidence; link the old goal audit in Git instead of presenting
its old source findings as current.
Verified: changed document local-link scan0missing, docs25topics, generated
facts and git diff checks pass. Derivation produced no metadata drift.
One documentation file only; no Rust/runtime changes or new tests/comments.
No native rebuild, CI, provider, installed-device or release claim.
Refs CW-7038 / #6866. Thanks @asto18089 for identifying the missing
failure and recovery signal. This independently extends the existing
configured-server note rather than harvesting the fork implementation.
Replace the two Engine captures with one nonblocking request-boundary
capture. Project fixed connection states from the pool, retain exact turn
ceilings and reviewed authority, and prioritize auth/failure rows within
the existing bounds. Connected is an observation, not permission or a
promise that tools will work. Raw errors and server-authored reasons do
not reach the status note. Withdraw stale status and guidance after
contention, config/scope changes and restore. Keep existing event kinds,
log deduplication, replay and cached-prefix behavior; no new prompt/store.
Migrate both discovery-policy callers to one captured filter.
Verified on frozen base2231d73df5 plus16paths: MCP owner2/2; current
TUI lib and feature_registry compilation20m28;42distinct affected host
cases resolved (41passed initially, feature description corrected from
132 to90characters, affected registry1/1passed). Website metadata30/30,
docs25topics, format, module/command boundaries, blocking/dead-code
ratchets, facts and diff checks passed. Existing test bodies expanded;
no new test functions. Earlier compile failures and source freezes remain
in artifacts/codex-0103-resume-20261029.
Existing oversized eh_frame test-link warning remains. No full suite,
hosted CI, provider/auth actions, installed/device or release proof.
Implement #7066 with nested auto/all/none, selected, disabled and explicit_only
policy. Canonical aliases share policy; filtering precedes first-wins merge so
compatible/manual alternatives remain available. Reuse invocation and activation
state rather than adding another store. All writes remain in the owned global
root, use confined atomic replacement and preserve edited/managed bodies. None
leaves bundle files/marker untouched. Remove legacy skill-directory deletion;
refuse nonregular markers without deleting their contents.
Carry one scoped SkillsConfig through Engine, App, children, tools, runtime API
and MCP resources; retire mode-only production discovery/render entry points.
Doctor and skills inspection report actual policy, paths and reasons. Escape
configured-name controls; document profile clears and CODEWHALE_HOME behavior.
Migrate the existing manager fixture to shared registered environment guards.
Verification: 182 distinct skills cases resolved across scoped reruns; runtime
config2 and command-contract11 pass. Host selection466 had464 pass/2 fixture
failures; final affected manager/inspect/registry10 pass resolves both, without
repeating unaffected cases. Current TUI lib+registry compilation passed; actual
production CLI build13m40 passed. Sandboxed offline doctor JSON/text observed
none mode, generation17, explicit-only PDF, compatible debug fallback, unknown
and ignored names, and byte-identical bundle files/marker. Website metadata30,
docs, format, boundaries, blocking/dead-code ratchets and diff checks passed.
No new test functions. Existing oversized eh_frame warning retained. No full or
hosted CI, provider/device/Windows/release qualification. Full0.10.3 remains open.
The normal pressure path honored compaction.enabled, but emergency
preflight/provider recovery forcibly enabled summaries. Refuse that path
before a model call, start event or history replacement when automatic
compaction is off. Preserve the original conversation and report an
actionable typed context error through the existing audit/error owners.
Share recovery-failure reporting between preflight and provider-error
handling so a rejected summary request retains its actual cause. Keep
cancellation ahead of failure reporting after the awaited recovery.
Explicit manual compaction is unchanged. Expand/rename the existing
emergency fixture for disabled-but-compactable and enabled-but-unnecessary
history, zero model calls/attempts, unchanged messages and one refusal.
Document the contract, synchronize embedded release notes, and replace
private CW citations in the unreleased public changelog with verified
public refs where available. Published historical sections are unchanged.
Local proof: initial libtest compile2m02 and137 focused compaction/error/
Engine checks passed. After the final model-step cancellation guard,
libtest recompiled in1m30 and69 affected Engine/compaction/recovery/
cancellation checks passed; unchanged pure-check evidence was retained.
Both builds had the existing oversized eh_frame linker warning. No new
test functions or code comments. Format, runtime boundary, blocking569sites
in169files, dead-code164, changelog synchronization, generated facts and
diff checks passed. Exact seven-path freeze remained intact.
This fixes the setting bypass, not every reported Windows journal seam.
Keep CW-7644/#7142 open for attribution and remaining continuity/lifecycle
acceptance. No real-provider, hosted CI, installed/device or release proof.
Refs CW-7644, #7142
Move skill discovery, provenance, mutation and bundled content to
codewhale-skills and migrate every consumer without a legacy facade. Keep
native selection/lifetime checks in the existing host manager and refuse
native authority when no host bridge is installed. Preserve bundled bytes,
reviewed snapshot identity, workspace trust and the Engine decision mint.
Add validated skills.listing_max_description_chars and
skills.listing_budget_fraction with independent profile inheritance. Keep
the 400-character/5-percent defaults and existing total budget bounds. Use
the same renderer for parent and fresh/nested child catalogues, context
loss notices and localized inspect notes. Remove the unbounded child-only
catalogue. Update all 15 locale notes, current en/zh docs, release order,
test selectors and generated facts; correct the old bundled Feishu claim.
Local validation on frozen source: skills182, runtime config2,
localization54 and command-contract11 passed. TUI lib and registry compiled
in21m56s (existing oversized eh_frame warning). The scoped integration had
277 passes and one metadata-summary-length failure; after the docs-only
correction both registry cases passed, covering278 distinct selected
TUI/registry cases. Final generated metadata30 passed. Existing fixtures
now cover configured80/400/1024 caps, fraction/profile validation, localized
notes, authority-bound child catalogues and real host startup for restored
native receipts. No new test functions or code comments were added.
Workspace format, command/runtime boundaries, blocking budget569sites in
169files, dead-code budget164, actual Cargo publication order, package list
(all62 skill assets) and diff checks passed. Only five non-Rust metadata/doc
paths changed after native compilation, recorded separately; native inputs
remain identical. No full-suite, hosted CI, real-provider/device or release
qualification. CW-7521 bundled policy and full decomposition remain open.
Refs CW-6129, CW-7531, #7073
A paragraph indented under a list item, such as a bullet's second
paragraph or the note after a numbered step's code block, rendered
flush left: the word wrapper drops spaces at the start of a row. The
item then looks finished and the paragraph reads as top-level text.
The parser now carries whether a list is open: a list item opens it,
blank lines, code and indented paragraphs keep it open, and anything
else closes it. A paragraph keeps its indent only while a list is open,
so indented rows of command notes such as /status and /cost render as
before. The decision is made at parse time because streaming renders
the text between code blocks separately, so a look-back in the renderer
would not match the final render.
The kept indent is drawn on every row, capped at half the width like a
list item's. As with list items, the first row copies with its indent
and wrapped rows strip it.
Signed-off-by: Lstarsky0 <[email protected]>
CW-6129 / CW-7525: integrate the reconciled S35, S52, N13 and S24p1
cuts. Separate thread storage from manager/history execution, move terminal
rendering helpers into codewhale-tui-kit, move error taxonomy into its own
crate while retaining the LLM adapter, and place hand-priced model metadata
in codewhale-models. Migrate direct consumers and release metadata; preserve
newer Vim/reasoning, delete/parent-thread and plugin-catalog behavior.
Reconcile the existing compaction fixture with the accepted fail-closed
cutover: an invalid summary stops once and preserves the original task,
earlier text and tool results. Allow Core startup in the existing queued
child fixture while keeping its original counterexample (500ms remaining
under the old shared deadline versus a 900ms response). Runtime budgets are
unchanged. Move two existing pure pricing checks; add no tests.
Remove the obsolete multi-release deconstruction deferral, refresh generated
source documentation, and tighten dead-code suppression to 164. The new
uninstall safety sites run synchronously before CLI runtime creation; record
only their measured 6 filesystem/8 canonicalization sites. Thread-manager
canonicalization allowance transfers with a net reduction.
Verification: current TUI lib and feature_registry targets compile; the test
linker retains its existing oversized eh_frame warning. New crates 242 pass;
models 108 pass; runtime API/thread lifecycle 759 pass; compatibility 205 pass
before its obsolete compaction expectation was corrected; final pricing,
catalog, corrected compaction/child fixtures and registry 113 pass. Generated
web facts 30 pass and match source. Format, command/runtime boundaries,
publication order, blocking-call budget and diff checks pass. All 34 named
pricing items and cache-write policy retain their prior implementations.
No full-suite, hosted CI, live-provider or release qualification is claimed.
TUI remains 578280 strict production lines, so full decomposition stays open.
CW-7639: replace vulnerable MSAL Node 6.0.1 with 7.1.2 through an
Azure Identity scoped override. Keep VSCE on its current supported route;
materialize the lockfile and remove the obsolete advisory acceptance from
the audit classifier and dependency-security documentation.
Verification: 47 existing extension checks passed; npm audit reported zero
vulnerabilities; replaying the old advisory is blocked. Credential-free
local VSIX packaging passed (30 files; no development dependencies).
Installed dependency graph resolves MSAL Node 7.1.2. No publishing or live
authentication was performed; no exploit or affected invocation is claimed.
Complete the interrupted coordinator batch: three visible approval choices with default Deny, exact-rule preview gating and Details-gated hidden session/ask shortcuts; separate everyday CLI help, doctor --all, recent-session Up navigation and transcript-free cameos; localize the resume hint and update the existing glossary/feature rows. Add session.window.rerooted diagnostics while retaining the previous journal branch. This event is not a root-cause or seam-summary fix for #7142.
Verification: 398 existing approval cases passed, then the existing obsolete hidden-a expectation was corrected and its replacement passed (399 distinct cases covered). Feature registry 2 passed; UI launch 59 passed/1 intentionally ignored render fixture; cameo 3 passed; doctor-filter checks 142 passed. Prior localization 54 and session_tree 22 passes were reused after verifying their 17 input hashes unchanged. Current CLI build and CLI all-target/all-feature Clippy with the repository CI allowlist passed. Isolated help/advanced-help/uninstall-help and doctor compact/all runs exited 0; doctor showed 219 versus 232 lines. No new tests or hosted CI dispatch.
Remaining: the broader launch filter found late_launch_permit_still_gets_the_full_work_budget failing with BudgetExhausted; it also fails alone. Its runtime path is untouched here and remains the next integrity investigation, not a release pass. Full 0.10.3 implementation, provider/platform qualification and final validation remain open.
Refuse managed/system, symlinked, non-owned/shared-writable and Cargo-owned destinations before mkdir in both Unix installers; inspect receipt metadata through the real parent even when bin does not exist. Align en/zh uninstall guidance with explicit-home and receipt ownership. Finish the inherited uninstall closure/type lint repair. Refs #7091.
Verification: 38 selected existing installer/guide checks passed; the 11 guide checks passed again after the final wording correction. Two old-source controls created forbidden directories; ten disposable refusal cases now create no destination. The applied scripts match those rehearsed bytes. Current CLI passed Clippy for all CLI targets/features with the existing ci.yml allowlist and built successfully. Four sandboxed current-binary uninstall rehearsals passed: ordinary removal, confirmed purge, managed refusal, hard-link protection. The sandbox denied network and real user-home file contents. Windows host behavior and release qualification remain open.
CI's Lint job runs `cargo fmt --all -- --check` before clippy and failed on these two files
(committed unformatted in 2166fc24cb and 1f0c4d0daa). Formatting only, no behavior change.
Verified: `cargo fmt --all -- --check` exits 0 on the full working tree after this change.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Usage reporting adds two aggregate events per session: model_usage (provider,
catalog model or "other", turns by outcome, input/output/cache-read/cache-write
tokens, turn-time buckets) and tool_usage (first-party tool name, or "mcp"/"other",
with calls and errors). Models are named only when in the bundled public
models.dev catalog; tool names come from a closed list. No content, arguments or
paths. CI and GitHub Action runs report the "ci" session source. DO_NOT_TRACK=1
turns reporting off like CODEWHALE_TELEMETRY=0. The notice bump re-shows the
disclosure once in all 15 locales. The ingest accepts v4 and still accepts
v3/notice 5; docs/TELEMETRY.md describes both events.
Re-applied from recovered commit a20702df43 (built on the S57 split) onto main
78580d3d4d without conflicts. Founder approved for 0.10.3 (D14).
DEPLOY ORDER: the live ingest rejects schema 4. Deploy the ingest before any
Engine release that sends v4. Nothing deployed here.
Verified on the frozen tree (artifacts/claude-0103-build-20261029-0418-telemetry/):
codewhale-telemetry 53/53, codewhale-localization 54/54, codewhale-config
telemetry 17/17, codewhale-tui lib telemetry 40/40, setup_state 11/11,
telemetry-ingest vitest 174/174 and tsc --noEmit clean. Not re-run on this tree:
the real-turn exec --auto into wrangler dev and the DO_NOT_TRACK zero-request run
reported on the original commit.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Canonicalizes home/install paths and dot spellings, rejects a purge target that
contains the home or install directory, keeps ownership and symlink guards,
names recovery/snapshot/shared-data consequences, uses a literal
environment-bound PowerShell path for deferred Windows deletion and canonicalizes
both sides of the Windows PATH-block match. INSTALL.md's go-back-further recipe
now moves the working binaries aside and restores them if the install fails.
Verified: macOS sandboxed probe on a stripped copy of the frozen-tree CLI
(sha256 d28274cb...), 4 cases pass (plain, purge, managed, hardlink); receipt in
artifacts/claude-uninstall-probe-0415/probes/receipt.json. NOT verified: Linux and
Windows execution, package-manager receipts, installer refusal rules in
scripts/release/install.sh, zh-Hans section 12 still suggesting rm.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Shortens file tool descriptions and reuses the line_ending schema in WriteTool
(#7070), copies InteractionTarget, scopes test-only imports in app-server, and
updates the /cost [details] diagnostics fixture (requires_argument now true).
Verified: tools::file 191/191, frame:: 37/37, surface_metadata 1/1. app-server
changes are compile-checked only by the in-progress CLI build.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Default for ReasoningEffort and the API-key/config writers moves to auto; docs and
config.example.toml follow. Explicit user effort is untouched.
Verified: reasoning_preference 8/8, model_routing 45/45. Fresh-config first-run
writer path not exercised end to end.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Moves the startup-route test to local_ollama::startup_route_configured (deleting
the copy in tui/app/init.rs) and uses it so exec, and doctor --probe-local, pick
a live local Ollama model when no cloud key or explicit route exists, saving
nothing. doctor reports "Not confirmed" without the probe.
Verified: compiled in the frozen tree; no fresh-state exec/doctor journey run
yet against a real Ollama server (#5848 stays open).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds the bounded git child_process shim (UTF-8 default, metadata-only Git,
preserved denial codes, nonzero exit and ABORT_ERR/maxBuffer error shapes),
the git_review_scope canonical action and read-only authority category, policy
v8, and explicit composition-review setup in the four DSH/skill import fixtures
that depended on a process-global reviewer.
Verified on the frozen current tree (Engine 81ae174537 + dirty inputs): Node
host 413 pass/7 skip earlier; tui lib plugins_dsh_tests 30/30, plugins_skill_tests
5/5, composition 8/8, tools::git 76/76, feature_registry 2/2. NOT verified: the
published @michengai/dsh-code-review loopback journey, cancel parity and the
sandbox-escape matrix; those remain open under CW-7055.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Negotiate structured review v2 with complete bounded source files, explicit omissions, prior findings and discussion on the existing Engine review path. Preserve v1 fingerprints. Validate path, revision, text and identity bounds before the model call; bind the result to the exact request and head/base. Keep only traced hosted findings and require a current included head-source trace for each explicit prior-finding resolution.
Advertise structured_review_version=2 and bound requested reasoning to the exact route and visible output budget. The hosted review retains one step with no execution tools; it cannot claim reproduction. No new turn loop, account store or provider fallback is added.
Verification: TUI lib-test binary compiled successfully; all six Review source hashes stayed unchanged. Explicit existing filters passed 9 review-core and 38 review-tool checks. Current protocol crate passed 78 existing checks. The original structured_review filter selected zero tests and is compile proof only. The linker warned that the unwind section exceeded its compact-table limit.
Concurrent compaction/Ollama and fixture edits invalidate a current whole-tree claim; their exact drift and the compile inputs are recorded in artifacts/codewhale-review-build-20261029/engine-review-compile-receipt.json. No full-tree, v2 provider rehearsal, hosted CI, release or deployment qualification is claimed. This commit must stay local until the release coordinator verifies its pending predecessor before pushing main.
Adapts the DSH/Pi compaction shape inside the existing Engine owner: prune bulky
tool output first, keep a token-priced raw tail cut on complete tool exchanges,
summarize the older span plus prior checkpoint, save original history before
commit. The summary validator now strips leaked think/reasoning spans (including
unmatched tags) before the placeholder check, so a bare closing tag no longer
passes as a summary. The lexical number/backtick checker and the duplicate
TypeScript survival validator are removed; one Rust validator remains. Turn
calibration learns only upward from reported usage and a failed automatic
summary stops the turn instead of looping.
Verification: NOT compiled or tested in this tree (uncompiled batch; build owner
is the Review peer's in-flight tui build). Local commit only, not pushed. Known
open: whole-conversation fallback when no older span exists.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Ollama model publication stays with its capability-aware native refresh; generic key verification cannot overwrite it with embedding rows. This prevents new contamination and does not claim repair of a previously persisted bad cache. Refs #7116.
Validation: the frozen TUI test target compiled; existing UI/provider/reasoning filter union: test result: ok. 233 passed; 0 failed; 13703 filtered out. This includes Work graph, session rewind, remote control and provider-key roster checks. The separate compaction group has one old header-text expectation to update (187 passed / 1 failed); this change does not touch that behavior. No hosted CI, live provider or release qualification.
A repeated start during connecting or connected reports a separate toast instead of replacing the current authorization banner. Refs #7127.
Validation: the frozen TUI test target compiled; existing UI/provider/reasoning filter union: test result: ok. 233 passed; 0 failed; 13703 filtered out. This includes Work graph, session rewind, remote control and provider-key roster checks. The separate compaction group has one old header-text expectation to update (187 passed / 1 failed); this change does not touch that behavior. No hosted CI, live provider or release qualification.
Keep the conversation rewind explanation when its persistence or Work update fails, and say a retry was not sent only when one was pending.
Validation: the frozen TUI test target compiled; existing UI/provider/reasoning filter union: test result: ok. 233 passed; 0 failed; 13703 filtered out. This includes Work graph, session rewind, remote control and provider-key roster checks. The separate compaction group has one old header-text expectation to update (187 passed / 1 failed); this change does not touch that behavior. No hosted CI, live provider or release qualification.
Translate the self-serve review-bot setup row in all 18 README packs and refresh their source stamps.
Validation: python3 scripts/check-readme-translations.py on the current-main merge tree: README translation check OK — 18 translations in sync with README.md (sha256:0430d94f3d47). Reviewed current-head changes, review threads and dependent PRs. No hosted CI dispatch or release qualification.
Both analysis and isolated publisher now resolve c2a4872328. Existing CLI v0.10.2/model/reasoning compatibility and selected-repository installation are unchanged. Validation: actionlint and diff whitespace checks passed. Reuses the 8 passing Action checks and actual-PR offline replay recorded in the carrying commit; no new live inference, GitHub review, hosted CI or release. Refs CW-6869 and CW-7503.
c962177c7 rewrote the 'codewhale review' table row in README.md but left
all 18 translations on the old wording, missing link and stale stamps,
so scripts/check-readme-translations.py fails on main. Translate the new
row in every pack (linking the anchor that commit added) and bump the
stamps.
Signed-off-by: Boyce <[email protected]>
The live mention review on PR 7126 published two suspected hypotheses as concerns despite recording that surrounding code was unavailable. The separate publisher now requires reproduced/traced evidence with nonempty detail, requires introduced authorship for inline comments, and collapses pre-existing findings. Model summaries, risk assessments and unlinked suggestions stay in the analysis artifact so they cannot repeat a withheld allegation. An empty result remains advisory and links to analysis/coverage. This is an evidence-record boundary, not independent reproduction or proof of accuracy; the Engine remains the review authority.
Validation: existing Action checks 8 passed, 0 failed; Node syntax checks and diff whitespace check passed. Offline replay of actual run 40835017974 artifact withheld both suspected claims and emitted zero inline comments. Direct local variations covered empty/missing evidence, introduced/preexisting/unknown attribution, escaping and deduplication; a mocked publisher run reported 0 eligible and 2 withheld findings without network/provider calls or GitHub writes. No new tests, Rust build or hosted CI. Legacy post:true retains its pinned CLI policy. Refs CW-7503.
The notices printed for a foreign-workspace checkpoint tell people to run `codewhale sessions`, but the command listed only saved sessions and so never showed a session that an interrupted turn had advanced past its last save. It now uses SessionManager::list_sessions_with_recovery, the call the resume picker already uses: a session with a newer checkpoint, or only a checkpoint, appears with that checkpoint's message count and an `interrupted` mark, and a recovery warning goes to stderr instead of failing the listing. `--search` still searches saved sessions only, and `codewhale resume` without an id still numbers saved sessions only; both are unchanged.
Validation: Linux Boat VM, debug build of a stack of origin/main 4d600eaaee plus the Work Graph change, #7126/#7130/#7127/#7121 and the uninstall slice, then this patch (sha256 d8dbb185…), with a loopback mock model inside an unshare -rn network namespace and a synthetic home. One saved session and one session killed by SIGTERM mid-stream: before, `sessions` showed the interrupted one as "New Session | 0 msgs"; after, "New Session | 1 msgs | interrupted"; `--limit 1` shows the newest row and "1 more session(s)"; `sessions list --limit 5` matches; cargo fmt --all -- --check clean. The row keeps the saved title, so it still reads "New Session". Not run: a session that exists only as a checkpoint (no saved file), the lib test suite (no test covers fn list_sessions in lib.rs), Windows, macOS.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
crates.io codewhale-ratatui 0.1.2 (published 2026-10-13) has no Accent role, but codewhale-tui uses codewhale_ratatui::Role::Accent since the design-tokens 2.3.0 move, and the vendored copy changed after 0.1.2 went out. A registry build of codewhale-tui, and the crates.io dry run that verifies it, cannot compile against 0.1.2. The vendored crate is now 0.1.3 and crates/tui, pet/tui, both workspace locks, the vendored crate's own lock and the check-versions.sh literal pin it. Publishing 0.1.3 from vendor/codewhale-ratatui stays a release-time step that must precede the codewhale-tui dry run; this commit publishes nothing.
Validation: cargo metadata --locked --offline succeeds in the workspace root, pet/tui and vendor/codewhale-ratatui; scripts/release/check-versions.sh and scripts/sync-changelog.sh --check pass. Not run: the crates.io dry run (needs 0.1.3 published first), a compile.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Adds the Unreleased entries for #7126/#7130 (vim Normal mode), #7127 (rc start failures), #7121 (undo localization and its sync guard), #7140 (TSX language id), the Work Graph publication fence (#7118, with its open limit stated) and the #7095 session entries: the status-line hint, retained and listed interrupted checkpoints, and the signal-exit flush (#7122). Credits @Ricardo-M-L on docs/CONTRIBUTORS.md and web/lib/release-credits.ts and extends the @Lstarsky0, @laozidabo and @Aboysky lines. crates/tui/CHANGELOG.md is regenerated by scripts/sync-changelog.sh. The three web/ generated files were stale at HEAD (missing crates account-cli and plugins, two features, and the GUIDE.md digests that #7127 changed); they are regenerated from HEAD plus only these edits in a scratch export, so they carry none of the uncommitted DSH work.
Validation: sync-changelog --check, check-contributor-credit.py v0.10.2 (9 contributors, all credited on all three surfaces), check-versions.sh and check-facts.mjs pass on the scratch export; check-facts.mjs was red at HEAD before this. Not run: web build or hosted CI.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Follow-up to #7121. /undo restores the files and truncates the App history before the portable handler sees the Restored outcome, and the Engine only learns about it through the SyncSession action that handler returns. The localized summary was built with ? on eight catalog lookups before that action, so a catalog that lacked one key turned a completed restore into a plain error result with no action, leaving the Engine's model context ahead of the files and history. The report is now built by restored_summary, and when it fails restored_fallback writes an English report from the outcome fields alone; is_error stays false and the SyncSession action is always returned. The shipped catalogs contain all 29 keys, so this is a guard, not a fix for a shipped failure.
Validation: codewhale-commands unit tests in a Git-free export on a Mac. The existing undo_reports_the_request_each_file_and_the_conversation, extended with a presentation context that fails one key, fails against the unrepaired code (the result was "Error: catalog lacks cmd_undo_action_restored" with no action) and passes after: 28 passed, 0 failed for the undo and debug filters. cargo fmt --all -- --check clean. Not run: the codewhale-tui lib tests that call the command, hosted CI.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Follow-up to #7126. The held-key flush runs after the event that triggered it has already drawn, so a Normal-mode key that changes the composer without setting needs_redraw (history recall with k, for one) left the old text on screen until the next terminal event. The flush now marks the frame dirty after dispatching the key.
Validation: Linux tmux PTY replay of a stack build (origin/main 4d600eaaee plus the Work Graph packet, #7126, #7130, #7127, #7121 and this change) with composer_vim_mode = "vim" and a seeded two-entry history, TERM_PROGRAM unset and iTerm.app: after Esc and k the composer showed the second seeded prompt at +150 ms and again after 1.15 s idle, k k showed the first, j the second again; i hello world Esc 0 x w x d d emptied the draft at 120 ms and 30 ms spacing; G moved to the end of the line; v q G Esc A left the draft unchanged. A single-write burst of Normal keys on an untrusted terminal is still inserted as text, as #7126 discloses. Not shown: the stale frame without this line (no #7126-only binary was built; the claim rests on history_up not setting needs_redraw). Not run: Windows, macOS terminals.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Refs #7118. A save, fork, rename, title change or conversation rewind used to publish whichever Work Graph transaction was pending, whatever session it belonged to and whether or not a newer graph had arrived after the snapshot was built. Publication now carries the saved session's owner and graph (SavedWorkProjection) and the Work runtime refuses it, leaving the transaction pending for the next save, when either no longer matches (publish_pending_matching and publish_pending_sync_matching in work_graph/runtime.rs; the unconditional publish remains only under cfg(test)). Fork reports that parent and child were saved but the active session did not change when Work could not be published. A conversation rewind that applied but could not be saved or published now says so ("Conversation was rewound, but its save or Work update needs attention"), puts the replacement or retry prompt back where the user can copy it, and does not send the retry.
Does not close#7118: the reporter's cancelled-turn case and the /task list versus To-do tracker visibility mismatch are neither reproduced nor changed. Known limits from three independent read-only reviews, none a blocker: the Engine SyncSession checkpoint branch still fails the request after the checkpoint was queued when the Work projection errors (its message says so); build_session_snapshot can still fall back to the last known Work state under lock contention, which the refused publish now reports after the write; a busy To-do lock still fails publication even with nothing pending; the partial-success toast says "retry was not sent" for a plain /undo and the new strings are not in the locale catalog.
Validation: Git-free export of origin/main 4d600eaaee plus this change, #7126, #7130, #7127, #7121 and the #7126 redraw follow-up, built and run on a Mac. cargo fmt --all -- --check clean. codewhale-tui lib focused run 281 passed, 0 failed (work_graph:: 43, session_snapshot_ 11, conversation_undo 3, undo_retry_apply_path 1, remote_control 90, phase_strip 43, paste 74, vim 3, composer_history 15) with RUST_MIN_STACK=16 MiB, the value CI and scripts/dev-test.sh export; at the default 2 MiB test stack the isolated undo lifecycle test overflows its stack in a debug build. Not run: the rest of the lib suite, Windows, hosted CI.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Legal JSX in App.tsx is rejected when typescript-language-server explicitly selects ScriptKind.TS from the existing typescript didOpen languageId. Preserve one transport and configured custom IDs; select typescriptreact only for TSX documents on the built-in TypeScript ID. Verified actual TypeScript 5.9.3 parse behavior; existing LSP suite 64 passed, 0 failed, scoped clippy and workspace fmt passed. No new tests or comments per repository guidance. Developed with Codex assistance.
The five `always_load entry ... matches no tool` rows came from saved config naming the built-in Computer Use bundle removed in 4cf2d2cfff, and the text claimed the plugin-N prefix 'renumbers' when it is the plugin-name length. Entries with the Computer Use prefix now produce one status derived from the existing plugin registry (not installed, needs review, installed but off, enabled but cannot start, or entries the plugin no longer offers, with left_click now click); other misses are one grouped line. Nothing is installed, trusted, enabled or executed. 'MCP server guidance added/withdrawn' statuses are Internal so the app collapses them.
Verified from a frozen git-archive export at 4d600eaaee plus only these two files: cargo check -p codewhale-tui exit 0, 0 warnings; focused tests status_visibility/always_load/should_default_defer 3/3 and always_load/deferral catalog 4/4 passed. Not run: full suite, hosted CI, installed-app replay.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The #7091 uninstall slice (cce6d3a84d) resolves the selected executable and each profile's parent with `canonicalize` and replaced seven `std::fs` calls with guarded no-follow opens, so crates/cli/src/update/uninstall.rs now has 5 path_canonicalize sites (budget 3) and 5 std_fs sites (budget 7). `codewhale uninstall` is a synchronous CLI command that runs before any async runtime exists, so these cannot move into spawn_blocking; the ratchet is regenerated with `python3 scripts/check-blocking-calls-budget.py --update` and changes exactly those two lines.
Validation: `python3 scripts/check-blocking-calls-budget.py` failed before this change (path_canonicalize sites 5 > budget 3) and reports "566 sites across 168 files, within budget" after it; the regenerated JSON differs from main only in the two uninstall.rs counters (diffed on a clean git-archive export).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Use the committed generator for the Codewhale Agent workflow. Installation access is checked before provider inference; missing installation cannot consume model tokens. Provider remains explicit DeepSeek, model deepseek-flash, reasoning off for checksummed v0.10.2 compatibility. Local validation: actionlint and whitespace checks pass. The last provider review completed cleanly on PR 7130; the App installation grant and full publication/mention round trip await the pending founder confirmation.
Live run 40832077629 completed a full DeepSeek review (4387 input, 544 output, zero reasoning tokens), then correctly stopped publication because the existing App was not installed on codewhale-hq/Codewhale. Generated App workflows now validate installation in a separate credential-isolated job before admitting model usage. Reuse one installation-token step template; the analysis job still receives only a read-only GitHub token and its selected provider key. Local verification: actionlint passes for both App and default-token generated workflows; Node syntax and whitespace checks pass. Existing review runtime source/evidence is unchanged. Installation approval is pending; no App permission was granted by this source commit.
Generate the workflow with checksummed CLI v0.10.2, deepseek-flash, reasoning off, and Action source 9032b4f931. The installed released CLI was exercised offline and confirmed that it loads reasoning_effort=off from the isolated run config. actionlint passes. This source pin includes the explicit reasoning input; no candidate source is executed.
The installed v0.10.2 binary identifies source 3400ebb5c9, whose review triage caps small reviews at 4000 output tokens regardless of the environment ceiling. Live runs 40830663408 and 40831324211 exhausted all 4000 tokens as reasoning and correctly published nothing. Current Engine source already removes that fixed cap, but unreleased source is not a substitute for a checksummed release. Add a validated reasoning-effort input, applied through a fresh secret-free isolated Engine configuration. Generated workflows default to off for this released compatibility boundary; users can select an explicit level or auto. Local verification: existing Action tests 8 passed, 0 failed; syntax, generated workflow actionlint and whitespace checks pass. No new tests or code comments; next live acceptance is on the newly pinned source.
Pin analysis and publication to f4c2ce3a6e, including the reasoning-output allowance and explicit incomplete receipts. Workflow is generated from the committed setup tool and passes actionlint. This is the frozen candidate for the authorized live review acceptance.
Live run 40830663408 reached DeepSeek but used all 4000 automatically allowed output tokens and returned no review. Publication was skipped. Generate an explicit 16384-token allowance, configurable with --max-output-tokens, and retain sanitized reasoning counts. Recognize the released CLI infrastructure/not_produced envelope as incomplete without logging raw provider/model errors. Local verification: existing review checks 8 passed, 0 failed; direct failure-envelope classification and actionlint pass. The running workflow still references the prior reviewed Action commit; the next source pin update carries this repair.
Use the existing Codewhale Agent installation and DeepSeek repository secret with deepseek-flash and checksummed CLI v0.10.2. Both Codewhale actions are pinned to c962177c79. The write-capable App token is minted only in the publisher job. Enable same-repository automatic reviews plus current-write-authorized review mentions and default-branch manual fork analysis. Local validation: generated from the committed setup tool; actionlint and whitespace checks pass. Founder authorized completing GitHub activation and live review acceptance in chat on 2026-10-28; live results are recorded separately.
Generate a workflow with immutable Action refs, exact release/model selection,
separate read-only analysis and credential-isolated publication, and optional
existing GitHub App identity. Reuse the Engine review command and installer.
Accept exact review mentions from current write/maintain/admin users; recheck
original and rerunning actors, comment identity/body and current revisions.
Opt-in inert fork diffs only through trusted default-branch manual or issue
comment triggers. Mentions require the separate publisher. No candidate code,
hooks or project configuration is executed. Share provider-key metadata with
the generator and redact every supported key, including OrcaRouter.
Local verification: existing node review tests 8 passed, 0 failed; Node syntax
checks and actionlint pass for default-token and App workflows. Direct offline
execution completed mention -> findings -> inline publisher, refused 10 unsafe
request cases, refused fork analysis without opt-in and cross-attempt findings,
and confirmed provider-key isolation/redaction. No new tests or code comments.
Live GitHub/provider acceptance follows separately; no release or moving tag.
`codewhale uninstall --yes --purge-data` deleted ~/.codewhale even when the run only printed a package manager's removal command and left the program installed. Purge now runs only in a confirmed run that removed a receipted installation. Adopting an older curl-installed copy opens every candidate without following links and refuses symlinks, hard links and files owned by another user before any receipt is written, and uninstall resolves the selected executable path on Unix. Recognizable Codewhale PATH blocks in shell profiles are reported for manual cleanup; uninstall never edits a profile. The website installer now creates new directories privately and refuses system, Homebrew and MacPorts bin directories, the home directory, `/`, and directories other users or groups can write, with the `chmod go-w` remedy in its message. INSTALL.md (English and Chinese), both changelogs and the generated agent docs follow.
Validation: a macOS arm64 release build of this exact uninstall.rs (4,160 build inputs unchanged) and a sandboxed probe of the resulting binary in synthetic directories passed 4/4 cases (plain removal, purge preview and purge, package-managed copy keeps data, hard-link refusal; network and the real home denied). An x86_64-pc-windows-gnu zig build of the same source compiled and linked (21m06s, inputs unchanged). scripts/release/install-safety.test.js passed 33/33 (website and archive installers) on a scratch export of this tree; rustfmt --all, sh -n install.sh and sync-changelog --check pass, and the generated install entry was re-derived with the repository's own generator. Not covered: no run on a Windows host, no MSVC build, no real npm, Cargo, Homebrew, WinGet or Scoop removal, no hosted CI, and no installer-written PATH block exists yet, so the report path only sees blocks written by hand. The PowerShell and NSIS routes and product-owned PATH integration (F2) remain open; #7091 stays open.
Refs #7091
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Select the existing rquickjs bindgen feature only for the Windows GNU LLVM target, which has no bundled bindings. Reuse the existing platform-specific dependency configuration.
Validated on x86_64-pc-windows-gnullvm with Rust 1.98.1 and isolated libclang 18.1.1: default library check passed; existing VM integration tests: 83 passed, 0 failed. Removing the cfg reproduces the missing-target-bindings build failure. ARM64 execution and cross-host builds remain unverified.
Signed-off-by: Codex <[email protected]>
Publish a terminal result after the particular owned run future is dropped.
Keep waits independent and cancel safe, preserve the supervisor/worker API,
and share the exec consumer's existing two-second deadline across Shutdown
and completion. Completion does not certify durable persistence or detached
writer/child termination; early error and broken-pipe exit limits are recorded.
Validation on main 71bc853fe with Windows GNU LLVM Rust 1.98.1:
- Strict TUI product-library check passed with rquickjs/bindgen.
- 11 run-completion regressions passed (0.27s); existing catalog probe: 1 passed (2.11s).
- TUI formatting and diff checks passed.
- Earlier identical helper/test blobs: renewed-budget mutation caused the
intended assertion failure, and the restored positive control passed.
Required CI, external client interoperability, installed artifacts and release
qualification remain separate. No E2E or deployment was performed.
Signed-off-by: Codex <[email protected]>
Keep uncommitted reasoning text out of the translation placeholder when a block closes without its usual completion event; retain the reasoning buffer for the normal session record. Remove a comment that implied an unowned tail would necessarily be delivered.
Validation: 9 focused existing Linux checks passed on the contributor candidate plus this follow-up, including the extended translation fixture; all 4,160 captured inputs stayed unchanged. Final release CI and an actual terminal event replay are separate.
Describe interactive zsh startup as Codewhale guidance instead of a shell-generated error.
Validation: rustfmt and diff checks passed; the extracted current diagnostic compiled and rendered with the existing auth command intact. The Ollama route mismatch in #7115 remains open.
Show the effective threshold for the current route alongside the configured target, and distinguish automatic compaction being off. Route limits and live token estimates can change the actual trigger.
Validation: existing context rail fixture passed 1/1 on the frozen Linux candidate (13,924 filtered); all 4,160 source inputs remained unchanged. The reported Windows compaction event has not been reproduced.
Flush already queued persistence during signal shutdown alongside Runtime API drainage, with a two-second bound and the existing second-signal escape. The flush does not capture state that was never queued or guarantee a write when the timeout expires.
Validation: 14 existing persistence-actor tests passed on the exact current-main plus contributor candidate on Linux; all 4,160 source inputs were unchanged through the check. The contributor also reports simulated signal-handler checks. Actual OS-signal-to-disk behavior and final release CI remain unverified.
Register the painted update segment as a typed mouse target, use the existing /update command, and show the same hover affordance as other actionable metrics. The check only explains the update; it does not install it.
Verification: 1 existing Linux render fixture passed on the clean main-based source export, covering visible hover and clicks in launch and session states. All 4,160 export inputs and canonical inputs stayed unchanged. The notice remains hidden when the metrics row is hidden or shed.
Use the snapshot returned by the validated Work restore instead of capturing it again after the target session has already become active. A contended second capture could previously abort the session switch after changing the Work owner.
Verification: 3 existing Linux restore checks passed on a clean main-based source export with this fix and the separate update-notice slice; all 4,160 export inputs and canonical source inputs stayed unchanged. The inherited Work Graph publish patch remains uncommitted.
Show a quiet available-version notice on the normal and empty-session metrics line, without replacing permission or remote-control status. A staged unsigned update no longer claims it is ready. Existing hidden/mini/pet layouts remain unchanged and the notice is display-only; /update remains the action.
Refs #7136. Verified on the frozen Linux source export: the existing empty-session render and status-item coverage checks passed (2 passed, 0 failed; 13,924 filtered in each). rustfmt and diff checks passed. First VM test attempt was killed at its memory limit; the one-job retry with swap passed. No installed binary, real update check, clickable control, or hosted CI was verified. Read-only design review by Claude.
Review found the previous `MessageStarted` change was dead code:
`App::flush_active_cell` clears `streaming_thinking_active_entry` on every path
(`app.rs:5928`, `:5935`, `:5940`) and only marks the thinking cell finished; it
does not deliver a delta that never reached it. The guard ran after the flush,
so it could never be true, and the reset afterwards still dropped the tail.
The drain now runs before the flush, which is the only point where the entry is
still live.
Two related losses on the same path:
- `flush_pending_text_tail` drained the buffer and then returned when
`streaming_message_index` was `None`, discarding the text. It resolves the
destination first now, and does nothing when the buffer is already empty.
- `finalize_current` took the buffer without a live entry to deliver into, so
the text was dropped. It now leaves the buffer alone in that case.
Tests drive the real handler order (finalize, then flush) and assert the tail
survives both steps, plus the no-entry and text-block cases.
The mirror of the text-to-thinking boundary the previous commit fixed. When
assistant text starts again, `MessageStarted` reset `StreamingState` after only
resetting the display clock. If `ThinkingComplete` never arrived, the reasoning
block still held its uncommitted tail and the reset discarded it, and the
thinking entry kept streaming with no new content ever reaching it.
`MessageStarted` now finalizes the live reasoning entry and stashes its buffer
before the reset, which is what `ThinkingComplete` already does for the normal
path.
Also adds the inverse test for `drain_text_block`: a reasoning block must stay
buffered, a text block must drain, and an empty state must not panic.
Review found two more places where the fix was incomplete.
`start_block` resets `StreamingState` when a reasoning block opens. The display
beat commits at most once per interval, so a block boundary that lands between
beats left the last slice of prose in the buffer, and the reset discarded it:
the tail before a reasoning card never reached the transcript. The tail is now
delivered to the assistant cell before the reset.
The translation branch of `ThinkingComplete` still drained block 0
unconditionally, so a text block that lost its boundary event would be consumed
and dropped instead of reaching the answer. It is guarded like the other
finalize paths.
Both new tests fail without these changes: the first asserts the prose cell
still holds its tail after a reasoning block opens, the second asserts a stray
text block is not appended to the reasoning card.
The finalize paths drained block 0 into whichever cell they were about to
close: a text drain into the assistant cell, a reasoning drain into the
thinking entry. That only stayed correct because the thinking drain happened
to run first. Both drains now check the block's kind, so a lost boundary
event cannot append reasoning to the prose or prose to the reasoning card
(#7123).
A display beat chose its destination cell by whether an assistant cell was
already live, not by the kind of the block the delta belonged to. The engine
keeps an assistant cell live across a finished text block (#7123), so a
response that streamed text and then opened a reasoning block appended the
reasoning to the prose body: one sentence appeared partly inside the thinking
card and partly outside it as ordinary text.
Route the committed delta by StreamingState::block_is_thinking, so reasoning
always lands in the thinking entry and a text delta still gets an assistant
cell when its own was already closed.
Keep the contributor's Vim dollar-motion fix and preserve its original commit. The follow-up keeps deletion and append within an empty line.
Verified locally: two existing grapheme filters passed 1/1 each, the CLI built, and three isolated one-key terminal replays covered dollar motion, repeated deletion and append. Paste-burst detection was disabled for these replays; the separate held-key path remains unqualified. No live provider or release validation was run.
Do not let x delete an empty line break or a move across it. This completes the dollar motion on empty lines.
Verified: two existing grapheme filters passed 1/1, the CLI built, and three sandboxed one-key PTY replays covered dollar, repeated deletion and append. No live provider or release validation.
Signed-off-by: CodeWhale Bot <[email protected]>
Fail the compaction pass without replacing history when the complete summary cannot fit, instead of retrying with older messages silently omitted. The existing image-size retry path remains available.
Verified: four existing focused compaction filters passed 1/1 each and the non-test CLI build passed on the frozen source; 4,160 inputs and HEAD remained unchanged. No live provider call or release validation.
Expose the provider-declared off toggle, clamp DeepSeek reasoning to the official model catalog, and leave automatic Responses effort to the provider. Custom proxies do not inherit first-party capability claims.
Verification: five existing DeepSeek catalog, precedence and wire filters passed 1/1 each in the unchanged combined source snapshot at 687a38c6ed. The later combined CLI build and TUI library lint also passed; the shared source included a separate unstaged Auto-default draft. No live provider calls were made.
Refresh the roster through the admitted built-in Codex route even when another provider is selected, and use a new account-scoped cache filename so older mixed catalogs are not reused. Reject nonofficial routes for this refresh.
Verification: existing own_roster_follows_selected_registration_and_disappears_after_sign_out filter passed 1/1 in the unchanged combined source snapshot at 687a38c6ed. A later combined CLI build and TUI library lint passed with the same catalog files present. No live provider calls were made.
Keep the built-in command available and expose the plugin command under a deterministic plugin-specific name. Preserve registration ownership and replay checks, and require a fresh Native policy review.
Verified focused registry, dispatch and policy checks, plus an isolated published-plugin activation and synthetic-model review flow. Real provider, plugin UI, direct Git and release qualification remain open.
Preserve @gaord's build repair for local Ollama discovery and the checkpoint wrapper. A follow-up removes newly added comments to follow the workspace policy while keeping the contributor's original commit.
Local verification on the combined frozen Engine tree: non-test CLI build passed; the existing Ollama probe and checkpoint round-trip filters passed 1/1 each; TUI library clippy passed. HEAD and all 4,160 captured source inputs stayed unchanged within each verification run. The provider-picker documentation relocation and comment-only follow-up were checked in the second run. No live provider call or final release validation was performed.
Use fixed guidance for recognized service rejection codes and retain the status-only fallback for other responses, so arbitrary server text is not copied into user-visible errors.
Verified on frozen source: the two existing rejection filters passed 1/1 each, the non-test CLI built, and all 4,160 recorded Engine inputs and HEAD were unchanged. The local HTTP fixture used synthetic responses; no live remote-control service was exercised.
Replace derived Debug implementations for Codex token holders with redacted output, including nested auth-file formatting. Extend the existing redaction fixture with synthetic credentials.\n\nVerified: debug_impls_redact_secrets 1/1 on the frozen 687a38c6ed source batch (4,160 inputs unchanged). The separate CLI build failed on the unrelated Ollama future lifetime and is not release evidence. No provider calls.
The engine appends a <turn_meta> block to each user message it saves. The
transcript, the resume picker and session titles leave it out, but /tree
and /export did not: /tree labelled each user entry with the start of the
envelope ("user: say flat things <turn_meta> Current local date:
2026-10-29 C"), and /export wrote the whole block into every user message,
including the absolute workspace path that the export header shortens to
the folder name.
Skip the block at the index turn_metadata_text reports, as prompt_text in
receipts.rs already does, in the /tree journal label, its linear fallback
and the export projection. Only user messages are filtered, like the
transcript's is_turn_metadata_block.
Signed-off-by: Lstarsky0 <[email protected]>
Adds Opper on the existing compatible-host descriptor path: Chat Completions at https://api.opper.ai/v3/compat, OPPER_API_KEY, and claude-sonnet-4-6 as the bootstrap model. Live GET /v3/compat/models stays the model authority. Billing is recorded as Unknown in the pricing omission ledger; no provider enum or price row.
Disclosure: Opper is my startup.
Signed-off-by: Felix Wunderlich <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
`cargo build` and the Lint gate have been failing on main since the local
Ollama discovery and interrupted-session landings, so every pull request
built on current main is red before it is reviewed. This makes main
compile and lint again.
The compile error was a lifetime mismatch in `fetch_tag_profiles`: the
per-tag async block borrowed `&String` from the caller's slice, so the
stream's item type carried a caller lifetime that `tokio::spawn` could
not satisfy. It now owns a `String`, which keeps the bounded-concurrency
probing and its ordering unchanged. Annotating the closure argument, as
the compiler suggests, does not fix it.
The two remaining errors were lints denied by the workspace
`-D warnings`: a redundant `return` and a unit value passed to `Some`.
Both keep their existing behavior.
Verified on macOS with the locked workspace: `cargo fmt --check`,
`clippy --all-targets --all-features` with the CI allowances, a full
`codewhale-cli` build, and the client, local_ollama and session_manager
suites. Two failures that reproduce on unmodified main are left alone:
session_manager's interrupted-deletion test, and intermittently failing
socket-bound tests in the client suite.
Signed-off-by: Ben Gao <[email protected]>
`runtime_api.rs` is the only way the community VS Code extension talks to the
engine, yet nothing in this repository or its tests would notice a changed
route, and the breakage would surface in that release instead of here. Record
the dependency where the next editor of the file will see it, along with the
three wire details it depends on without a probe to fall back on.
Docs only. Verified the claims against the extension's `src/api/engine.ts` and
`src/api/api-client.ts` — the `serve --http` launch, the listener-line and
token checks, the wrong-method feature detection, and the capability flags —
and against `build_router`, `RUNTIME_LISTENING_PREFIX` and the advertised
flags here. No code changed, so no build or test run applies.
Signed-off-by: Ben Gao <[email protected]>
Retain per-model supported levels and defaults from official DeepSeek catalogs while keeping compatible endpoints and unknown levels unclaimed. Reject malformed metadata without replacing the previous catalog.
Verified the existing limits_a_generic_route_keeps_the_limits_its_listing_states test: 1 passed, 0 failed, 13930 filtered; 4160 input hashes unchanged on the shared frozen tree. That tree included other recorded uncommitted workspace changes; the linker emitted a compact-unwind warning. No provider call or inference.
Inspect the copied manifests for selected Native package entries and record unsupported dsh.client components in the exact-content conversion receipt. This makes omitted plugin screens visible before import while keeping them unexecuted.
Verified the plugin crate build, 36 existing install tests and the existing unsupported-enable test with 529 inputs unchanged. A local linked-converter harness checked synthetic and published package fixtures, a no-client control, stable and changed hashes, and the persisted receipt. This does not qualify plugin activation or release behavior.
Keep synchronous start and preflight failures visible through existing Failed state, preserving active and unexpired lease guards. Report failures in redacted TUI logs, current UI history and bounded toasts; retain the /rc status action after toast expiry.
Verified: remote_control::tests 84 passed, 0 failed; existing UI regression 1 passed, 0 failed; phase_strip::tests 11 passed, 0 failed. UI and footer tests use the development wrapper stack setting RUST_MIN_STACK=16777216. cargo fmt --all -- --check and git diff --check passed. No live enrollment or interactive PTY verification.
Signed-off-by: laozidabo <[email protected]>
`$` left the cursor after the last character, on the newline or past the
end of the draft. So `$x` did nothing on the last line and deleted the
newline anywhere else, joining the line to the next one. `$` now steps
back onto the last character, as Esc already does at the end of the
draft, and `x` steps back the same way when it deletes a line's last
character, so `$xx` takes two characters off the end. `o` still opens
its line after the last character, through move_cursor_line_end.
Signed-off-by: Lstarsky0 <[email protected]>
Measure columns across the filtered catalog so offsets remain stable while scrolling. Keep the family heading plain and reserve advanced footer hints for windows with enough width and height; preserve the underlying actions and exact route selection.
Verified five existing TUI checks in the shared frozen Engine batch: wide footer actions, compact/wide header clicks, family-heading hitboxes, full-catalog navigation/sort/refresh/mouse, and locked-row keyboard focus. Each passed 1/1. Current file SHA-256 ac21c77e0e596d8feee9c8cc760889944d3dd75e1540aa68ddf411e473f2f603 and exact diff match the checked frozen patch. git diff --check passed. No new tests or provider calls; no installed-terminal or release qualification claimed.
Probe unknown model capabilities with bounded concurrency and publish a complete catalog only when every tag is resolved. Fence automatic adoption against a newer discovery or changed endpoint, while retaining a confirmed local candidate when the catalog is incomplete.
Verified on the frozen shared Engine source: four focused existing checks passed for local discovery, picker adoption, publication fencing, and runtime catalog routing. These use synthetic local fixtures; a real Ollama and desktop session remain unverified.
Recognize the control-plane rejection status and machine code, and show a fixed actionable message without exposing untrusted response content.
Verified on the frozen shared Engine source: the existing bounded error-field test passed. The focused Platform server rejection test also passed; no hosted remote-control flow was exercised.
Describe subscription billing without asserting that the provider charged nothing, while retaining detailed usage receipts and mixed-route reporting.
Verified on the frozen shared Engine source: the existing TUI cost diagnostic test passed, covering subscription, details, mixed and unknown cases; two focused commands-host checks also passed. No live provider billing was inspected.
Let file writes preserve the existing line-ending style by default or explicitly choose LF or CRLF, applying size checks and receipts to the actual bytes written.
Verified on the frozen shared Engine source: six focused existing checks passed, including CRLF preservation, schemas, byte receipts, and queued cancellation. The broader agent-driven workflow remains unverified.
Retain crash checkpoints, discover recoverable sessions in the picker, and promote only under the session lease with workspace and saved-state checks. Preserve the shared legacy slot and fail closed when damaged recovery could otherwise be overwritten.
Verified on macOS with the locked hermetic TUI checks: checkpoint 78 passed, continue 12 passed, picker rename 1 passed, picker archive 1 passed. The linker reported a compact-unwind size warning. Legacy-slot cleanup and old-writer coordination remain limited.
On a terminal that isn't trusted to deliver bracketed paste, the raw-paste
guard holds each plain character for a few milliseconds and, when nothing
follows, types it into the composer. That happens before the event loop's
Normal-mode arm sees the key, so with vim mode on, i, x, dd and the
motions went into the draft as text until the session's first bracketed
paste. A held character now goes to the Normal-mode keys when no
completion menu is open, the condition that arm checks; it is absorbed in
Visual mode, as the arm after it does, and typed otherwise.
Signed-off-by: Lstarsky0 <[email protected]>
crossterm reports an uppercase letter with SHIFT, and the Normal-mode arm
only took a key with no modifiers at all, so G (end of the draft) fell
through to the insert arm and was typed into the draft, as was any other
shifted letter. The arm and its Visual-mode twin now take a key that passes
is_plain_char, the test that insert arm already uses: Shift is allowed,
Ctrl, Alt and Super are not.
Signed-off-by: Lstarsky0 <[email protected]>
prepare-release.sh 0.10.3 refuses a release section without a Keep a
Changelog subsection (rehearsed on an export of main: "CHANGELOG.md
section 0.10.3 must contain at least one Keep a Changelog subsection").
The Unreleased bullets are regrouped, unchanged, into Added, Changed,
Fixed, Security and Maintenance as in 0.10.2, with Contributors last;
the freeze then only renames the heading.
check-contributor-credit.py also failed on main: @laozidabo (#7101,
localized /diff feedback, merged) was not credited. Added to the
CHANGELOG Contributors block, docs/CONTRIBUTORS.md and
UNRELEASED_CONTRIBUTORS; agent docs regenerated.
Verified on the edited export of main: the sorted bullet set is
unchanged apart from the new credit line; sync-changelog.sh --check,
check-feature-release-notes.sh v0.10.2 HEAD, check-contributor-credit.py
v0.10.2 (was exit 1), check-bundled-plugin-claims.py and web
check:facts pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A DSH plugin tool can now call ctx.subagents.startActivation({provider:
'spawn', childId, request: {parent, prompt}, signal, delivery}) and get back
{childId, messageId, result, dispose}. The bridge is a thin adapter over
SharedSubAgentManager: no second executor or loop, and no provider call
outside the existing child turn.
- Host (root.ts): `subagents` shim (getProvider('spawn') reports
inheritsParentContext:false; startActivation), an empty `agents` shim, and
a `systemPrompt` shim whose methods refuse outside a child's agent/created
context. During startActivation the owner's own agent/created listeners get
a sealed child: systemPrompt.section(PERSONA_PREFIX_SECTION) is an additive,
child-scoped persona (at most 32 KiB) and tools.restrict({deny}) can only
narrow. agent/created is no longer refused as unprojected; global
agent/created listeners are refused instead of never firing. tools.get(name,
agent) reads the calling tool's catalog.
- Engine protocol: subagent/start, subagent/result and subagent/dispose, each
bound to the live root invocation's subagent ticket and the child ids that
invocation started; reviewed in REVIEWED with why each gives the host no core
authority. protocol.generated.ts and dist/ regenerated.
- Approval planning: the start is executed as a typed `agent` call through the
turn loop's nested-call gate (gate_nested_call), so it gets the same
plan_tool_calls, hooks and approval card as the model's own `agent` tool.
Only action=start, fork_context=false with a string prompt is accepted.
- Manager: prepare_background_with_assignment_options / commit_prepared_child
with a frozen child configuration (persona, denied_tools, delivery Parent |
Caller). Cancellation, disposal and the result receipt go through the
manager; an invocation that ends disposes its children. The stale/cancelled
commit check now applies the stop-token test only to activation children,
restoring the ordinary spawn path's earlier behavior under an already
cancelled parent (limits_tests, completion_usage_tests).
- Compatibility table (src/dsh/compatibility.json and the plugins asset copy):
subagents, agents and systemPrompt move to provided. Changing the table
invalidates earlier trust.
- Existing tests updated: host.test.mjs no longer expects systemPrompt, agents
and subagents to be refused (llm still is). CoreMessage::Request (test-only)
boxes its request to keep the enum within clippy's variant-size limit.
- Docs: DSH_PLUGIN_COMPAT.md checklist, child-agent mapping and known limits;
EXTENSIONS.md (en, zh_hans); CHANGELOG (root and crates/tui synced).
Known limits: only the spawn provider; fresh context only; a requested model
must already be usable by the parent; only text output blocks reach the
plugin; the older subagents.start form is refused; @michengai/dsh-code-review
now activates but its direct git use is still denied by the Native profile
(not run).
Verified on Linux Boat VM vm22:
- Live Runtime API probe (fake loopback provider, fixture-sa): install,
review, trust, enable; the dsh_probe tool approval, then a separate
approval card for the nested `agent` start; the child's own model request
carried the persona marker and its tools excluded dsh_probe and write;
delivery 'caller' returned "child completed (denied dsh_probe,write, message
accepted)" to the plugin, and /probe-count read back stopReason completed
and the messageId. The first probe run found agent/created refused as
unprojected; fixed here and rerun green.
- Final tree (origin/main b8afed5f27 + this change):
cargo check --workspace --all-targets --locked ok;
cargo test -p codewhale-tui --lib -- tools::subagent:: extension_host::
tools::codemode:: 988 passed, 0 failed;
typescript_protocol_is_generated_from_the_rust_types 1 passed (no update);
cargo fmt --all -- --check ok after formatting.
- Earlier on the same change (base 1420d35c9f): CI's clippy command
(--workspace --all-targets --all-features -D warnings with CI's three -A
allows) ok; host npm test 413 passed, 0 failed, 6 skipped;
core::engine MCP tests 10/10 when run on their own (under heavy parallel
load 6 timed out, and 2 also fail on pristine main under the same filter).
Not run: Bun host tests, macOS, Windows, desktop App, interactive TUI.
Refs CW-7054
Co-Authored-By: Claude Opus 5.5 <[email protected]>
QA friction audit (codewhale-ops dd2c253d row 9): /mcp add recommended
playwright wrote config, but the first connect failed because the cold
npx @playwright/mcp download overran the 30 s connect budget. The error
read "MCP SDK request failed before an admitted reply: mcp/open timed out
after 30s; the host was told to cancel it".
- A stdio server launched through npx, pnpx, bunx or uvx with no
per-server connect_timeout now gets max(global, 120 s). An explicit
per-server value still wins.
- When initialize fails because the connect budget ran out, the error now
leads with "MCP server '<name>' didn't start within Ns; retry with
/mcp restart, or raise its connect_timeout", with the transport detail
still in the chain.
- docs/MCP.md states the floor.
Verified on the shared tree (same code, pre-S19 layout):
scripts/dev-test.sh tui mcp 584/584 passed. Ported to the S19 layout
(crates/mcp/src/config.rs) by text; rustfmt clean. The live npx cold
start was not re-run here.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
0e9c5beba5 (E5) added one std::fs::create_dir in
SandboxManager::prepare_bwrap to create the workspace's .codewhale/
before bubblewrap starts (bwrap can only protect a path that exists).
`prepare` is synchronous and runs just before the sandboxed process is
spawned, so the call stays synchronous; the budget is raised for that
single site (crates/sandbox/src/lib.rs std_fs 2 -> 3), as the checker
directs for a site that can only run on synchronous code.
Verified on an export of origin/main 3e4ce7d772: before, exit 1
("crates/sandbox/src/lib.rs: std_fs sites 3 > budget 2"); after
`--update`, "within budget", exit 0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
INSTALL.md: re-running the installer upgrades; automatic updates
(signing rule, opt-out, organization policy); rollback; cleaning up old
copies; release-signing founder steps. CONFIGURATION.md: [update] mode
and 6 h interval. REBRAND.md: stale deepseek shims. CHANGELOG entries,
crates/tui/CHANGELOG.md synced with scripts/sync-changelog.sh.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
web/public/install.sh and scripts/release/install.sh now upgrade a
destination that is a regular, user-owned Codewhale binary (marker
string present), with or without a receipt, and replace an older
receipt. The old files move to ~/.codewhale/recovery/<stamp>/ with a
manifest that `codewhale doctor --restore <stamp>` reads; nothing is
left as .bak beside the commands. Symlinks, other users' files, foreign
programs and differing companions are still refused (FORCE_REINSTALL
moves a regular file you own to recovery). Both finish with
`codewhale doctor --installs --brief`. install.ps1 keeps the replaced
files in the same recovery folder layout instead of deleting them.
install-safety.test.js: execution sentinel now allows only the new
binary's doctor call; the refusal test names a non-Codewhale file.
Verified: install-safety.test.js 33 passed, 0 failed on this series
tree (macOS). On Linux Boat vm16, the website installer pointed at a
local fake release upgraded an unreceipted ~/.local/bin copy: old
codewhale and codew are in ~/.codewhale/recovery/<stamp>/ with a
manifest, the install dir holds only the commands and a fresh receipt,
and a plain script at the destination is still refused. Re-running the
website installer over a different installed release (QA report):
temp HOME, install release A then release B from a local server; B
replaces A in place, the receipt is rewritten with B's digests, and A's
codewhale, codew and receipt sit in one recovery folder with an undo
command; a foreign file at the destination is still refused
(install-safety). install.ps1 was not run (no Windows or pwsh here).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Items 4 and 6 of the install-cleanup design land together because both
reshape the same replace path in crates/cli/src/update.rs.
Update adoption (item 4): `codewhale update` writes an install receipt
for a pre-receipt directory whose commands it just installed, and a
divergent Codewhale `codew` beside `codewhale` is moved to the recovery
folder and replaced instead of failing the update. Receipt renewal keeps
its retired receipt in recovery, not in the PATH directory.
Automatic updates (item 6):
- [update] mode = auto | notify | off (default auto for release
binaries); CODEWHALE_UPDATE_MODE; a root-owned, not group/world-writable
policy.toml (/Library/Application Support/Codewhale, /etc/codewhale,
%ProgramData%\Codewhale) overrides both; malformed policy means off.
`codewhale config set update.mode` writes it.
- Check interval 6 h (was 1); a failed check keeps the last answer and
retries about an hour later.
- The TUI stages a newer release in the background with
`codewhale update --stage` (child process) into
~/.codewhale/updates/staged/ with its manifest and signature; chip
"↑ vX ready".
- At the start of the next codewhale process (never update/uninstall/
doctor/machine output/inherited listener), the staged binary is
re-verified against the stored manifest and, when a release key is
configured, the Ed25519 signature; staged.json's own flags are not
trusted. Signed: applied silently, then exec. Unsigned: one [y/N]
question on an interactive terminal (default no; declining is
remembered); never applied non-interactively.
- `codewhale update --rollback` swaps back the binary the last update
replaced (kept in ~/.codewhale/updates/previous/) and pins the
rolled-back version so it is not staged again.
Signing: crates/release/src/signing.rs verifies a base64 Ed25519
signature over "codewhale-release-manifest-v1\n<version>\n<manifest>",
reusing the desktop updater's ring-based check. TRUSTED_RELEASE_KEYS is
empty until the founder creates the key; CODEWHALE_RELEASE_PUBLIC_KEY at
build time adds one. With a key configured, a missing, unfetchable or
invalid signature refuses both update and stage. CI: release-artifacts'
assemble job signs with the CODEWHALE_RELEASE_SIGNING_KEY secret
(scripts/release/sign-release-manifest.sh) when present, else says the
release is unsigned; the inventory verifiers accept the optional .sig.
Security review E3/E4 fixes: Missing is no longer accepted when keys
exist; apply-time re-verification; default-no prompt; the checksum
manifest now only ever comes from GitHub on the proactive path, and the
CNB mirror may only serve a binary whose SHA-256 that manifest lists
(replaces the two tests that encoded the old mirror-manifest fallback).
Debug builds only: CODEWHALE_UPDATE_ALLOW_LOOPBACK_HTTP,
CODEWHALE_TEST_RELEASE_PUBLIC_KEY and CODEWHALE_UPDATE_POLICY_PATH let
fixtures run against a local fake release; release builds ignore them.
Verified on Linux Boat vm16 (debug build) against origin/main plus this
series, temp HOMEs and a local fake release only:
- cargo build -p codewhale-cli clean; cargo fmt --all --check clean;
cargo clippy -p codewhale-release -p codewhale-cli -p codewhale-tui
-p codewhale-config -p codewhale-runtime --all-targets --all-features
with CI's -D warnings set: clean, run with -A clippy::needless_return
because main's provider_lake.rs:2033 fails that lint under
--all-features (not touched here); an earlier run on this series code
over an older main gave cargo check --workspace --all-targets 0 errors
and 0 warnings.
- tests: codewhale-release 57 passed; codewhale-cli update 110 passed;
parses_update 1 passed; codewhale-config lib 808 passed (1 ignored);
feature_registry 2 passed. JS: assemble-release-assets 7/7,
verify-release-inventory 7/7, release-workflows 1/1.
- fixtures: unreceipted dir with a divergent codew updates, the old codew
goes to recovery, a receipt is written and `uninstall` previews exactly
codewhale, codew and the receipt; `update --rollback` restores the
original bytes and pins the version; a group-writable dir updates but
declines the receipt. Unsigned stage: a non-interactive start does not
apply, Enter keeps the old version and the answer is remembered, `y`
applies. Key configured: missing .sig refuses update and stage (nothing
staged); a signature from another key is refused; a signed stage
applies silently at a non-interactive start. A tampered staged binary
is refused and discarded; a forged "signed": true in staged.json
without a key is ignored. An offline stage fails cleanly. doctor shows
the mode source for default, CODEWHALE_NO_UPDATE_CHECK and an
organization policy file; `config set update.mode` writes and validates.
Not exercised: the TUI background staging path end to end, Windows,
macOS start-time exec, the CI signing step (no secret exists), tui lib
unit tests (the VM ran out of memory linking them).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Copies the token migration from upstream codewhale-hq/codewhale-ratatui
528a9c4 into vendor/codewhale-ratatui, before the 0.10.3 tag:
- roles.rs regenerated from tokens 2.3.0 (19 roles: `Accent` for links,
focus and selection; `Primary` is filled-action ink; diff tints from
diff_add/diff_remove); vendor/codewhale-design/tokens.rs 1.1.2 -> 2.3.0;
theme.rs, tui_palettes.rs and assets/tui-palettes.json match upstream.
- The kit's Primary-to-Accent call sites are applied to the Engine's own
copy (base snapshot 2ad795b plus its patches); the other upstream
commits since that snapshot are not taken.
- The 16 named TUI palettes map Accent to the same accent_primary slot
the old Primary read, so the built-in themes keep their colours.
- crates/tui: the infoline Info/policy ink and two test fixtures move
from Role::Primary to Role::Accent.
- UPSTREAM.json records a `tokens_update` section (commit, scope, local
changes, sha256 of the 47 changed files); the original snapshot hashes
are left as provenance, as pet_update does.
Verified on a Linux Boat VM from a fresh clone of origin/main 786a0e4b33
plus this patch (the later main commits touch none of these files):
cargo check --workspace --all-targets --locked green (10m21s);
cargo test -p codewhale-tui --lib -- tui:: 4023 passed, 0 failed,
8 ignored (render, snapshot, infoline and phase-strip tests; no snapshot
needed updating). The unfiltered `cargo test -p codewhale-tui` run aborted
in acp_server on a stack overflow before reaching these tests, and its
cucumber/integration failures all report the unbuilt `codewhale` CLI
binary; palette_audit passed. Real TUI screenshots (built CLI in tmux,
truecolor, Underwater and Shoreline Light: home with the command menu
and mode badge, /diff, /provider with focus and link) are under
CW/artifacts/design-20261027/tui-engine/.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
New crate crates/account-cli (codewhale-account-cli): cli/src/cloud.rs
becomes its lib.rs, cloud/{work,machine,computer}.rs and their tests
move with it, and cli/src/dispatch.rs becomes account_cli::dispatch.
CloudArgs, DispatchArgs, the login-timeout constants, run,
run_account_login, reject_inline_api_key, machine_review_provider,
dispatch::run and machine::MachineError are now pub. The three
crate::hashing::sha256_hex uses call codewhale_runtime::hashing; the cli
keeps its hashing.rs for update.rs. codewhale-cli drops `mod cloud;
mod dispatch;`, calls codewhale_account_cli::, and no longer depends on
webbrowser. All 124 tests moved; a test-only clap wrapper in the new
crate replaces the private cli Cli type. No behavior change.
Implemented on vm13 by the split session.
Path-keyed: root Cargo.toml and Cargo.lock, scripts/release/crates.sh
(after app-server), scripts/dev-test.sh (area account-cli),
docs/features.toml owners, check-blocking-calls-budget.json keys, and a
path comment in web docs-auth.ts.
Verified on Linux x86_64 Boat VM vm21 (tree 69daf5299f70, on
S19+E2+S57+S49):
- cargo fmt --all -- --check: exit 0
- cargo check --workspace --all-targets --locked: exit 0, 0 warnings
- CI Lint clippy (--all-targets --all-features -D warnings, CI allows):
exit 0
- cargo test -p codewhale-account-cli -p codewhale-cli --lib:
account-cli 124 passed, cli 377 passed, 0 failed
- validate-crate-publish-order.py over release_crates: exit 0
- check-blocking-calls-budget.py: only the pre-existing uninstall.rs and
sandbox overages
Not run: production-profile cli build, cli integration binaries,
macOS/Windows, hosted CI.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
crates/app-server/src/lib.rs goes from 6,962 to 990 lines:
- http.rs: runtime_compatibility_router, app_router, handlers, auth
token and CORS (pub mod)
- stdio.rs: BoundedLines, run_stdio_loop, stdio dispatch, interrupt and
thread-message handling (pub mod)
- turns.rs: thread, prompt and bridged-turn handling
- bridge.rs: runtime bridge acquisition, catalog, impl RuntimeBridge,
SSE parsing, and the cfg(test) bridge helpers
- config_rpc.rs: process_app_request and config persistence
- tests.rs: the former inline test module, moved verbatim
lib.rs keeps the options and public types, run, bind_*, the JSON-RPC
types and helpers, and build_state*. Callers are rewired without
facades (tui acp_server, runtime_api and its tests now import
codewhale_app_server::stdio::BoundedLines and
::http::runtime_compatibility_router); the public thread-type and
thread-control re-exports are preserved. Visibility widened only to
pub(crate). No behavior change. Implemented on vm12 by the split session.
The RUNTIME_API docs' drift-test path now names
crates/app-server/src/tests.rs, and that one entry of
web/data/agent-docs.generated.json is regenerated with
web/scripts/derive-facts.mjs. The rest of that run's output (stale
INSTALL/CONFIGURATION entries and features.generated.json, which
predate this change) is not included.
Verified on Linux x86_64 Boat VM vm21 (tree 32722082b2cb, S19+E2+S57
base):
- cargo fmt --all -- --check: exit 0
- cargo check --workspace --all-targets --locked: exit 0, 0 warnings
- CI Lint clippy (--all-targets --all-features -D warnings, CI allows):
exit 0 (first clippy run for this slice)
- cargo test -p codewhale-app-server --locked: lib 177 passed,
tests/daemon_socket.rs 15 passed, doctests 0; 0 failed
- check-blocking-calls-budget.py: only the pre-existing uninstall.rs
and sandbox overages
Not run: cfg(windows) compile (daemon_windows.rs), app-server-smoke.sh,
macOS, hosted CI.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
configured_codewhale_binary() fell back to the bare name "codewhale",
so Fleet runs started from `codewhale fleet run` or `POST
/v1/fleet/runs` resolved the worker on PATH. With a minimal PATH
(launchd/Finder, `env -i`) every worker failed with "starting worker
…: No such file or directory", and with an older codewhale earlier on
PATH the run silently used it.
Without CODEWHALE_FLEET_CODEWHALE_BINARY, a process whose executable
stem is codewhale/codew or codewhale-*/codew-* now uses
std::env::current_exe(). Other hosts (test harnesses such as
codewhale_tui-<hash>) keep the old "codewhale" default, so they never
spawn themselves as workers. Windows codewhale.exe matches the same
stem. Reported and verified by the QA session.
Verified on Boat vm19 (Linux x86_64) at base 804b7fca89 + this change:
`cargo test -p codewhale-tui --lib --locked fleet::` 452 passed, 0
failed. QA ran the built binary (sha256 513820fa…5558e3) under its own
name `codewhale-7119b` against a mock provider: with PATH=/usr/bin:/bin
and no codewhale on PATH, Fleet completed 2/2 (both workers exit 0);
with a stale `codewhale` first on PATH, 2/2 and the stale binary was
never invoked (CW/artifacts/qa-20261027/fleet/vm19-7119b-ownname.txt).
The previous gate (exact stem only) failed both cases under that name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
crates/config/src/lib.rs goes from 8,109 to 135 lines. The items move
into 23 modules (provider_toml, permissions, config_toml,
provider_config_keys, provider_chain, hotbar, extension_toml,
stream_timeouts, fleet, service_toml, config_resolve, telemetry,
project_config, model_normalization, base_url_resolution,
runtime_options, config_store, config_backup, state_dirs,
config_migration, display_redaction, path_io, env_overrides); lib.rs
keeps `mod X; pub use X::*;` (pub(crate) for the four modules with no
public items), so every codewhale_config:: path and the tests'
`use super::*` are unchanged. A handful of private fields and methods
widened to pub(crate) for cross-module use. No behavior change.
catalog.rs is untouched. Implemented on vm14 by the split session.
scripts/check-provider-registry.py now reads every crates/config/src/*.rs
(except tests.rs) and finds ProvidersToml in provider_toml.rs. The
blocking-calls budget entry for config/src/lib.rs (std_fs 9,
path_canonicalize 3) is re-keyed to the files the sites moved to:
config_migration.rs std_fs 1, path_io.rs path_canonicalize 2,
state_dirs.rs std_fs 8 and path_canonicalize 1.
Verified on Linux x86_64 Boat VM vm21 (tree 7f443e6164e2, S19+E2 base):
- cargo fmt --all -- --check: exit 0
- cargo check --workspace --all-targets --locked: exit 0, 0 warnings
- CI Lint clippy (--all-targets --all-features -D warnings, CI allows):
exit 0
- cargo test -p codewhale-config --lib: 808 passed, 0 failed, 1 ignored
- python3 scripts/check-provider-registry.py: passed
- check-blocking-calls-budget.py: no config entries; only the
pre-existing uninstall.rs and sandbox overages
Not run: module_graph.py --check, macOS/Windows, hosted CI.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Security finding E2 / 0.10.3 checklist B15: the Codewhale catalog parser
dropped any digest for tarball sources (`sha256: None`), and the Claude
parser recorded `archive.sha256` with an UNVERIFIED_PIN warning but never
checked it, so a catalog could not pin the bytes it installs.
A Codewhale catalog entry may now carry `sha256`; the Claude `archive`
source's `sha256` takes the same path. The pin is folded into the install
spec as a URL fragment (`#sha256=<hex>`, or `&sha256=` after `#path=`),
so it reaches every remote install route (install, InstallExact with
expected_content_hash, update) and the .installed-from marker.
install_remote_bytes compares sha256(bytes) with the pin before staging
and returns PluginArchiveMismatch (Runtime API 409) with nothing written.
A non-string, malformed, or non-archive pin is an INVALID_SHA256 /
INVALID_SOURCE error, so the candidate is not installable (fail closed).
Direct installs accept the same `#sha256=` fragment.
The bundled first-party catalog still names the unpinned
releases/latest Computer Use URL; it is re-pinned once the cu-plugin
release asset exists.
Re-applied onto S19's layout: the patch's crates/tui/src/plugins paths
now live in crates/plugins/src; claude.rs calls
crate::install::PluginInstallSource::pin_archive_spec and
runtime_api/plugins.rs downcasts
codewhale_plugins::install::PluginArchiveMismatch.
Earlier proof (vm13, real binary via `codewhale app-server --http` and a
local Codewhale marketplace pointing at a public codeload archive): wrong
pin -> HTTP 409, nothing installed; malformed pin "abc" -> HTTP 409
INVALID_SHA256, nothing installed; correct pin -> HTTP 201 installed.
Verified on Linux x86_64 Boat VM vm21 (tree a73a3e24cb4a, S19 parent):
- cargo fmt --all -- --check: exit 0
- cargo check --workspace --all-targets --locked: exit 0, 0 warnings
- CI Lint clippy (--all-targets --all-features -D warnings, CI allows):
exit 0
- cargo test -p codewhale-plugins --lib: 201 passed, 0 failed, 1 ignored,
including claude_source_object_matrix_matches_documented_forms (first
run of the updated assertion)
- codewhale-tui lib filtered to runtime_api::plugins plugins: 69 passed,
0 failed
- check-blocking-calls-budget.py: only the pre-existing uninstall.rs and
sandbox overages
Not run: a live install on this exact build, macOS/Windows, hosted CI.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Since Host MCP became the default, text `codewhale doctor` planned the
extension-host sandbox through planned_sandbox, which created
$CODEWHALE_HOME/extension-host/data-builtin (plus data, data/tmp and the
materialized bundle for the plugin tier). That broke doctor's read-only
promise and failed diagnostic_read_only::doctor_text_leaves_a_sealed_home_untouched
and diagnostics_read_home_legacy_settings_without_migrating_them on Linux
(linux-ci-regressions-20261027 group C).
planned_sandbox now creates nothing. It plans only when the tier's data
directory (and, for the plugin tier, data-builtin, data/tmp and the bundle
file) already exist; otherwise doctor prints "host sandbox not determined:
not planned until the host first starts, which would create <path>".
Creation stays on the real launch path (plan_launch / wrap_host). Limit: a
home whose host has never started gets no sandbox verdict from doctor.
The existing launch_plans_carry_their_tier... assertion now expects that
"would create" result instead of a planned Unsandboxed verdict.
Verified on Linux vm22 (origin/main c8e68b9afb + this patch, --all-features,
hermetic test home, CODEWHALE_EXT_HOST_TESTS=1 CODEWHALE_EXT_HOST_BUN_TESTS=1):
- cargo build -p codewhale-cli --bin codewhale: ok
- --test integration diagnostic_read_only::: 14 passed, 0 failed (both
group C tests included)
- --lib extension_host::: 175 passed, 4 failed. The 4 are environmental and
unrelated: two Bun tests need Bun >=1.4.0 (VM has 1.3.14), two compiled
Native tests need CODEWHALE_COMPILED_HOST_TEST_BINARY, which was not set.
- cargo fmt --all -- --check: ok
Not run: clippy, macOS, Windows.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Plugin discovery, review, install, marketplace parsing and the provider
registry move from crates/tui/src/plugins into a new codewhale-plugins
crate (git mv, mod.rs -> lib.rs). The MCP config types (McpConfig,
McpTimeouts, McpServerConfig, McpServerOAuthConfig) move to
codewhale_mcp::config, with reviewed-plugin provenance stored as an
opaque McpProvenance; the skills parse core and recommendations move to
codewhale_skill_install::{skill_parse, recommend}. About 120 tui files
are rewired with no re-export shims. The node composition reviewer stays
in tui behind a OnceLock hook that fails closed ("composition reviewer
is unavailable") if it is not installed. first-party-marketplace.json
and dsh-compatibility.json move to crates/plugins/assets. Tests that
need the extension host stay in tui (plugins_dsh_tests,
plugins_skill_tests, plugins_provider_tests).
Integration onto main: route_preferences.rs conflicted because
0296261823 moved the route-writer block above the test module; the
patch's copy at the old location was dropped and the moved block
repointed at codewhale_plugins (identical otherwise). The
blocking-calls budget entry moved to crates/plugins/src/builtin.rs with
upstream's tightened count. DeclarativePluginFixture gains a Default
impl: as a pub item under --all-features it tripped
clippy::new_without_default.
Verified on Linux x86_64 Boat VM vm21, tree ea13bd15d4a9 on 0d0b2f46af:
- cargo fmt --all -- --check: exit 0
- cargo check --workspace --all-targets --locked: exit 0, 0 warnings
- CI Lint clippy (--workspace --all-targets --all-features -D warnings,
CI's three -A allows): exit 0
- cargo test --lib: codewhale-plugins 201 passed, 1 ignored;
codewhale-skill-install 47; codewhale-mcp 2; 0 failed
- codewhale-tui lib filtered to plugins skills:: mcp:: extension_host::
runtime_threads runtime_api provider_lake provider_picker: 1780 passed,
3 failed, 2 ignored. Failures: runtime_api::tests::mcp_management_
selected_connection_and_retry_preserve_siblings and mcp_tools_endpoint_
is_passive_until_connect_requested (known Linux baseline), and
extension_host::mcp::tests::host_restart_mints_fresh_owner_and_never_
revives_old_grant (bwrap handshake timeout under parallel load; passes
3/3 alone with --exact on the same binary)
- check-blocking-calls-budget.py: only the pre-existing overages in
cli/src/update/uninstall.rs and sandbox/src/{bwrap,lib}.rs
Not run: full tui lib suite, tui integration binaries, macOS/Windows
builds, hosted CI.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Unreleased entry said to install Computer Use from the `codewhale`
marketplace, but no cu-plugin release has the `computer-use.tar.gz` the
marketplace entry points at (it returns 404; release checklist B15), and
codewhale.net/computer-use says the installer is pending. The entry now
says it is a separate experimental macOS plugin whose install steps go
on the Computer Use page once its signed release is published. Restore
the marketplace wording if that release lands before the tag. Spotted
by the marketing session.
Verified on an export of this commit: sync-changelog.sh --check,
check-bundled-plugin-claims.py and check-versions.sh exit 0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
c21e0b1e14 left a second `if known().await { return; }` just before a
block that only compiles outside test builds, so under
--all-features (test-thread-scoped-state + debug_assertions) the return
was trailing and clippy::needless_return failed Lint on main (seen on
#7111). Fold the second known() check into the cfg-gated load_model
condition; behavior is unchanged in both configurations.
Verified: cargo clippy -p codewhale-tui -p codewhale-runtime --all-features
--lib --locked -- -D warnings (CI's allow list) finished clean on this tree
(10m13s); rustfmt --edition 2024 reports no diff.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Linux hosted CI (Test ubuntu-latest, `cargo nextest run --workspace
--all-features`) had 11 failures from 4cf2d2cfff (coordinator diagnosis,
CW/artifacts/linux-ci-regressions-20261027/RESULT.md group A). The move
of settings, paths, catalog cache and credential lookup into
codewhale-runtime gates its test redirect on a feature that
`--workspace --all-features` turns on, so the shipped-shape binary and
the cli tests read `/tmp/codewhale-tui-test-state-*` instead of
HOME/CODEWHALE_HOME. `codewhale_runtime::resolve_state_from_environment()`
(a no-op without `test-support`) is called by the binary's main and the
cli test env_lock, so state comes from the real environment again.
Release artifacts build `-p codewhale-cli --profile dist` without
`--all-features`, so they never had the redirect (resolver 2; the
features are dev-dependency only).
Also: c21e0b1e14 left a `return` in provider_lake.rs that clippy's
needless_return flags when the cfg(not(test…)) block after it is
compiled out (CI's all-features debug build). The second known() check
now guards the model load directly; shipped behavior is unchanged.
Patch by the coordinator session (fix.patch); provider_lake by the
release manager. Verified:
- Linux (coordinator, Boat VM): cli lib 501, cucumber 54,
diagnostic_read_only 12 passing (2 group-C tests remain, routed).
- macOS, export of 39c446a164 + this change, under the build lock:
`cargo clippy --workspace --all-targets --all-features --locked --
-D warnings` (CI Lint allowances) exit 0, 0 errors, 1288 s;
`cargo test -p codewhale-cli --all-features --locked` (hermetic home)
exit 0: lib 501/0 and every integration binary 0 failed.
- rustfmt --check clean on all six files.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
c21e0b1e14 ends refresh_unverified_local_context with a block gated on
not(any(test, all(feature = "test-thread-scoped-state", debug_assertions))).
Under CI's Lint command (clippy --workspace --all-targets --all-features)
that block is compiled out, the second early return becomes the function
tail, and clippy::needless_return fails the gate on main. The return is
needed in every other build, so the lint is allowed only under the cfg
that removes the block.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
New codewhale_release::installs classifies every codewhale, codew,
codewhale-tui, deepseek and deepseek-tui in PATH, the running binary's
directory, ~/.local/bin and ~/.cargo/bin as receipt-owned, unreceipted
Codewhale, backup leftover (nine backup naming schemes, stale
.codewhale-backup-*/-update-* entries), package-managed (exact manager
command; Cargo bins only when .crates.toml lists them), desktop app, or
not ours (symlinks, other users, other programs such as codewhale-go).
Nothing found is executed: ownership is name grammar plus native magic
plus a compiled-in marker string, read with a bounded scan.
- `codewhale doctor` gains an Installations section; `doctor --installs`
(`--json`, `--brief`) prints only that, before config loads.
- `doctor --fix` adds MoveInstallLeftover, AlignAlias, AdoptInstall and
PruneRecovery to the one existing plan. Moves go to
~/.codewhale/recovery/<UTC stamp>/ with a manifest, each printing its
undo; recovery folders older than 30 days are deleted by a later --fix.
The plan prompt is now [Y/n] (EOF answers no).
- `doctor --restore [STAMP]` moves a folder back; an occupant at the
original path is itself moved into a new recovery folder.
- `uninstall` lists other copies through the inventory; its private
manager_command moved to installs::manager_command (report_path_copies
replaced).
Receipt adoption refuses a group- or world-writable directory, where
`uninstall`'s ownership checks would later reject the receipt.
Verified on Linux Boat vm16 (debug build), temp HOMEs only:
- this commit's tree: cargo check -p codewhale-cli -p codewhale-tui clean;
the full series: cargo build -p codewhale-cli and
cargo check --workspace --all-targets --locked with 0 errors and
0 warnings; codewhale-release tests 57 passed; feature_registry 2 passed.
- fixture mirroring the founder's machine (7 backup schemes plus
.codewhale-old-pid, a stale .codewhale-backup-* dir, a divergent codew,
codewhale-go, a symlink, a Cargo root listing codew/codewhale with a
stray backup, a Homebrew Cellar symlink, a marker-bearing script
sentinel): `doctor --installs` and `--json` classify every entry as
designed; `doctor --fix --yes` moved 9 items (6.2 GB) into one recovery
folder with per-item undo lines, aligned codew and wrote the receipt; a
second --fix plans no installation work; package-managed, foreign and
symlink entries hash-identical before/after; the sentinel never ran;
`doctor --restore` put all 9 back byte-identical.
Not run here: the tui doctor_fix unit tests (the VM ran out of memory
linking the tui test binary).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
e5283cb022 landed an unformatted condition in InstallMethod detection, so
cargo fmt --check (Lint) failed on main and on every PR merged with it
(seen on #7111). Pure rustfmt output; no behavior change.
Verified: rustfmt --edition 2024 on the file yields this exact content; the
CI Lint log reported this as the only fmt diff.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Route /undo outcome messages through the presentation facet and provide
translations in all 15 complete locale packs, following the /diff
localization in 887a3c669. English output stays byte-identical: the
exact-string host tests pass unchanged.
Refs #7071 (undo portion).
Signed-off-by: Boyce <[email protected]>
Picks up the protocol clippy fix and CHANGELOG receipts so required checks run against current main.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#7114: /provider counted every known id as configured because
provider_config_table!(@read) is always Some once [providers] exists, so a
fresh home showed a phantom "Antigravity (legacy, disabled)" row. The picker
now asks provider_is_configured, the same resolver the rest of the runtime
uses. A home whose only configured row is a keyless local runtime opens on
the full catalog (DeepSeek etc.) instead of Configured, and Shift+A toggles
the view like a.
#7113: at dispatch an unloaded Ollama model has no /api/ps context, so the
route fell back to 8192 and the second turn was refused with "~5120 tokens
usable". refresh_unverified_local_context now asks the daemon to load the
model (POST /api/generate, no prompt) when the context is still unknown and
refreshes again. The dispatched turn's route limits are carried back into the
App so the footer and compaction budget read the same window (it showed
"context 100%" and the /compact nag before).
#7116: provider_lake::models_for_provider also lists a provider whose own
endpoint has answered a live listing, so a keyless Ollama that is not the
active route shows its installed models in /model and
GET /v1/providers/ollama/models. One resolver: the runtime API and model
inventory both already go through models_for_provider.
No-key default kept as Ollama adoption: a keyless DeepSeek default cannot
answer a turn; with #7114 DeepSeek is now one Enter away in /provider.
Evidence (dev build, isolated env -i CODEWHALE_HOME):
- /provider on a fresh home: opens "Provider · all", no Antigravity row,
Shift+A -> configured view, a -> back.
- qwen3:4b unloaded (/api/ps empty): turn 1 read+patch calc.py, turn 2 wrote
notes.md; both completed, footer context 34% then 52%; /api/ps 32768.
Before: turn 1 failed with "~5120 tokens usable".
- serve --http, DeepSeek active: after /v1/providers/ollama/models/refresh,
GET /v1/providers/ollama/models lists qwen3:4b (context 32768); was [].
- scripts/dev-cargo.sh test -p codewhale-tui --lib -- provider_picker
provider_lake ollama_catalog list_provider_models provider_models dispatch
context_usage: 443 passed; 0 failed.
Paths: crates/tui/src/tui/provider_picker.rs, crates/tui/src/provider_lake.rs,
crates/tui/src/client/ollama_catalog.rs, crates/tui/src/tui/ui/dispatch.rs,
crates/tui/src/tui/ui.rs (UserDispatchOutcome field only), CHANGELOG.md and
crates/tui/CHANGELOG.md (one line each).
Refs #7113, #7114, #7116
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Add InstallMethod::DesktopApp for an Engine inside a macOS
`*.app/Contents/MacOS/` bundle or the desktop app's pinned
`<CODEWHALE_HOME>/gpui/engine/` store. It does not support self-update,
so `codewhale update`, target validation and `uninstall` refuse to write
there, and the CLI, /update and the TUI notice say to use
Codewhale > Check for Updates instead of printing GitHub migration help.
Verified on a Linux Boat VM against origin/main plus this series (debug build): cargo build -p codewhale-cli and cargo check --workspace --all-targets --locked clean (0 errors, 0 warnings); cargo test -p codewhale-release 57 passed; codewhale-cli update:: 109 passed. Fixture in a temp HOME: `update` from X/Codewhale.app/Contents/MacOS/codewhale prints the desktop message and exits 1, `uninstall` names the app, bundle bytes unchanged. Not exercised: a real signed desktop bundle on macOS.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`cargo fmt --all -- --check` failed on main at 0e9c5beba5 with diffs in
crates/tui/src/runtime_api/git.rs (hunk patch parsing, cf10ca1446) and
crates/tui/src/runtime_api/thread_delete.rs (DELETE /v1/threads). The
CI Lint job runs that check first, so the release candidate's CI would
fail on formatting. Formatting only; no code change.
Verified: `cargo fmt --all -- --check` exits 0 on an export of this
commit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Security review E5 (codewhale-ops/security/PRE-LAUNCH-REVIEW-20261027.md;
founder-accepted D13, 2026-10-27). In workspace-write mode a sandboxed
command could write .git/hooks/* or .git/config (for example
core.hooksPath), which later runs outside the sandbox on the next git
operation; and .codewhale/ was protected only if it already existed.
- Every writable root now gets `.git/hooks` and `.git/config` as
read-only subpaths when it holds a git dir; a linked worktree's
gitdir/commondir roots get their own `hooks` and `config`.
- Workspace roots (cwd and configured writable roots) always protect
`.codewhale/`, even before it exists. Seatbelt rules work on
nonexistent paths; their parameter now resolves through the
canonical parent so /var vs /private/var cannot slip past.
- bwrap can only bind existing paths: prepare_bwrap creates the
workspace's `.codewhale/` first, and read-only exceptions may now be
files (`.git/config`), not only directories.
- The seatbelt policy test matched the old single-exclusion text
exactly; it now checks the root-plus-first-exclusion prefix.
Verified with an uncommitted probe that runs one sandboxed shell
script against a fresh repo (hook write, config append, .codewhale
creation, ordinary file write, git commit), before vs after:
- macOS Seatbelt (local, build lock): before all WROTE; after hooks,
config and .codewhale DENIED, file write and git commit OK, no hook
left behind. codewhale-sandbox tests: 104 passed, 0 failed.
- Linux bubblewrap (Boat vm20, Ubuntu 24.04): same before/after split.
codewhale-sandbox tests: 100 passed, 0 failed.
Logs: CW/artifacts/release-0103-gates-20261027/e5-mac.log, e5-vm20.log.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
cf10ca1446 (security-review) clamps a server ttlMs to at least 60 s so
ttlMs: 0 no longer revalidates every supervision sweep; the freshness
bullet still said only "capped at 24 hours".
Verified on the shared tree carrying the same mcp.rs: scripts/dev-test.sh
tui mcp extension_host 736/736 passed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pre-launch security review 2026-10-27 (two of its findings, plus hygiene):
- mcp: a server-supplied list ttlMs had no floor, so ttlMs 0 made the
catalog stale on every 5s tick, and revalidation runs discovery while
holding the MCP pool lock. Any configured server could keep every MCP
operation waiting. Catalog TTLs are now clamped to at least 60s.
Still open: running discovery outside the lock, and rate-limiting
list_changed-driven revalidation.
- runtime_api/git: validated_patch sliced the first byte off each @@
range token, which panicked on a multi-byte character and fired the
process panic hook. It now strips the expected -/+ sign and refuses
anything else as a malformed @@ line.
- .gitignore: ignore private key and certificate files, with an
exception for the existing test-only fixture key.
Verified: scripts/dev-test.sh runtime_api/git.rs git_ -> 146 tests run:
146 passed (includes git_routes_drive_a_real_workspace_repo and
git_write_preconditions_reject_stale_state); scripts/dev-test.sh mcp.rs
mcp:: -> 405 tests run: 405 passed. No new tests, per repo policy.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The installer deployed at codewhale.net/install.sh predates the receipt
and writes no .codewhale-install.sha256. web/public/install.sh on main
already writes one. As a result, `codewhale uninstall` refused every copy
installed through the main channel ("No command ownership receipt").
When there is no receipt, uninstall now adopts the copy, but only if:
- its directory is an install.sh default (~/.local/bin,
~/.local/codewhale/bin, ~/.local/codewhale-release.*, or
%USERPROFILE%\bin on Windows), and
- each command is a regular file owned by this user and byte-identical
to the running program (the release ships codewhale and codew as one
binary).
The preview lists exactly those files and changes nothing. --yes writes
the receipt with GuardedFile::stage + move_to_vacant (no clobber), then
runs the normal receipted path, which re-verifies each hash before it
unlinks anything. A modified or foreign codew is left out and reported.
Any other unreceipted directory is still preserved.
Verified on macOS arm64, temp HOMEs only, binary 16bb0efa6079:
- Live script, run as `curl -fsSL https://codewhale.net/install.sh | sh`
with CODEWHALE_RELEASE_BASE_URL set to a file:// release of the
candidate binary: 12 passed, 0 failed. Covered: default dir with no
receipt; preview changes nothing; --yes removes both commands and
leaves no receipt or leftovers; data and an unrelated file preserved;
live reinstall then a second removal; ~/.local/codewhale/bin adopted; a
modified codew preserved and reported; a custom CODEWHALE_INSTALL_DIR
refused; a foreign codew preserved byte-identical.
- Receipted installs from web/public/install.sh (preview, --yes, repeat,
reinstall, two copies, tampered alias, manual copy, cargo, npm, brew,
scoop, winget and nsis layouts, purge): 27 passed, 0 failed.
The adoption hunk is applied to main's uninstall.rs as-is. The build
that ran is the shared tree, which also carries another writer's
uncommitted install-cleanup work in the same file. That work is not
included here.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`cargo clippy --workspace --all-targets --all-features -- -D warnings`
(the CI Lint flags) fails on macOS with clippy::collapsible_if at
crates/tui/src/extension_host/supervisor.rs:919. The block is
macOS-only, so the Ubuntu Lint job never sees it, but the release's
macOS lanes and local release gates do. Collapsed into a let-chain;
there is no else branch, so behavior is unchanged.
Verified: rustfmt --check clean. The locked clippy run on an export of
1affdc5fda reported this as its only error (exit 101, 2067 s). The same
hunk on 6793371e4e (091c3e59c0) passed `cargo clippy --workspace
--all-targets --all-features --locked -- -D warnings` with the CI Lint
allowances on macOS: exit 0, 0 errors, 1473 s. Replayed unchanged onto
7bde746261; that commit's own supervisor.rs hunk was not re-linted here.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On Linux the Native host probe ran `<runtime> --version` inside bwrap and
treated any failure as "no verified OS sandbox", a hard error raised while
preparing the launch, before runtime = "auto"'s Bun-to-Node fallback (which
only covers spawn failures). A runtime that is a script or shim (asdf's bash
shims; the test's #!/bin/sh fake Bun) cannot execute in the sandbox because
its interpreter is not among the Native read grants, and bwrap reports
`bwrap: execvp <runtime>: No such file or directory`. bwrap has already built
its namespaces at that point, so the sandbox is verified: the probe now
accepts that line, the real host spawn fails as an ordinary runtime start
failure, and auto falls back to Node with its existing diagnostic. Explicit
bun or node still never fall back; genuine bwrap failures (namespaces, uid
map, missing binary, timeout) remain hard errors.
Paths: crates/tui/src/extension_host/supervisor.rs (bwrap_probe_verdict, its
doc comment, one assertion added to the existing
missing_verified_sandbox_refuses_native_but_reports_pinned_builtin_exception body)
Verified by the release manager on Boat vm19 (Ubuntu 24.04 x86_64, bwrap,
Node 24.22, Bun 1.3.14), origin/main d02e14806b + this patch + the
memory_cap message fix: cargo test -p codewhale-tui --lib --locked -- --exact
auto_uses_node_for_the_session_when_the_bun_host_fails_to_start,
supervisor::tests::missing_verified_sandbox_refuses_native_but_reports_pinned_builtin_exception,
memory_cap_stops_a_node_host, memory_cap_stops_a_bun_host: 4 passed, 0 failed
(61.26 s). Log: CW/artifacts/release-0103-gates-20261027/vm19-memcap-bwrap.log.
Before (vm18, 5f59494913): auto_uses_node failed with "Native extensions
require a verified OS sandbox: bwrap unavailable (bwrap: execvp .../bun: No
such file or directory)".
Co-Authored-By: Claude Opus 5.5 <[email protected]>
memory_cap_stops_a_node_host failed on Linux x86_64 (Ubuntu 24.04,
bubblewrap, Node 24.22): the RLIMIT_DATA cap worked, the kernel refused
the 2 GiB allocation and the tool failed cleanly inside a live host,
but Node reports it as "RangeError: Failed to allocate memory", which
the assertion did not accept (it only matched "allocation failed").
The Linux/Windows branch now accepts both messages; enforcement is
unchanged.
Verified on Boat vm19 (Linux x86_64, bwrap) at d02e14806b with this
change and the DSH bwrap-execvp probe patch applied:
`cargo test -p codewhale-tui --lib --locked -- --exact` for
memory_cap_stops_a_node_host, memory_cap_stops_a_bun_host,
auto_uses_node_for_the_session_when_the_bun_host_fails_to_start and
supervisor::tests::missing_verified_sandbox_refuses_native_but_reports_pinned_builtin_exception:
4 passed, 0 failed. Before, on vm18 at 5f59494913: memory_cap failed
with the RangeError message.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
QA observed the 413 recovery on a local mock (six 640x480 images,
CW/artifacts/qa-20261027/b5/): request 1 was 3,484,230 bytes and got a
413; exactly one retry was sent at 53,212 bytes with 0 images and 6
omission notes (receipt image_projection: cause initial_http_413,
omitted 6, retained 0). Saved images were unchanged. The entry implied
the retry dropped only older images; older-first omission is the
separate local byte-budget path. Wording now matches both paths.
Verified on an export of this commit: sync-changelog.sh --check,
check-feature-release-notes.sh v0.10.2 HEAD and check-versions.sh exit 0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
CONFIGURATION.md (en and zh_hans) said composer_multiline_mode makes
Shift+Enter send and leaves Alt+Enter unchanged. KEYBINDINGS.md and the
binary disagree: Alt+Enter sends, Shift+Enter sends only where the
terminal reports it distinctly, and Ctrl+J inserts a newline.
Observed on Linux (Boat vm18, tmux, debug build of 2a4406a57d, whose
TUI input code matches main) with composer_multiline_mode = true:
Enter and Ctrl+J inserted newlines, zero requests reached a local mock
model while composing, and Alt+Enter sent one user message
"alpha\nbeta\ngamma" (#7093 / CW-7551 release check B6).
Docs only; no checks run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
DELETE /v1/threads/{id} (new runtime_api/thread_delete.rs) removes an
idle thread: its turns, items, events file, goal, seed journal and
approval grants, and stops its cached engine, all under the active-
thread lock and the store's thread mutation lock. It refuses with 409
while a turn runs, and while an active automation or a pending or
dispatching trigger delivers into the thread (naming them, so the
client cancels or pauses them first rather than leaving a schedule that
would fail at fire time). The saved session bound to the thread is
deleted too unless another thread is bound to it; if the session store
refuses (an open terminal holds it), the thread is still deleted and
the answer says session_deleted: false with the reason. Tasks and child
threads that name the deleted id keep it (documented).
POST /v1/threads accepts parent_thread_id naming an existing thread
(400-class error otherwise), complementing 0da2b3d704's fork links so a
client can nest a conversation it creates. Registry row thread-delete;
the two Unreleased changelog lines picked up by ccda707286 now describe
the landed `patch` form of hunk writes, not the superseded `hunks` one.
Built on origin/main from the shared checkout's verified edits (the
checkout was 3 ahead/82 behind with other writers' work, so this commit
was assembled in a temporary index and contains only these 9 paths).
Evidence (shared working tree = these edits + others' uncommitted work):
RUST_MIN_STACK=8388608 cargo test -p codewhale-tui --lib --
fork summary create_thread delete runtime_api::git tools::tasks
test result: FAILED. 271 passed; 2 failed; 1 ignored. Both failures are
tools::subagent::{limits_tests::root_fork_of_depth_two_leaf_cannot_regain_a_generation,
completion_usage_tests::completion_usage_counts_real_manifest_only_root_fork},
matched by the "fork" filter, in the DSH session's uncommitted
tools/subagent edits; this change touches nothing there.
cargo check --workspace --all-targets fails only on dead-code in that
same uncommitted tools/subagent/mod.rs and tools/codemode.rs.
feature_registry was queued behind the build lock and had not run when
this was committed. No live Engine run of the delete route.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
fix(catalog): read the token limits a live model listing states. Required checks green on 233f590fe5 (Lint, Test (windows-latest), npm wrapper smoke). CW-7525.
fix(fleet): enable rename_exclusive on Android to prevent SELinux EACCES linkat fallback. Required checks green on eb4539fe30; maintainer merge ported the fleet/files.rs hunks to crates/util/src/files.rs after S14. libc 0.2.191 defines SYS_renameat2 and RENAME_NOREPLACE for every Android arch; author verified on an Android 6.6 device. CW-7525.
Both are thanked inline in Unreleased (#7043, #6992) but were missing
from the release's Contributors block, which the GitHub Release body
reuses. Spotted by the marketing session. crates/tui/CHANGELOG.md
regenerated with scripts/sync-changelog.sh.
Verified on an export of this commit: sync-changelog.sh --check,
check-contributor-credit.py v0.10.2 and check-versions.sh exit 0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
DeepSeek retired V4 Flash on 2026-09-10 and is phasing out V4 Pro
(codewhale-ops/engineering/DEEPSEEK-MODELS-20261027.md, from DeepSeek's
own model and changelog pages). The FAQ's OpenRouter example now uses
deepseek/deepseek-v4.1-flash (the slug in provider_descriptors.json)
instead of deepseek/deepseek-v4-pro and deepseek/deepseek-v4-flash, and
the configuration guide's --set example uses deepseek-flash instead of
deepseek-v4-pro, in English, Chinese and the GT catalogs. Test fixtures
and the ratatui capture keep their IDs; they are not copy.
Checks: tsc --noEmit exit 0; eslint on the three source files exit 0;
check-locales PASS; both catalogs parse; /en and /zh faq and
docs/configuration return 200 on the local dev server with the new IDs
present and no deepseek-v4-pro or deepseek-v4-flash in the HTML.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
prepare-release.sh snapshots every file it may rewrite and restores them
if any later gate fails, but web/data/agent-docs.generated.json was not
on that list. derive-facts.mjs regenerates it from docs/INSTALL.md, so a
failed `prepare-release.sh 0.10.3` (for example, before the changelog
section exists) left that tracked file holding the bumped INSTALL.md
content in the shared checkout. Found in a 0.10.3 bump dry run on a
copy of 9f6999f99d.
Verified: scripts/release/prepare-release.test.sh exits 0 on an export
of origin/main with this change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PR #7108 by @ddlw merged as bdf71d6df4 and repaired main's clippy,
rustfmt, extension-host imports, provider-registry check and Windows
DSH receipt bytes. check-contributor-credit.py then failed with
"@ddlw (commit author) -- not in: docs/CONTRIBUTORS.md, CHANGELOG.md,
web/lib/release-credits.ts". ddlw is now credited on all three
surfaces; crates/tui/CHANGELOG.md and web/data/agent-docs.generated.json
are regenerated.
The blocking-calls ratchet also failed on main (crates/tui/src/plugins/
builtin.rs at 0 path_canonicalize sites against a budget of 1; the
crate split moved sites into snapshot, repl, dependencies and the CLI
uninstall module). ratchet-gate.sh blocks push/dispatch runs, so the
frozen candidate's CI would fail. Regenerated with --update.
Verified on an export of 2a4406a57d plus these files:
check-contributor-credit.py v0.10.2 ("Every contributor in the window
is credited on all three surfaces"), check-blocking-calls-budget.py
("557 sites across 166 files, within budget"), sync-changelog.sh
--check, check-versions.sh, check-bundled-plugin-claims.py, and web
check:facts / check:docs all exit 0. Before: credit exit 1, blocking
calls exit 1.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up #7108, which restores main's clippy and rustfmt gate (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A separate page for Codewhale Enterprise (CURRENT_DECISIONS §57) that
lists what it will deliver, not how: evals that define done, training
data from your team's accepted work, a fine-tuned open-weight model you
own, and RL environments that keep improving it, each tested on your own
holdout. Available today: the open agent for any model (including
self-served vLLM, SGLang and Ollama) and saved sessions with receipts.
Planned: managed eval and training environments, qualification on each
new base model, routing between rented and owned models, private
deployment. It says no customer model has been trained, names no price
or customer, and keeps the method private (founder, 2026-10-27). Calls
to action: a design-partner email to [email protected] and /install.
English and Simplified Chinese; other locales fall back to English
(enterprise.ts is an optional locale file). The type lives beside the
English dictionary so types.ts, which another session is editing, stays
untouched. Added to the sitemap; no nav or footer link yet.
Updates lib/docs-ia.test.ts: the sitemap now has 112 entries (110 + /enterprise in en and zh).
Checks: check-locales PASS; vitest lib/docs-ia.test.ts 19 passed; tsc --noEmit exit 0; eslint on the touched
files exit 0; vitest page-meta + i18n dictionaries + locale-layout
8 files, 72 passed; /en and /zh/enterprise render on the local dev
server (HTTP 200, no console errors, no overflow at 375 px). Full npm
run check and a deploy not run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The site painted with the Moda brand kit (navy grounds, signal-blue actions)
over tokens 1.1.2, while the desktop app, web app and mobile run tokens 2.3.0
(pearl and charcoal grounds, ink filled actions, one slate accent). Founder
direction 2026-10-27: one design system on every surface. This supersedes
the 2026-10-11 Moda-kit color decision (web/docs/BRAND_COLOR_MAPPING.md is
rewritten to say so).
- vendor/codewhale-design is synced to 2.3.0 with codewhale-design's
scripts/sync-to.sh (generate.py --check passes). Only
scripts/export-design-tokens.py reads it in this repository.
- The exporter now emits the 2.x roles (accent distinct from hover,
ocean_depth, diff_add, diff_remove) and the type roles, motion durations
and loops, easing curves, popover and dialog elevation, breakpoints and
the logo ombre as --gpui-* custom properties.
- tokens-roles.css names every site role as a var(--gpui-*): light, pinned
dark and the stage (footer and install band, the dark set in both
appearances). Type, spacing, radius and motion roles bind to the tokens;
the fluid editorial display sizes stay as the site's own layout. The logo
blues remain only on the whale and the mark.
- The home proof strip keeps the release, source marker, licence and live
contributor count; the source-tree tool and provider-route counts are
removed, because a source count is not a released total
(lib/public-copy.test.ts) and marketing asked for no provider count.
- The locale layout comment no longer claims the site follows the OS
appearance; it is light unless dark is pinned.
- Contract tests that pinned the Moda kit inks, the blue sea and tide ramps
and literal easing/prose sizes now assert the token roles instead; the
WCAG AA check over every ink and ground in every scheme is unchanged and
passes.
Verified: check:latest-release, prebuild, check:docs PASS, check:tokens up to
date (158 tokens), eslint clean, tsc --noEmit clean, check:locales PASS,
next build compiled with 1011/1011 static pages. vitest: 770 passed,
1 failed: docs-ia sitemap count (112 vs 110) comes from the unpushed local
/enterprise commit a6b26b5c3e, not from this change. check:facts fails
in this checkout because its tree is behind origin/main's regenerated
feature registry; not touched here. Screenshots (1440 and 390, light and
pinned dark, home, product, docs, install):
artifacts/design-20261027/after-tokens-2.3.0/.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up #7108, which restores main's clippy and rustfmt gate (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up #7108, which restores main's clippy and rustfmt gate (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up #7108, which restores main's clippy and rustfmt gate (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up #7108, which restores main's clippy and rustfmt gate (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up #7108, which restores main's clippy and rustfmt gate (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up #7108, which restores main's clippy and rustfmt gate (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up #7108, which restores main's clippy and rustfmt gate (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up #7108, which restores main's clippy and rustfmt gate (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
style(tui): apply rustfmt to mcp and restore the lint gate. Required checks green on f8a6346159 (Lint, Test (windows-latest), npm wrapper smoke). Maintainer commits on the branch: feature-gated Ollama dead_code expectation, Windows unused mut, merges of main incl. 715bb2ed02 resolved to the branch's gating. Supersedes #7106 (same mcp.rs rustfmt) and #7109 (.gitattributes line now included). CW-7525.
2b898fb041 and c5d45b48df fixed the same two main failures that
contributor PR #7108 (@ddlw, with maintainer commits) already fixes,
and made that PR conflict in scripts/check-provider-registry.py. Main
now carries #7108's exact version of the script (a regex accepting
`pub` or `pub(crate)`) and its exact "First-party marketplace receipt"
changelog line in the same position, replacing the duplicate bullet,
so #7108 merges cleanly as itself with no rebase on the contributor's
side.
Verified on an export of this commit: check-provider-registry.py,
check-bundled-plugin-claims.py, check-contributor-credit.py v0.10.2,
check-versions.sh and sync-changelog.sh --check all exit 0, and
`git merge-tree` of this commit with origin/pr-7108 (f8a6346159)
reports no conflicts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
c5d45b48df added an Unreleased band to docs/CONTRIBUTORS.md, which
web/data/agent-docs.generated.json embeds, so check:facts failed with
"generated agent docs are stale". Regenerated with `npm run prebuild`.
Verified on an export of c5d45b48df with this file regenerated:
`npm run check` passed check:facts, check:latest-release, prebuild,
check:docs, check:tokens, eslint and tsc --noEmit; `npm run build`
exit 0 (next build). Only the CONTRIBUTORS source entry changed
(content and sha256).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Fixes the three release-note gates that fail on main:
- check-feature-release-notes / check-versions: commit 4cf2d2cfff
references #7093 (multiline send key on Linux) and #7097 (relocated
state root) with no changelog receipt. Both now have Unreleased entries
describing the shipped source behavior.
- check-bundled-plugin-claims: the first-party marketplace pin
a5f1d21 (CW-6732, Computer Use runtime removed from the marketplace
bundle) was never named in CHANGELOG.md.
- check-contributor-credit: Boyce (@Aboysky, #7104) was uncredited.
AUTHOR_MAP maps the commit email to the GitHub account; the changelog,
docs/CONTRIBUTORS.md (new Unreleased band) and UNRELEASED_CONTRIBUTORS
credit the window's code contributors.
The Unreleased section gains a Contributors block so the 0.10.3 cut
only has to rename the heading. crates/tui/CHANGELOG.md regenerated by
scripts/sync-changelog.sh.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The homepage opened on a headline beside an empty column, then a sponsored
banner, and only then the product. A first-time visitor now sees the
captured Codewhale TUI at the fold, with the live whale resting on its top
edge, then a proof strip read from facts: latest release, licence,
contributor count, built-in tool count and provider-route count. The
OrcaRouter offer moves beside the models section it relates to, and the
0.10.3 highlights move below availability, so first-read sections are the
product and its uses rather than the changelog.
- The hero is tighter (smaller title, less padding) so the terminal shows
at the fold at 1440x900 and on a 390px phone.
- The terminal caption no longer calls the released v0.10.2 capture
"Unreleased"; it says so only when the capture version is unpublished.
- On screens narrower than 30rem the copy button keeps its accessible name
but shows only its icon, so more of the install command is visible.
- Three new proof labels are translated in all 18 locales; the GT catalog
is re-exported.
Verified: tsc --noEmit clean; eslint . clean; check:locales PASS (GT catalog
OK); check:tokens up to date (150 tokens); check:docs and check:latest-release
pass; next build compiled, 993/993 static pages. check:facts fails on main's
working tree because another session's in-progress feature-registry edits
are not regenerated yet; not caused by this change. Before/after
screenshots (desktop 1440 and mobile 390, light and pinned dark, en and zh):
artifacts/design-20261027/{before,after}/.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The 0.10.3 combined batch (4cf2d2cfff) moved the four provider identity
functions into crates/runtime/src/config.rs as `pub fn`, but
scripts/check-provider-registry.py still demanded `pub(crate) fn`, so the
Lint job's provider-registry step failed on main with four "missing
canonical config identity boundary" errors. The check now names the
signatures that exist.
Verified: python3 scripts/check-provider-registry.py exits 0 ("Provider
registry drift check passed.") on a git-archive export of origin/main
9f6999f99d with this one file patched, and on the canonical working tree.
Before the patch the same export exited 1 with the four errors.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
docs/INSTALL.md says Windows removal is compile-checked but not yet
exercised on a Windows host (e66998bec3 also records that nothing ran on
Windows). The homepage uninstall item now says so in en and zh.
Raised by the marketing session.
Path: web/lib/content/release-highlights.ts.
Verified: tsc --noEmit and eslint on the file pass; dev server /zh and
/en render the caveat in the uninstall item. Not deployed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The site's prebuild failed on main since 0da2b3d704: derive-facts only
accepted tui/api/web feature surfaces, while the Engine registry
(crates/tui/tests/feature_registry.rs) also allows desktop, which the new
task-suggestions row uses. The web validator and Feature type now accept
the same four surfaces. The features page showed since = "unreleased" as
"vunreleased"; it now shows the existing Unreleased label.
Docs, from CHANGELOG Unreleased and source on main:
- KEYBINDINGS: Ctrl/Cmd-Enter steers (closes the in-flight request and
continues the turn; a second Enter within 500 ms of queueing also steers,
per App::enter_with_double_tap); Tab and Shift+Tab work mid-turn and apply
at the agent's next step (CW-7552).
- MCP: Connection Lifecycle describes the SDK host backend, server/discover
and the legacy initialize fallback (CW-6713).
- CONFIGURATION: documents [extension_host] mcp_backend ("host" default,
"rust" for one release, no fallback), per crates/config McpBackend.
Homepage "Coming in v0.10.3": adds "Steer mid-turn"; uninstall now covers
Unix and Windows and --purge-data (e66998bec3).
Regenerated web/data/features.generated.json, agent-docs.generated.json and
lib/facts.generated.ts from an export of origin/main 22e341107e plus these
edits (crates=39 after the split), so other writers' uncommitted registry
rows are not included.
Verified: in that export, npm run check passed check:facts,
check:latest-release, prebuild, check:docs, check:tokens, lint and tsc
(next build there hit Turbopack's symlinked node_modules refusal); next
build in the canonical tree: 993/993 pages. Browser (dev server on the
canonical tree): /en and /zh show 9 v0.10.3 items, /en/features shows
task-suggestions "DESKTOP: Preview · Unreleased", no "vunreleased", no
console errors. Not deployed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Maintainer merge to bring the branch onto current main (no conflicts).
Main now carries the Windows/rustfmt CI fixes, #7099 (model facts inherit
across a provider host move) and the crate-split moves.
Verified on main+this branch (Linux x86_64 Boat VM): cargo check -p
codewhale-tui --lib --tests clean; focused tui run (client::, catalog,
provider_lake, route_budget) 933 passed, 3 failed under full-suite load;
those 3 (create_message_scenario, roster_omission_stands_unless_the_
payload_explicitly_attests_the_id, model_added_endpoint_cannot_use_
ambient_proxy_but_operator_can) pass when run alone here and on main.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up 22e341107e, which clears the crates/tui/src/lib.rs rustfmt
drift main carried, and #7099 (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up 22e341107e, which clears the crates/tui/src/lib.rs rustfmt
drift main carried (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up 22e341107e, which clears the crates/tui/src/lib.rs rustfmt
drift main carried (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up 22e341107e, which clears the crates/tui/src/lib.rs rustfmt
drift main carried (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up 22e341107e, which clears the crates/tui/src/lib.rs rustfmt
drift main carried (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up 22e341107e, which clears the crates/tui/src/lib.rs rustfmt
drift main carried (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up 22e341107e, which clears the crates/tui/src/lib.rs rustfmt
drift main carried (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up 22e341107e, which clears the crates/tui/src/lib.rs rustfmt
drift main carried (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Picks up 22e341107e, which clears the crates/tui/src/lib.rs rustfmt
drift main carried (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A plain `codewhale` launch deliberately starts fresh. When it found an
in-flight session snapshot (a session whose turn never finished, which
exists only under ~/.codewhale/sessions/checkpoints/), the only pointer to
`codewhale --continue` was an eprintln! printed just before the TUI took
the alternate screen, so a user who closed the terminal mid-task reopened
to an empty session and no visible way back (#7095).
`preserve_interrupted_checkpoint_for_explicit_resume` now returns that
message and the launch passes it as the TUI startup notice; an auto-resume
receipt still takes precedence. Its doc comment no longer claims
checkpoints are persisted as sessions (untrue since #4479).
Also applies rustfmt to the `mod usage_persistence` line 98104b9737
left out of order, the only `cargo fmt --all -- --check` drift on
origin/main at a19f2d8885.
Not changed here (follow-ups): the SIGHUP/SIGTERM path exits without
flushing the persistence actor; the resume picker and `codewhale sessions`
do not list checkpoint-only sessions; the 24h checkpoint prune can remove
them.
Evidence (on an export of #7108's merged head cee2d8ab7c plus these lib.rs
hunks, since origin/main codewhale-tui did not compile at the time):
- scripts/dev-cargo.sh check -p codewhale-tui --lib --locked: rc=0, no
errors or warnings.
- scripts/dev-cargo.sh test -p codewhale-tui --lib --locked -- checkpoint:
test result: ok. 79 passed; 0 failed (includes
plain_launch_preserves_checkpoint_but_starts_fresh,
plain_launch_consumes_legacy_checkpoint_after_preserving_it,
continue_without_interactive_terminal_leaves_checkpoint_for_a_real_launch).
- cargo fmt --all -- --check on origin/main + this commit: clean.
Not run: a live TUI launch with a real interrupted session.
CW-7525.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main moved crates/tui/src/fleet/files.rs to crates/util/src/files.rs (S14,
CW-6129); the three Android cfg changes are applied to the moved file.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Maintainer merge so the required checks run against current main
(main was red when this branch last ran CI; CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Maintainer merge so the required checks run against current main
(main was red when this branch last ran CI; CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Maintainer merge so the required checks run against current main
(main was red when this branch last ran CI; CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Maintainer merge so the required checks run against current main
(main was red when this branch last ran CI; CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Maintainer merge so the required checks run against current main
(main was red when this branch last ran CI; CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Maintainer merge so the required checks run against current main
(main was red when this branch last ran CI; CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Maintainer merge so the required checks run against current main
(main was red when this branch last ran CI; CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
715bb2ed02 fixed the same Windows and rustfmt breaks on main with
attributes; this branch fixes them by gating, so each conflict keeps the
branch side: `native_snapshot_roots` is `cfg(not(windows))` on HostLaunch
and HostProcess, `load_global_context_file` is `cfg(all(test, unix))`, and
the Ollama probe helpers keep the feature-gated `expect(dead_code)`.
main's `cfg_attr(windows, allow/expect(dead_code))` lines are dropped; the
mcp.rs rustfmt hunks were identical on both sides.
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Brings in #7104's README stamp refresh, which the Lint leg's README
translation check needs. On Windows `plan_launch` never assigns
`native_snapshot_roots` (the field is `cfg(not(windows))`), so
`let mut launch` failed `unused_mut` under -D warnings on Test
(windows-latest); the non-Windows assignment is now a struct update.
Maintainer fix on the contributor branch (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The probe functions are dead only in a non-test debug build with
`test-thread-scoped-state` on (the workspace test build), where
`spawn_live_local_ollama_probe` compiles its no-op arm. Without the
feature (`cargo build -p codewhale-cli --all-features`, the MSRV leg) they
are live, so `expect(dead_code)` keyed on `debug_assertions` alone failed
as an unfulfilled expectation on Test (ubuntu-latest) and MSRV.
Maintainer fix on the contributor branch (CW-7525).
Signed-off-by: CodeWhale Bot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`custom_catalog_schema_ignores_table_name` pinned the generic branch's old
verdict wholesale, `limit.is_none()` included. The point of that assertion is
the Baseten enrichment -- pricing and `supported_features` -- never reaching a
non-Baseten endpoint, and that still holds. A flat `context_length`, though, is
a field every OpenAI-shaped listing may carry, and reading it is what this
branch now does.
Assert the ceiling that survives and the enrichment that does not, so the test
keeps guarding #6289 instead of the 128K fallback.
Six cases on the generic branch: the OpenAI `limit` block and every flat
field shape are read; `metadata.context_limit` outranks `context_budget`;
an unreadable high-priority field costs only its own candidate instead of
hiding the readable fields below it; a row stating no positive whole token
count is left out; a body with no `data` array yields nothing; and a
generic route keeps the limits its listing states end to end.
Each case is synthetic and offline.
The first version of this pass selected a limit field by presence and then
parsed whatever it had selected. A row that states an unreadable
high-priority field therefore lost every readable field below it:
`{"context_length": 200000, "metadata": {"context_limit": "1M"}}` produced
no context at all, and the route fell back to 128K -- the exact symptom
this pass exists to remove. `metadata.context_budget` was likewise
unreachable whenever `context_limit` was merely present.
Parse the candidates in priority order and take the first that reads as a
positive whole token count. A malformed field now costs its own candidate,
nothing else.
Also key the per-model limit map through the same canonicalization the
OpenCode Go cutline applies, so a rewritten roster id still matches the
row the provider stated.
Verified against a local stub listing, unchanged input, pre-fix binary
versus this one:
row-a metadata.context_limit "1M" + context_length 200000
before: ctx absent (route fell back to 128000), out 32000
after: ctx 200000, out 32000
row-b limit {context 1048000, output 64000} both: unchanged
row-c no limit stated both: absent
Against the relay that reported the defect, `model resolve` now answers
`context_tokens: 1000000, output_tokens: 32000` for claude-haiku-5.5 and
`context_tokens: 1048000` for deepseek-v4-pro, both `used_fallback: false`.
The generic branch of `catalog_delta_from_models_body` built every
`CatalogOffering` with `limit: None`. A provider that publishes per-model
context and output ceilings in its own `/models` response therefore lost
them, and a custom route with no bundled catalog row fell back to
`LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS` (128K). Operators saw 128K for a
model whose listing says 1M, carrying an `unverified ctx` chip that
correctly admitted the number was a guess.
Read the same document a second time, best-effort, and attach the limits
each row states. Recognised shapes are the OpenAI `limit` block, flat
`context_length` / `context_window` / `max_model_len` /
`max_context_length`, `max_output_tokens` / `max_completion_tokens`, and
a relay `metadata` block carrying `context_limit`, `context_budget` and
`output_budget`. `metadata.context_limit` outranks
`metadata.context_budget`: the first states the model capability, the
second the share one deployment will accept. `input` stays empty so no
route is clamped on an inference.
The roster still comes from the provider's typed parser, and this pass is
best-effort: an unreadable metadata row drops only that row's limits.
Nothing here can fail a refresh that used to succeed.
The launch card's context row keeps the last two path components and
then clips from the right, so a pane too narrow for both cut the folder
you are in: under a long parent folder at 50 columns the row read
"…/customer-portal-redesign/…". Drop to the folder alone when two
components don't fit, the order empty_state_caption already uses for
the caption the same screen shows once the card dissolves.
Signed-off-by: Lstarsky0 <[email protected]>
Avoid fetching codew a second time when its manifest digest matches codewhale. Preserve separate download and verification when release assets differ.
Refs #7102
Exercise Simplified Chinese interpolation, stat header, pager title and
opaque patch preservation through the real diff handler. This regression
check fails against the original English-only handler and passes with the
localization fix. The full commands suite remains 28 passed, 0 failed.
Route diff messages and pager titles through the presentation facet and
provide translations in all 15 complete locale packs. Preserve the English
output, action behavior and raw diff payloads.
Refs #7071 (diff portion).
A conversation could silently stop keeping its saved session up to date: once
a stream died mid-reply — system sleep, a dropped connection — and the request
was retried, every later save of that thread was refused with a 409 about an
"active turn" that had already finished. The thread kept working, but the
session list froze at the last successful save: stale message count, stale
cost, stale timestamp.
The interrupted reasoning segment is now closed at the boundary that ends it,
every item is settled when its turn ends, and the save guard reads the turn
rather than its items. Threads an older build already left in that state are
repaired the first time they are opened.
OpenCode Go's roster still lives at https://opencode.ai/zen/go/v1 while
models.dev now publishes its api as https://opencode.ai/inference/go/openai/v1.
complete_from required the lower row's published base to fingerprint-match
the live roster before copying anything, so the models.dev limit for
deepseek-v4.1-flash (context 1,000,000) was discarded and the window fell
back to LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS (128K).
A context window belongs to the model, not the host serving it. For a live
row completed from a model-describing layer (bundled, models.dev, cloud
facts, Codewhale corrections), the configured base must still match the
live roster, but the published-base match now gates only endpoint-scoped
facts: transport, provider_api, provider_npm, status and cost. Limits,
modalities, reasoning and the other model facts are inherited. Live-to-live
and override arms are unchanged; complete_facts_from (signed cloud patch)
still copies everything.
Not in this slice: moving the opencode-go descriptor to the inference/
host. The new docs split the chat base (/inference/go/openai/v1), the
Anthropic base (/inference/go/anthropic/v1) and the models base
(/inference/go/v1), which the single model-aware base_url cannot express;
zen/go/v1 still answers /models with 200 (checked 2026-10-26).
Verified:
- scripts/dev-test.sh config: 806 run, 806 passed, 1 skipped
- scripts/dev-test.sh tui provider_lake catalog context_window opencode:
365 run, 365 passed
- Throwaway reproduction (not committed): live opencode-go row at zen/go/v1
plus models.dev row with api inference/go/openai/v1 and limit 1M.
Without fix: FAILED (context stays None). With fix: passed; context is
1,000,000, provider_api and cost not inherited, and a mismatched
configured base still returns false.
- cargo fmt -p codewhale-config --check clean
Fixes#7096
Co-Authored-By: Claude Opus 5.5 <[email protected]>
/cache renders a turn with no cache telemetry as "—" in its hit, miss
and write cells and keeps it out of the totals, so a route that never
reports cache tokens doesn't read as one that never hits. The totals
still printed those empty sums as numbers: after a turn on such a
route the row said "—" and the footer said "Σ hit: 0 Σ miss: 0" and
"sum_write: 0". Print "—" for the three sums when no listed turn
reported cache telemetry; once one has, they read as before.
Signed-off-by: Lstarsky0 <[email protected]>
Hosts send SetModel whenever they resync the engine's model, not only
when it changes: a reasoning-effort change resends the current model,
and so does /clear. The engine acknowledged every one with "Model set
to: <model>", so `/effort high` ended with "Model set to: mock" in the
footer in place of its own "Reasoning effort: high". Send the status
only when the model differs from the one already set, as SetCompaction
does for its switch since 04de8eb67. A real switch still says "Model
set to: <model>".
Signed-off-by: Lstarsky0 <[email protected]>
The /context inspector shortened the workspace path from the right, so
a narrow pane kept the root and cut the folder that tells you which
workspace this is: at 50 columns a path ending in `…/cw-tools/run/ws`
showed as `…/cw-tools/ru…`. Use workspace_context::truncate_left, which
the status line's workspace chip already uses, so the tail survives and
the cut moves to the front. Paths that fit render as before.
Signed-off-by: Lstarsky0 <[email protected]>
2026-10-23 12:13:07 +08:00
2702 changed files with 376819 additions and 163629 deletions
pengujian, dan peninjauan yang lengkap dan terbaru.
## Memulai (Getting Started)
## Pilih tempat yang sesuai
### Prasyarat
| Yang ingin Anda sampaikan | Tempatnya |
| --- | --- |
| Bug, crash, atau perilaku yang tidak diharapkan | [Laporan bug](https://github.com/codewhale-hq/CodeWhale/issues/new?template=bug_report.yml) |
| Ide atau permintaan fitur | [Permintaan fitur](https://github.com/codewhale-hq/CodeWhale/issues/new?template=feature_request.yml) |
| Masukan tentang tampilan, tulisan, atau pengalaman penggunaan | [Formulir masukan](https://github.com/codewhale-hq/CodeWhale/issues/new?template=feedback.yml) |
| Pertanyaan | [Tanya jawab](https://github.com/codewhale-hq/CodeWhale/discussions/categories/q-a) atau [Discord](https://discord.gg/37gfS3ksug) |
| Masalah keamanan | [Laporan keamanan privat](https://github.com/codewhale-hq/CodeWhale/security/advisories/new), bukan issue publik |
| Perbaikan untuk issue yang sudah ada | Pull request yang menautkan issue tersebut |
| Terjemahan, penyedia model, atau perbaikan dokumentasi | Pull request; tidak perlu membuka issue terlebih dahulu |
- Rust 1.88 atau lebih baru (edisi 2024)
- Pengelola paket Cargo
- Git
Jika ragu, buka issue dan jelaskan apa yang Anda coba lakukan, apa yang terjadi,
dan apa yang Anda harapkan. Pengelola akan membantu mengarahkannya.
untuk persyaratan Rust, perintah build, dan cara menjalankan versi sumber.
Versi di `main` dapat lebih baru daripada unduhan rilis; sertakan keluaran
`codewhale --version` ketika melaporkan masalah.
- Jalankan `cargo fmt` sebelum melakukan commit untuk memastikan format kode konsisten.
- Jalankan `cargo clippy` dan atasi seluruh peringatan.
- Ikuti konvensi penamaan Rust (`snake_case` untuk fungsi/variabel, `CamelCase` untuk tipe data/struct).
- Tambahkan komentar dokumentasi untuk API publik.
Jalankan pemeriksaan yang sesuai dengan bagian yang Anda ubah, mengikuti
[panduan pengujian lokal](../CONTRIBUTING.md#fast-local-loop). Sebelum mengirim
pull request, baca perubahan Anda sendiri dan jelaskan apa yang berubah serta
bagaimana Anda memeriksanya. Jangan sertakan kunci API atau data pribadi.
### Pengujian
## Peninjauan dan kredit
- Tulis pengujian untuk fungsionalitas baru.
- Pastikan seluruh pengujian yang ada lulus: `cargo test --workspace --all-features`.
- Tempatkan unit test di samping kode yang diuji (`#[cfg(test)]`), serta tambahkan pengujian integrasi di bawah direktori `tests/` pada crate yang bersangkutan.
---
## Pengajuan Pull Request (PR)
1. Buat branch baru dari `main` untuk fitur atau perbaikan Anda.
2. Pastikan `cargo fmt`, `cargo clippy`, dan pengujian lulus sebelum mengajukan PR.
3. Tulis judul dan deskripsi PR yang jelas yang menjelaskan tujuan perubahan.
4. Setiap kontributor akan mendapatkan kredit dalam changelog dan `docs/CONTRIBUTORS.md`.
Pull request Anda digabungkan sebagai kontribusi Anda sendiri. Pengelola
membantu menyelesaikan konflik akibat perubahan di `main`; pemeriksaan yang
merah karena masalah lain bukan tanggung jawab Anda. Jika perubahan belum siap,
pengelola menjelaskan alasannya. Kontribusi dicatat di changelog dan
Thanks for the idea. English or 中文 are both fine. Short is good.
This is the best place to start a feature, even if you plan to build it yourself: agreeing on the shape here makes the pull request small and quick to merge. The [roadmap](https://github.com/codewhale-hq/CodeWhale/blob/main/docs/ROADMAP.md) shows what is already planned.
If someone already asked for this, add a 👍 to their issue instead.
Want to talk it through first? Use [Discussions](https://github.com/codewhale-hq/CodeWhale/discussions) or [Discord](https://discord.gg/37gfS3ksug).
@@ -61,4 +63,4 @@ body:
attributes:
label:Contributing
options:
- label:I would like to open a pull request for this. 我愿意提交 PR。
- label:I would like to open a pull request for this, once we agree on the shape. 我愿意在方案确定后提交 PR。
description:Something looked, read or felt off, or you have a thought to share. 反馈与感受。
labels:["needs-triage"]
body:
- type:markdown
attributes:
value:|
All feedback is welcome, and you do not need a fix or a plan. English or 中文 are both fine.
First impressions, confusing wording, an awkward step, a screen that looks wrong, something you liked: it all helps.
For something broken, the [bug report](https://github.com/codewhale-hq/CodeWhale/issues/new?template=bug_report.yml) form asks for the details we need to reproduce it.
- type:textarea
id:feedback
attributes:
label:What would you like us to know? 想告诉我们什么?
description:What you were doing, and what you noticed. A screenshot helps for anything visual.
validations:
required:true
- type:input
id:version
attributes:
label:Codewhale version (optional) 版本
description:Output of `codewhale --version`, if you have it.
"${greeting} — your contribution landed in [\`${short_sha}\`](${commit_url}) on \`main\`:" \
"" \
"> ${subject}" \
"" \
"Closing this PR now that the code is on \`main\`. Credit lives in the commit message and (where applicable) the \`CHANGELOG.md\` entry for the next release. Apologies for not closing this at the time of the merge — the auto-close workflow is new in v0.8.31." \
"" \
"If you want to land more work and would prefer your future PRs merge cleanly without a harvest step, the [\`CONTRIBUTING.md\`](${contributing_url}) doc has a short note on what makes a contribution mergeable as-is." \
)"
echo "Closing PR #${pr} (harvested in ${short_sha})"
if ! gh pr close "${pr}" \
--repo "${GITHUB_REPOSITORY}" \
--comment "${body_text}"; then
echo "::warning::Failed to close PR #${pr}; continuing"
echo "Generated by the scheduled catalog refresh from models.dev. No LLM wrote these rows; review them like any other change."
echo
echo "New models from the vendor allowlist. Merging this PR publishes them on the public /models page; rows carry an \`unreviewed\` source until someone checks them against the vendor's docs:"
echo
echo '```text'
cat "$RUNNER_TEMP/discover.txt"
echo '```'
echo
echo "Checks run in the job: \`seed render --check\`, \`seed age --max-days 30\`, \`scripts/catalog_models_dev_test.py\`, \`web/scripts/check-facts.mjs\`: $([ "$CHECKS_OK" = true ] && echo passed || echo FAILED, see the job log; this PR is a draft)."
echo
echo "To stop proposing a model, add its id to \`[discover].skip\` in \`scripts/catalog/models_dev_seed.toml\`."
} > "$RUNNER_TEMP/pr-body.md"
title="chore(catalog): refresh the Models.dev offline seed"
existing=$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number // empty')
echo "::error::WEB_MAINTAINER_DEEPSEEK_API_KEY is not set. Create a dedicated DeepSeek key with a provider-side monthly cap and add it as that repository secret, or unset WEB_MAINTAINER_ENABLED. See web/maintainer/README.md."
echo "::error::bubblewrap did not confine a child on this runner (writes outside the workspace or outbound network were not denied). Refusing to give a model a shell."
exit 1
fi
if [ ! -e "$probe" ] || [ -e /usr/local/sandbox-probe ]; then
echo "::error::sandbox probe left unexpected state. Refusing to give a model a shell."
@@ -55,10 +55,13 @@ base prompt". Two more corollaries earned here:
- Search for behavior and symbols before reviving work from an old branch. If a
lane is obsolete, preserve its intent and evidence rather than merging stale
code mechanically.
- Agent-owned work uses the canonical checkout on current `main`. Do not
create branches, worktrees, nested clones or task-specific integration lanes
to handle dirty, stale or conflicting work. When agents share the checkout,
partition by file and retry an `index.lock` failure. Verified main slices may
- Agent-owned work lands on current `main`. Each writing agent works in its
own short-lived git worktree cut from `origin/main`, rebases and pushes
`main` the same day, then removes the worktree and its build directory. No
long-lived branches, nested clones or integration lanes, and never a new
worktree to hide dirty, stale or conflicting work: that work is still yours
to finish. In a shared checkout, stage by explicit path and retry an
`index.lock` failure. Verified main slices may
be committed and pushed normally under the rule below; tag, release,
deployment and destructive cleanup still require their explicit
authorization.
@@ -77,11 +80,11 @@ base prompt". Two more corollaries earned here:
- Commit by explicit path; never `git add -A`.
- Work that fails its check and cannot be fixed now is not pushed to `main`.
Record it and report it instead.
- **Maintainers do not open PR branches** (founder, 2026-10-08: more PRs means
more CI and slower work). Verified slices land directly on `main` and are
pushed: run the focused gate, put the real counts in the commit message,
fetch and rebase onto `origin/main` and retry on rejection. Never force-push
or rewrite a shared ref. Contributor PRs still merge as PRs.
- **Maintainers land small verified slices directly on `main`.** Run the
focused local check (`scripts/preflight.sh --check`), put the real counts in
the commit message, fetch and rebase onto `origin/main` and retry on
rejection. Never force-push or rewrite a shared ref. Contributor PRs are
always welcome and merge as themselves (founder, 2026-11-07).
- When the task is local-only, stay fully offline: no browsing, GitHub or remote
Git operations, downloads, dependency installation, provider calls, or
source/diff transmission. Record the missing external receipt and keep working
@@ -102,19 +105,19 @@ base prompt". Two more corollaries earned here:
- **Write down what a design does not do**, beside the behaviour it owns — a
short known-limitations note in the owning module. A stated limit stops the
next reader from assuming a capability that was never built.
- **Substantive contributor replies are authorized for the 0.10.2 release**
(founder, 2026-10-13). Thank people specifically for their reports, diagnosis,
fixes and testing; link the carrying commit or PR and distinguish candidate
work from a shipped release. Status, answers and useful questions are welcome;
repetitive progress posts and bot noise are not. Evidence stays in commits
and PR bodies; ownership stays in Linear. Outside this release, the previous
no-comment rule remains, except a one-sentence reason when closing or
superseding a human contribution. Review workflows remain disabled unless
the founder separately authorizes enabling them.
- **Be generous in public** (founder, 2026-11-07). Thank people specifically
for their reports, diagnosis, fixes and testing; link the carrying commit or
PR and distinguish candidate work from a shipped release. Status, answers
and useful questions are welcome; repetitive progress posts and bot noise
are not. Evidence stays in commits and PR bodies; ownership stays in Linear.
A contributor PR is never closed because CI is slow, flaky or red for a
reason the PR did not cause; when a PR is closed, say why in one specific,
thankful sentence. Review bots stay off unless the founder enables them.
- **A user feature lands with its registry row.** A new command, `[features]`
flag, provider or user-visible feature adds or updates its row in
`docs/features.toml` in the same change;
`cargo test -p codewhale-tui --test feature_registry` fails on flag drift.
- **Public planned-work issues are written for contributors.** An issue opened for planned or release work has the parts in CONTRIBUTING.md § "What a planned-work issue gives you": status, ways to help with files, a local test command and a done-when, what maintainers already hold, and how to claim. Keep it current as pieces land or are claimed.
- **Write `close`/`fix`/`resolve #N` only when you mean it.** GitHub closes the
issue on merge even inside "does not close #N"; use `Refs #N` otherwise.
@@ -123,17 +126,18 @@ base prompt". Two more corollaries earned here:
An external contributor's branch goes stale because *we* land things, not
because they did anything wrong. Treat their time as more expensive than ours.
**The goal is the contributor's PR merging as itself.** Review it, help it
rebase, or fix it on their branch — that is the default path. Closing their PR
and re-landing the work as our own commit (`auto-close-harvested`) is the
fallback for a branch that truly cannot merge in reasonable time; done
casually it reads as taking the work even when credit is preserved.
**The goal is the contributor's PR merging as itself.** Review it, run or
rerun only the checks it needs, fix small problems on their branch, and resolve
conflicts that `main` caused. Landing their commits as ours (harvesting) is the
rare fallback for a branch we cannot push to and that cannot merge in
reasonable time; ask on the PR first, because done casually it reads as taking
the work even when credit is preserved.
- **Never make a contributor rebase around our churn.** If their PR conflicts
only because main moved, a maintainer resolves it.
Thank you for your interest in contributing to codewhale! This document provides guidelines and instructions for contributing.
Codewhale is what it is because of the people who used it, reported what broke,
translated it, added their providers and sent fixes. Thank you for being here.
Everyone is welcome, in English or 中文, with or without code.
All feedback is a gift to this project, including "this looked wrong" or "I got
lost here" with no fix attached. The [roadmap](docs/ROADMAP.md) says where
Codewhale is going and where help counts most.
## Where things go
| You have | Best place |
| --- | --- |
| A bug, a crash, or behavior that surprised you | [Bug report](https://github.com/codewhale-hq/CodeWhale/issues/new?template=bug_report.yml) |
| An idea or a feature you want | [Feature request](https://github.com/codewhale-hq/CodeWhale/issues/new?template=feature_request.yml), or [Ideas](https://github.com/codewhale-hq/CodeWhale/discussions/categories/ideas) to talk it through first |
| Feedback on how something looks, reads or feels | [Feedback](https://github.com/codewhale-hq/CodeWhale/issues/new?template=feedback.yml) |
| A question | [Q&A](https://github.com/codewhale-hq/CodeWhale/discussions/categories/q-a) or [Discord](https://discord.gg/37gfS3ksug) |
| A security problem | A [private advisory](https://github.com/codewhale-hq/CodeWhale/security/advisories/new), never a public issue ([policy](.github/SECURITY.md)) |
| A fix for an open issue | A pull request that links the issue |
| A translation, a provider or model, a docs fix | A pull request, no issue needed ([standing projects](#standing-projects)) |
| A new feature, already written or not | An issue first ([why](#new-features-start-as-an-issue)) |
If you are not sure, open an issue. Nothing is filed in the wrong place for
long: a maintainer moves it.
## New features start as an issue
Issues are the best place for feature requests, ideas and bugs. A pull request
is the best place for work we have already agreed on.
A feature that first appears as a pull request takes longer to land than one
that starts as an issue, even when we want it. The questions that decide a
feature are about the product, not the diff: does it belong in the engine or in
a plugin, what does it do in each permission mode, how does it show up in
receipts, what is it called in 19 languages. Answering those in a review means
rewriting code you already finished. Answering them in an issue costs you a few
sentences.
So for a new feature:
1. Open a [feature request](https://github.com/codewhale-hq/CodeWhale/issues/new?template=feature_request.yml).
Say what you are trying to do and what gets in the way. A proposed design is
welcome and optional.
2. Tick "I would like to open a pull request" if you want to build it.
3. A maintainer answers with the shape that fits, or with what is still
undecided. Then the pull request is small and merges quickly.
A feature pull request that arrives without an issue is still read, and it is
never closed for that reason. We will most likely ask the same questions there
and it will take longer.
## Standing projects
These are always open, and a pull request needs no issue first:
- **Translations.** You can improve the terminal language packs, the translated
READMEs in `docs/i18n/`, the website, and the translated docs in
`docs/zh_hans/` and `docs/id/`. The [localization matrix](docs/LOCALIZATION.md)
shows what each language has and how to add one, and the
Comment on the issue to claim it so two people do not build the same thing.
Large changes to planned work are easier in layers; see
[Layered and EPIC-Sized Work](#layered-and-epic-sized-work).
### What a planned-work issue gives you
Every issue a maintainer opens for planned work, and every release tracking issue, has the same parts, so you can pick one up without asking first:
- **Status:** what is on `main` already, with commit links, and what is still missing.
- **Ways to help:** each piece open to contributors, with the files to read, the command to test it locally (usually `scripts/dev-test.sh <crate> <filter>`) and what "done" means. Pieces that need no Rust, such as trying a feature and reporting exactly what happened, are listed first. A precise report is credited like code.
- **Being built by maintainers right now:** the parts someone already holds, so you don't build the same thing twice.
- **How to take a piece:** comment to claim it, then open a pull request that says `Refs #<issue>`.
If an issue for planned work is missing one of these, say so on it. That is a bug in the issue.
## How we work with you
- We reply by naming what you reported, diagnosed or fixed.
- Your pull request merges as itself, with you as the author
([details](#how-your-contribution-lands)).
- You do not rebase around our changes, and a red check you did not cause is
ours to fix.
- We credit you in the changelog and in
[the contributors list](docs/CONTRIBUTORS.md).
- If something is not ready or not a fit, we say what is blocked and what would
change the answer.
## Getting Started
@@ -229,9 +326,9 @@ Co-author a *person* freely; the address must be their GitHub-linked one
When a commit harvests code from a community PR (see "How Your Contribution
Lands" below), include a `Harvested from PR #N by @author` line in the commit
body. An auto-close workflow watches for this pattern and closes the
referenced PR with credit so the contributor gets a clear signal that
their work shipped.
body. A maintainer then closes the referenced PR by hand with a thank-you and
a link to the commit, so the contributor gets a clear signal that their work
shipped.
## What happens after you open a pull request
@@ -242,26 +339,23 @@ maintainer to approve the first run for a brand-new GitHub account.
| Check | What it runs | When it is red |
| --- | --- | --- |
| `Lint` (required) | `cargo fmt --check`, clippy on every target, and the fast drift checks (locales, READMEs, provider registry, dead-code and blocking-call budgets) | Run `cargo fmt --all` and the clippy command in [Pre-push verification](#pre-push-verification); the log names the file and the check |
| `Test (ubuntu-latest)` | The whole workspace suite on Linux with `cargo nextest`, plus doctests | Re-run the named test locally, for example `scripts/dev-test.sh tui <test name>` |
| `Test (windows-latest)` (required) | Compiles every workspace target for Windows with warnings denied, and the installer PATH helper tests | Usually a `cfg(windows)` path or an import only Windows sees; the log has the compiler error |
| `npm wrapper smoke (ubuntu-latest)` (required) | Builds `codewhale` and installs it through the npm wrapper | The log shows the failing install or entrypoint step |
| `Safety gate` | The sandbox, authorization and auto-review tests | Treat it as a real regression in a trust boundary |
| `Version drift` | Version files, the `feat:` release-note rule, release helper tests and contributor credit | The credit step only reports: a maintainer adds your credit when the PR merges |
| `PR closes an issue` | Your description has `Closes #N`, `Refs #N` or `No-Issue: <reason>` | Edit the description, then re-run the check |
| `DCO` | Lists commits without `Signed-off-by` | Advisory; it never blocks |
| `Contribution gate` | Labels PRs from authors not yet in `.github/APPROVED_CONTRIBUTORS` | A label, not a judgement; see [Contribution Gate](#contribution-gate) |
Smaller checks cover MSRV, the VS Code extension, integrations, the extension
host and plugin conversion, plus workflow lint when you change
`.github/workflows`. A documentation-only change skips the Rust builds but
still reports the required checks, so it can merge. The Linux test suite took
45 minutes on a recent pull request; the other checks run alongside it.
Smaller checks cover the VS Code extension, integrations, the extension host
and plugin conversion, plus workflow lint when you change `.github/workflows`.
A documentation-only change skips the Rust builds but still reports the
required checks, so it can merge.
Your pull request does not run the macOS and Windows test suites, the slow
runtime-contract and persistence-backlog measurements, the dependency audits
or the OpenHarmony build. Maintainers run those once per release, on the exact
commit being released. If that run finds a problem your change introduced, we
fix it on `main`; we will not ask you to chase it.
Your pull request does not run the test suites, the safety gate, MSRV, the npm
wrapper smoke, the slow runtime-contract and persistence-backlog measurements,
the dependency audits or the OpenHarmony build. Maintainers run those once per
release, on the exact commit being released. If that run finds a problem your
change introduced, we fix it on `main`; we will not ask you to chase it.
A red check that your change did not cause is not your job to fix. We rerun it
or fix it on `main`, and we never close a PR because CI is slow or flaky. Your
PR may be a draft, and the issue line in its description is optional.
## How Your Contribution Lands
@@ -280,9 +374,10 @@ Before merging, a maintainer may:
### Fallback — harvest
Rarely, a branch cannot merge in reasonable time, for example when it is in a
fork we cannot push to and the conflicts cannot be resolved there. Then a
maintainer may **harvest** the useful commits into `main` instead. This is
**not a rejection**: your code landed, and your credit is kept mechanically.
fork we cannot push to and the conflicts cannot be resolved there. We will ask
on your PR first. If it still cannot merge, a maintainer may **harvest** the
useful commits into `main` instead. This is **not a rejection**: your code
landed, and your credit is kept mechanically.
- You stay the author of the harvested commit, with your GitHub-linked address
from `.github/AUTHOR_MAP` or
@@ -291,8 +386,8 @@ maintainer may **harvest** the useful commits into `main` instead. This is
- The commit body includes `Harvested from PR #N by @your-handle`. That line is
the contract: it is your credit and the signal that your work shipped.
- The `CHANGELOG.md` entry for the next release credits you by handle.
- `auto-close-harvested.yml` closes your PR with a thank-you and a link to the
commit on `main`.
- A maintainer closes your PR by hand with a thank-you and a link to the commit
on `main`.
When a maintainer closes a harvested PR by hand, the closing comment
follows this template (the pattern set on PR #2634):
@@ -382,96 +477,54 @@ because `main` moved while it was in review, say so and a maintainer resolves
it on your branch. If your branch is in a fork we cannot push to, we ask on the
PR first; harvesting is the last resort.
## Contribution Gate
## Labels
Codewhale uses a maintainer-managed contribution gate for the community front
door. Maintainers and collaborators bypass this gate automatically. The gate
workflows run in dry-run mode and only add labels, so maintainers can observe
the signal before changing contributor flow.
Issues and pull requests are labelled by `.github/workflows/triage.yml`, which
only ever adds labels that already exist. Pull requests get their labels from
the paths they change and their conventional-commit title prefix; issues get
them from the title and form answers.
The maintainer posture is documented in
[docs/AGENT_ETHOS.md](docs/AGENT_ETHOS.md): automation should reduce load while
keeping good-faith contributors seen, credited, and able to keep helping.
| Mode | `Tab` (when the input is empty) or `/mode` | **Plan** reads and searches but changes nothing. **Work** edits files and runs commands. **Operate** has the same access as Work, treats your request as a goal, and keeps going until the goal is verified or it needs input from you. |
| Permission | `Shift+Tab` | **Ask** applies file edits in your repository without asking and asks before shell commands that are not read-only. **Auto-Review** lets rules and a reviewer model approve routine calls and refuses high-risk ones; switch to Ask to approve those yourself. **Full Access** never asks and turns off Codewhale's sandbox; repository rules and managed policy still apply. |
| Mode | the Plan stop on `Shift+Tab`, or `/mode` | **Plan** reads and searches but changes nothing. **Work** (every other stop) edits files and runs commands; start a goal with `/goal` when a request needs more than one turn. |
| Permission | `Shift+Tab` | **Ask** asks before every file edit and every shell command that is not read-only. **Accept Edits** applies file edits in your git repository without asking and still asks before commands. **Auto-Review** lets rules and a reviewer model approve routine calls and refuses high-risk ones; switch to Ask to approve those yourself. **Full Access** never asks, and commands can reach anything on this computer; repository rules and managed policy still apply. |
See the [modes and permissions guide](docs/MODES.md) for the details.
@@ -203,7 +206,7 @@ and operate other apps. It stays off until you review its access and enable it
| `codewhale` | The interactive terminal app |
| `codewhale exec "…"` | One task without the interface. On its own it answers without using tools; `--auto` enables tools with Full Access by default, `--sandbox workspace-write` restricts them, and `--output-format stream-json` streams events as JSON |
| `codewhale web` | A local web UI on `127.0.0.1` ([guide](docs/WEB.md)) |
| `codewhale review --pr N` | An advisory review of a GitHub pull request. It posts nothing unless you add `--post`; the [GitHub Action](docs/GITHUB_ACTION.md) runs the same review in CI |
| `codewhale review --pr N` | An advisory review of a GitHub pull request. The [GitHub bot setup](docs/GITHUB_ACTION.md#set-up-your-own-bot) generates a pinned workflow for automatic reviews and `@codewhale review`, using your own model key and optional App identity |
| `codewhale app-server --http` | The [Runtime API](docs/RUNTIME_API.md), a local HTTP API for threads, events, approvals, and each turn's file changes |
| `codewhale serve --acp` | The Agent Client Protocol over stdio, for editors such as Zed |
@@ -255,14 +258,19 @@ that. More in [authorization order](docs/AUTHORIZATION_ORDER.md),
## Community
If a provider is missing or a workflow is awkward,
[open an issue](https://github.com/codewhale-hq/CodeWhale/issues/new/choose) or
[send a pull request](CONTRIBUTING.md). Contributors keep credit for the work
that lands, and the [repository layout](CONTRIBUTING.md#project-structure)
shows where things live.
Codewhale is what it is because of its community, and all feedback is
welcome: a bug, an idea, or something that looked or felt wrong.
[Open an issue](https://github.com/codewhale-hq/CodeWhale/issues/new/choose) for any
of these; an issue is also the best place to start a new feature. Pull requests
are welcome for fixes, translations, providers and planned work. The
[contributing guide](CONTRIBUTING.md) shows where each kind of help goes, and
the [roadmap](docs/ROADMAP.md) shows where the project is headed. Contributors
keep credit for the work that lands, and the
[repository layout](CONTRIBUTING.md#project-structure) shows where things live.
Chat on [Discord](https://discord.gg/37gfS3ksug), or add the maintainer,
Hunter, on WeChat (`hunterbown`) and ask to join the Whale Brothers group.
"`codewhale account` does not accept the global `--api-key` flag because command-line values can leak through shell history. Use `account keys set <provider>` for a hidden prompt, `--api-key-stdin`, or `--from-local`"
returnErr(CloudTransportError::new("The Work reply does not confirm the requested source context; replay the same message and context",std::io::Error::other("task context mismatch")).into());
Generated from `contract/codex-v2.json` (contract `codewhale-codex-app-server-v2` version 1) and the pinned Codex `rust-v0.166.0` export (commit `6ff1e1ca3b30fc8b914720681fcf5fe62e95f856`) by `cargo run -p codewhale-app-server-protocol --bin codewhale-codex-contract -- --write`. Edit the manifest, never this file; `tests/contract.rs` fails when they drift. The method sets are the unmodified export members under `tests/pinned/codex/`.
- **A**: required for 0.10.3 Codex parity.
- **B**: service methods, required for 0.10.3.
- **C**: out of scope, for the reason given.
- **undecided**: the 0.10.3 parity bar does not settle it yet. The last column carries a recommended tier for the founder to confirm; until then it is handled exactly like Tier C.
Status: `implemented` means a Codex-shaped handler is on main (response goldens are not yet checked); `partial` means the Codex method name dispatches to the legacy shape; `missing` means no handler; `method-not-found` means the dispatcher answers `-32601`; `never-sent` means the server never emits it. The stable export is the scope: methods that exist only in the experimental export are not listed. A complete inventory or typed shape is not a handler: status, not tier, says what main dispatches.
## Dispatch rules
| Tier | Rule |
| --- | --- |
| A | selected for 0.10.3; the typed params/result in src/v2.rs are the wire contract |
| B | selected for 0.10.3 (service methods); the typed params/result in src/v2.rs are the wire contract |
| C | not selected; a client request answers methodNotFound (-32601); the server never sends a Tier C request or notification |
| undecided | not selected until the founder decides; until then it is handled exactly like Tier C (methodNotFound, never sent), and the row keeps its recommendation |
## Client requests
| Method | Tier | Status | Basis | Recommended | Params | Result |
| --- | --- | --- | --- | --- | --- | --- |
| `initialize` | A | implemented | connection lifecycle (CW-7704; on main) | | `InitializeParams` | `InitializeResponse` |
| `account/read` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C | GetAccountParams (untyped) | GetAccountResponse (untyped) |
| `getConversationSummary` | C | method-not-found | deprecated v1 API (upstream marks it DEPRECATED; TypeScript-only in the export) | | GetConversationSummaryParams (untyped) | GetConversationSummaryResponse (untyped) |
| `gitDiffToRemote` | C | method-not-found | deprecated v1 API (upstream marks it DEPRECATED; TypeScript-only in the export) | | GitDiffToRemoteParams (untyped) | GitDiffToRemoteResponse (untyped) |
| `getAuthStatus` | C | method-not-found | deprecated v1 API (upstream marks it DEPRECATED; TypeScript-only in the export) | | GetAuthStatusParams (untyped) | GetAuthStatusResponse (untyped) |
| `fuzzyFileSearch/sessionUpdated` | undecided | missing | pairs with fuzzyFileSearch | B | FuzzyFileSearchSessionUpdatedNotification (untyped) | |
| `fuzzyFileSearch/sessionCompleted` | undecided | missing | pairs with fuzzyFileSearch | B | FuzzyFileSearchSessionCompletedNotification (untyped) | |
| `thread/realtime/started` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeStartedNotification (untyped) | |
| `thread/realtime/itemAdded` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeItemAddedNotification (untyped) | |
| `thread/realtime/item/started` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeItemStartedNotification (untyped) | |
| `thread/realtime/item/transcript/delta` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeItemTranscriptDeltaNotification (untyped) | |
| `thread/realtime/item/completed` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeItemCompletedNotification (untyped) | |
| `thread/realtime/transcript/delta` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeTranscriptDeltaNotification (untyped) | |
| `thread/realtime/transcript/done` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeTranscriptDoneNotification (untyped) | |
| `thread/realtime/outputAudio/delta` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeOutputAudioDeltaNotification (untyped) | |
| `thread/realtime/sdp` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeSdpNotification (untyped) | |
| `thread/realtime/error` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeErrorNotification (untyped) | |
| `thread/realtime/closed` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeClosedNotification (untyped) | |
| `windows/worldWritableWarning` | undecided | missing | Codex Windows sandbox | C | WindowsWorldWritableWarningNotification (untyped) | |
| `windowsSandbox/setupCompleted` | undecided | missing | Codex Windows sandbox | C | WindowsSandboxSetupCompletedNotification (untyped) | |
| `account/login/completed` | undecided | missing | OpenAI account state | C | AccountLoginCompletedNotification (untyped) | |
## Totals
| Kind | A | B | C | undecided |
| --- | --- | --- | --- | --- |
| Client requests | 19 | 25 | 19 | 46 |
| Client notifications | 1 | 0 | 0 | 0 |
| Server requests | 5 | 0 | 4 | 1 |
| Server notifications | 27 | 6 | 4 | 50 |
Typed definitions generated into `src/v2.rs`: 319.
## Errors
Codex envelopes carry no `jsonrpc` member. ACP keeps its own JSON-RPC 2.0 envelope and is not this contract.
| Name | Code | Meaning |
| --- | --- | --- |
| `PARSE_ERROR` | -32700 | the line or message is not JSON |
| `INVALID_REQUEST` | -32600 | not a valid request envelope, or a request before initialize |
| `METHOD_NOT_FOUND` | -32601 | Tier C or undecided method, or a name the pinned export does not have |
| `INVALID_PARAMS` | -32602 | params do not decode into the selected method's typed params |
| `INTERNAL_ERROR` | -32603 | the Engine failed while serving a valid request |
| `OVERLOADED` | -32001 | upstream Codex transport overload code; the outgoing queue is full |
| `THREAD_NOT_FOUND` | -32004 | the named thread does not exist (existing App Server code) |
| `RUNTIME_UNAVAILABLE` | -32005 | the Engine could not be reached (existing App Server code) |
| `CAPABILITY_UNSUPPORTED` | -32020 | the method exists and is selected, but this thread's executor or placement cannot serve it; `data` is `CapabilityRefusalData`; the request has no side effect and never falls back to local execution |
## Codewhale extensions
These ride on Codex shapes without changing any pinned field. The pinned `Thread` schema allows additional properties, so a Codex client ignores the fields. Codewhale notifications live under `codewhale/`, because Codex already uses `thread/environment/*` for its own environments. Extension shapes are declared under `extensions.definitions` in the manifest and generated with the Codex types; none carries a credential.
| Name | Kind | Tier | Status | Shape |
| --- | --- | --- | --- | --- |
| `Thread.executor` | field | A | defined, unpopulated | `ThreadExecutor``{ kind, agentId, agentName? }`: a thread driven by an external agent over ACP; absent for a native thread. Never an environment value or a credential. |
| `Thread.environment` | field | B | defined, unpopulated | `ThreadPlacement``{ id, providerKind }` (named apart from Codex `ThreadEnvironment`, which is a different type): where the thread runs; absent means this computer. A thread placed in an environment this build cannot serve refuses with `CAPABILITY_UNSUPPORTED` (`environment_not_supported`) and never runs locally. |
| `codewhale/environment/*` | server notifications | B | reserved, not implemented | environment state changes for placement |
| `ThreadItem.placement` | field | B | reserved, not implemented | where a child or job ran |
| `approval.decided.responder` | Runtime journal field | A | implemented | the client that answered (`desktop`, `phone`, `web`, `tui`, `acp`), as declared by that client |
The request-ID, request/notification/error data shapes in src/rpc.rs and initialization data shapes in src/lifecycle.rs are adapted from official openai/codex rust-v0.166.0, immutable commit6ff1e1ca3b30fc8b914720681fcf5fe62e95f856, codex-rs/app-server-protocol/src/rpc.rs and src/protocol/v1.rs. Apache-2.0 LICENSE-APACHE and upstream NOTICE are retained with the original Codewhale MIT license in LICENSE-MIT. The crate combines MIT Codewhale implementation with Apache-2.0 contract adaptations.
Changed files: these adaptations remove Codex-specific derive/export dependencies and replace the absolute-path wrapper with PathBuf on the server-created response, whose producer validates absolute scope. RpcCall normalizes existing optional jsonrpc compatibility input and requires present request IDs to be strings or i64. It preserves notification ID absence. Correlated results and errors require RequestId. The existing compatibility uncorrelated parse-error helper uses a distinct null-ID error record; that response does not satisfy the pinned correlated Codex error envelope and remains explicit phase4 closure work. No private Engine source observation, receipt or accounting producer proof appears in this protocol crate.
The exact pinned wire golden fixtures under tests/pinned are unmodified members of the local stable precomputed schema export. This is a staged owner/lifecycle adoption, not complete wire/parity acceptance.
The method sets in tests/pinned/codex/{ClientRequest,ClientNotification,ServerRequest,ServerNotification}.ts are unmodified TypeScript members of the same commit's stable precomputed export (`codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst`, compressed SHA256 `c3802deddc224896967bccd29e353e93e62af768b44dbd5d583e35825fb3e02a`). Member SHA256: ClientRequest.ts `d52eab2b624ee8bab88a121f78a5465faaa6a2701bbd0d2979dc7e5937741b36`, ClientNotification.ts `96692c191ff9ac32c2688b46a0a04f23d15646937adc352f2adcc8211c6d17a0`, ServerRequest.ts `30880f1a8da876eec30600ba27560d05ad0db5c645e14564c9c79a2429274f06`, ServerNotification.ts `df9b5ab2f11010ac336fb7c5b6bb386a7c389cd5dbe742ac5c804fcc69bd40d9`. The TypeScript members are used because they carry the full wire enums: the export's JSON schemas omit three deprecated v1 client requests and two internal-only notifications. Variant names in src/methods.rs follow the upstream `common.rs` variants.
The selected v2 contract is generated from two further unmodified pins of the same commit. `tests/pinned/codex/codex_app_server_protocol.schemas.json` is the `json_schema/codex_app_server_protocol.schemas.json` member of the stable precomputed export (expanded export SHA256 `7ae41cc257d2a4d9f93d1e99f24560802439d2f2d050ec5685832025ced962a2`; member SHA256 `9b6d53647b00d68a04181b826599025a810cfa78acb3ec4d518c68b4120499c0`). `tests/pinned/codex/common.rs` is `codex-rs/app-server-protocol/src/protocol/common.rs` (Git blob `2936c01855e463275d8306df8583ec0303e5c854`, SHA256 `b7f0a90dcc822962e59890b252a43142a113cb1355f333d8c6b0f704b97f89f7`); it is read only by `tests/contract.rs` to check each selected request's result type and is not compiled. The Rust types in `src/v2.rs`, the TypeScript in `schema/typescript/codex-v2.d.ts` and the selected-closure JSON Schema in `schema/json/codex-v2.schema.json` are derived from the schema member by `src/codegen`; they are Apache-2.0 contract adaptations like the files above.
"description":"Optional namespace that groups related tools (e.g. `\"tau_bench\"`).\nWhen present, the runtime may expose the tool as\n`<namespace>::<name>` to the model."
},
"name":{
"type":"string",
"description":"Short tool name. Combined with `namespace` it forms a unique tool id."
},
"description":{
"type":"string",
"description":"Human-readable description exposed to the model."
},
"input_schema":{
"description":"JSON Schema describing the tool's input parameters."
},
"defer_loading":{
"type":"boolean",
"description":"If true, the runtime may defer schema validation / tool loading until\nthe model actually calls the tool.\n\nDefaults to `false` so that older clients omitting this field still\nbehave the same way.",
"default":false
}
},
"required":[
"name",
"description",
"input_schema"
],
"description":"Specification for a dynamic external tool registered by a runtime client.\n\nExample JSON from the spec:\n\n```json\n{\n \"namespace\": \"tau_bench\",\n \"name\": \"get_reservation\",\n \"description\": \"Look up an airline reservation.\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"reservation_id\": { \"type\": \"string\" }\n },\n \"required\": [\"reservation_id\"],\n \"additionalProperties\": false\n }\n}\n```"
},
"DynamicToolItemStatus":{
"type":"string",
"enum":[
"in_progress",
"completed",
"failed"
],
"description":"Lifecycle status of a dynamic tool item shown in thread detail and event\npayloads."
},
"DynamicToolCallResult":{
"type":"object",
"properties":{
"success":{
"type":"boolean",
"description":"Whether the client-side tool execution succeeded."
},
"content":{
"type":"array",
"items":{
"$ref":"#/$defs/DynamicToolCallContent"
},
"description":"Content fragments returned by the tool.\n\nDefaults to an empty vector when omitted so clients can send a minimal\n`{ \"success\": false }` payload.",
"default":[]
}
},
"required":[
"success"
],
"description":"Result submitted by a runtime client after executing a dynamic tool."
},
"DynamicToolCallContent":{
"oneOf":[
{
"type":"object",
"properties":{
"text":{
"type":"string"
},
"type":{
"type":"string",
"const":"input_text"
}
},
"required":[
"type",
"text"
]
},
{
"type":"object",
"properties":{
"image_url":{
"type":"string"
},
"type":{
"type":"string",
"const":"input_image"
}
},
"required":[
"type",
"image_url"
]
}
],
"description":"A single content fragment inside a [`DynamicToolCallResult`]."
},
"TurnEnvironmentParams":{
"type":"object",
"properties":{
"environment_id":{
"type":"string"
},
"cwd":{
"type":"string"
}
},
"required":[
"environment_id",
"cwd"
],
"description":"Environment target selected for a turn's shell/filesystem work.\n\nExample JSON:\n\n```json\n{\n \"environment_id\": \"local\",\n \"cwd\": \"/workspace\"\n}\n```"
},
"RuntimeEventEnvelope":{
"type":"object",
"properties":{
"schema_version":{
"type":"integer",
"format":"uint32",
"minimum":0,
"default":1
},
"seq":{
"type":"integer",
"format":"uint64",
"minimum":0
},
"event":{
"type":"string"
},
"kind":{
"type":"string"
},
"thread_id":{
"type":"string"
},
"turn_id":{
"type":[
"string",
"null"
]
},
"item_id":{
"type":[
"string",
"null"
]
},
"timestamp":{
"type":"string"
},
"created_at":{
"type":"string"
},
"payload":true
},
"required":[
"schema_version",
"seq",
"event",
"kind",
"thread_id",
"turn_id",
"item_id",
"timestamp",
"payload"
],
"additionalProperties":true
},
"ThreadLoadedListResponse":{
"type":"object",
"properties":{
"data":{
"type":"array",
"items":{
"type":"string"
}
},
"nextCursor":{
"type":[
"string",
"null"
]
}
},
"required":[
"data",
"nextCursor"
]
},
"ThreadSetNameResponse":{
"type":"object",
"additionalProperties":false
},
"RuntimeCapabilities":{
"type":"object",
"properties":{
"client_token_intents":{
"type":"boolean",
"description":"Device client tokens have immutable watch/drive intent. Watch cannot\nmutate Runtime state, acquire control, or forward display input.",
"default":false
},
"account_session":{
"type":"boolean",
"default":false
},
"account_model_owner":{
"type":"boolean",
"default":false
},
"session_provider_keys":{
"type":"boolean",
"default":false
},
"threads":{
"type":"boolean"
},
"thread_create_idempotency":{
"type":"boolean",
"default":false
},
"thread_idle_precondition":{
"type":"boolean",
"default":false
},
"thread_patch_undo_turn_id":{
"type":"boolean",
"default":false
},
"thread_scoped_reads":{
"type":"boolean",
"default":false
},
"thread_scoped_git_writes":{
"type":"boolean",
"description":"Git stage, unstage, discard, commit, push and branch accept\n`?thread_id=` and act on that thread's workspace.",
"default":false
},
"thread_scoped_snapshots":{
"type":"boolean",
"description":"`GET /v1/snapshots` and `POST /v1/snapshots/{id}/restore` accept\n`?thread_id=` and act on that thread's workspace.",
"default":false
},
"thread_scoped_terminals":{
"type":"boolean",
"description":"`GET /v1/threads/{id}/terminals` lists the thread's live shells, and\n`/v1/threads/{id}/terminals/{terminal_id}/output|input|resize|kill`\ndrive one by the identity the Engine issued it.",
"default":false
},
"worktrees":{
"type":"boolean",
"description":"`GET`/`POST /v1/worktrees` and `POST /v1/worktrees/remove`.",
"default":false
},
"job_operations":{
"type":"boolean",
"default":false
},
"thread_shell_consent":{
"type":"boolean",
"description":"Explicit per-thread shell opt-in is checked against loaded policy and\ncannot broaden a conversation while it has an active turn.",
"default":false
},
"turns":{
"type":"boolean"
},
"turn_operation_idempotency":{
"type":"boolean",
"description":"`POST /v1/threads/{id}/turns` accepts a durable, thread-scoped\n`operation_key` and returns the original turn for exact retries.",
"default":false
},
"turn_operation_lookup":{
"type":"boolean",
"description":"Read-only exact accepted-turn lookup by thread and operation key.",
"description":"Bounded inline image inputs, persisted and replayed with their turn.",
"default":false
},
"turn_output_token_limit":{
"type":"boolean",
"description":"Per-turn maxOutputTokens is validated and intersected with the route ceiling.",
"default":false
},
"profile_constitution":{
"type":"boolean",
"description":"Account profile snapshots and Engine-owned constitution preview.",
"default":false
},
"turn_steer":{
"type":"boolean"
},
"turn_interrupt":{
"type":"boolean"
},
"event_replay":{
"type":"boolean"
},
"external_tools":{
"type":"boolean"
},
"environments":{
"type":"boolean"
},
"worker_runtime":{
"type":"boolean"
},
"fleet_run_create":{
"type":"boolean",
"default":false
},
"thread_fleet_members":{
"type":"boolean",
"default":false
},
"thread_fleet_member_durable_operations_v1":{
"type":"boolean",
"description":"Member starts are durable per `(store, thread, operation_key)`;\n`GET /v1/threads/{id}/members/operations/{operation_key}` reads them.",
"default":false
},
"fleet_run_start":{
"type":"boolean",
"default":false
},
"fleet_event_replay":{
"type":"boolean",
"default":false
},
"fleet_event_stream":{
"type":"boolean",
"default":false
},
"fleet_local_target":{
"type":"boolean",
"default":false
},
"thread_goals":{
"type":"boolean",
"description":"`GET/PUT/DELETE /v1/threads/{id}/goal` and the `complete`/`block`\nlifecycle actions are available.",
"default":false
},
"memory":{
"type":"boolean",
"description":"`GET /v1/memory` and `GET /v1/memory/{id}` are available for\nbounded inspection of the native memory store. `POST /v1/memory`\nand `DELETE /v1/memory` are also available (auth-gated via the\nstandard route layer) for lifecycle controls.",
"default":false
},
"mcp_server_management":{
"type":"boolean",
"description":"Whether the runtime supports create/update/enable/disable/reconnect/delete\noperations on MCP server configuration via the `POST|GET|PATCH|DELETE\n/v1/apps/mcp/servers` family of endpoints.",
"default":false
},
"skill_lifecycle":{
"type":"boolean",
"description":"Skill lifecycle operations (install, update, uninstall, trust, audit)\nare available via the HTTP API.",
"default":false
},
"skill_detail":{
"type":"boolean",
"description":"`GET /v1/skills/{name}` returns one skill's full body and routing\nmetadata, so a client can compose an activation instruction for its\nnext turn. `GET /v1/skills` rows also carry `invocation`, `aliases`,\nand `bundled_tier` routing fields when this flag is set.",
"default":false
},
"plugin_management":{
"type":"boolean",
"description":"Plugin bundle and marketplace lifecycle operations (list/detail,\ninstall/update/uninstall, trust/enable/disable/revoke, marketplace\nadd/remove/install) are available via the `/v1/apps/plugins` and\n`/v1/apps/marketplaces` endpoint families.",
"default":false
},
"agent_mail":{
"type":"boolean",
"description":"Durable, workspace-scoped cross-task Agent Mail endpoints and events.",
"default":false
},
"event_stream_resume":{
"type":"boolean",
"description":"`GET /v1/threads/{id}/events` puts the durable `seq` on every journal\nframe as the SSE `id:` and resumes from a `Last-Event-ID` header, so a\nbrowser `EventSource` reconnects without a cursor in the query string.",
"default":false
}
},
"required":[
"client_token_intents",
"account_session",
"account_model_owner",
"session_provider_keys",
"threads",
"thread_create_idempotency",
"thread_idle_precondition",
"thread_patch_undo_turn_id",
"thread_scoped_reads",
"thread_scoped_git_writes",
"thread_scoped_snapshots",
"thread_scoped_terminals",
"worktrees",
"job_operations",
"thread_shell_consent",
"turns",
"turn_operation_idempotency",
"turn_operation_lookup",
"watch_event_turns",
"structured_review_turns",
"structured_review_version",
"automation_mutation_preconditions",
"turn_image_inputs",
"turn_output_token_limit",
"profile_constitution",
"turn_steer",
"turn_interrupt",
"event_replay",
"external_tools",
"environments",
"worker_runtime",
"fleet_run_create",
"thread_fleet_members",
"thread_fleet_member_durable_operations_v1",
"fleet_run_start",
"fleet_event_replay",
"fleet_event_stream",
"fleet_local_target",
"thread_goals",
"memory",
"mcp_server_management",
"skill_lifecycle",
"skill_detail",
"plugin_management",
"agent_mail",
"event_stream_resume"
],
"description":"Fixed capability map advertised by `GET /v1/runtime/info`.\n\nAll fields are required on serialization so clients can rely on the shape."
},
"FleetProfileDocument":{
"type":"object",
"additionalProperties":false,
"properties":{
"id":{
"type":"string"
},
"kind":{
"$ref":"#/$defs/FleetProfileDocumentKind"
},
"origin":{
"type":"string"
},
"path":{
"type":[
"string",
"null"
]
},
"text":{
"type":"string"
},
"revision":{
"type":"string"
},
"writable":{
"type":"boolean"
},
"refusal_reason":{
"type":[
"string",
"null"
]
}
},
"required":[
"id",
"kind",
"origin",
"path",
"text",
"revision",
"writable",
"refusal_reason"
]
},
"FleetProfileDocumentKind":{
"type":"string",
"enum":[
"profile",
"fleet"
]
},
"RuntimeExperimentalCapabilities":{
"type":"object",
"properties":{
"environments":{
"type":"boolean",
"default":false
}
},
"required":[
"environments"
],
"description":"Experimental opt-in flags advertised by `GET /v1/runtime/info`.\n\nFields are additive and default to `false` when omitted by older servers."
},
"DynamicToolCallParams":{
"type":"object",
"properties":{
"thread_id":{
"type":"string"
},
"turn_id":{
"type":"string"
},
"call_id":{
"type":"string"
},
"namespace":{
"type":"string",
"description":"Optional namespace that was registered with the tool."
},
"tool":{
"type":"string",
"description":"Tool name that the model invoked."
},
"arguments":{
"description":"Arguments supplied by the model, validated against `input_schema`."
}
},
"required":[
"thread_id",
"turn_id",
"call_id",
"tool",
"arguments"
],
"description":"Parameters identifying a dynamic tool call request emitted by the runtime.\n\nThis is the typed payload for `tool_call.requested` events and also the\nnatural identifier used when the runtime looks up a pending call."
},
"DynamicToolSpecOutput":{
"type":"object",
"properties":{
"namespace":{
"type":"string",
"description":"Optional namespace that groups related tools (e.g. `\"tau_bench\"`).\nWhen present, the runtime may expose the tool as\n`<namespace>::<name>` to the model."
},
"name":{
"type":"string",
"description":"Short tool name. Combined with `namespace` it forms a unique tool id."
},
"description":{
"type":"string",
"description":"Human-readable description exposed to the model."
},
"input_schema":{
"description":"JSON Schema describing the tool's input parameters."
},
"defer_loading":{
"type":"boolean",
"description":"If true, the runtime may defer schema validation / tool loading until\nthe model actually calls the tool.\n\nDefaults to `false` so that older clients omitting this field still\nbehave the same way.",
"default":false
}
},
"required":[
"name",
"description",
"input_schema",
"defer_loading"
],
"description":"Specification for a dynamic external tool registered by a runtime client.\n\nExample JSON from the spec:\n\n```json\n{\n \"namespace\": \"tau_bench\",\n \"name\": \"get_reservation\",\n \"description\": \"Look up an airline reservation.\",\n \"input_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"reservation_id\": { \"type\": \"string\" }\n },\n \"required\": [\"reservation_id\"],\n \"additionalProperties\": false\n }\n}\n```"
"description":"A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type":"string"
}
},
"properties":{
"codexHome":{
"allOf":[
{
"$ref":"#/definitions/AbsolutePathBuf"
}
],
"description":"Absolute path to the server's $CODEX_HOME directory."
},
"platformFamily":{
"description":"Platform family for the running app-server target, for example `\"unix\"` or `\"windows\"`.",
"type":"string"
},
"platformOs":{
"description":"Operating system for the running app-server target, for example `\"macos\"`, `\"linux\"`, or `\"windows\"`.",
Loaded 100 of 2702 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.