The receipt embeds the build commit, which CI stamps with the full
40-hex GITHUB_SHA while local builds carry the literal 'unknown'.
A full-length sha pushed the serialized receipt to 394 bytes over the
384-byte admission cap, so every CI build refused child launches and
the receipt regression tests failed on GitHub runners (they passed
locally only because 'unknown' is short). Some legitimate routes also
reached 386 bytes even without the sha.
- Truncate the embedded build commit to 12 hex chars; version + short
sha still identify the build for attribution.
- Raise the cap to 448 bytes with headroom for route growth.
- Remove the temporary debug workflow used to capture the runner
failure output.
Verified with the exact CI condition reproduced locally: a build
with DEEPSEEK_BUILD_SHA set to a full 40-hex sha now passes the
receipt tests, and the full lib suite is green (10301 passed).
Derive archive mtimes from the pinned source commit in release artifacts, retain reproducible archive bytes and executable modes, and cover metadata plus input validation.
Bind npm publication to the exact release SHA after the public asset freshness gate, without a long-lived registry token. Document the npm-side publisher binding and interactive 2FA recovery path.\n\nCloses #5299
Three v0.9.5 benchmark tasks could not launch Codewhale at all:
mteb-leaderboard, mteb-retrieve, and pytorch-model-recovery run older ARM64
images, and the aarch64-unknown-linux-gnu artifact built on ubuntu-24.04-arm
requires GLIBC_2.39.
Release and nightly now build aarch64-unknown-linux-musl on the native ARM
runner, alongside the x64 musl build that has shipped since v0.8.65. Both
gain a static check (no ELF INTERP) plus a launch smoke on the matching
native runner, so a dynamically linked or non-starting binary fails the
build rather than the user's install. docs/INSTALL.md drops the arm64 glibc
floor section and describes the v0.9.6 matrix.
Also reconciles the copy the earlier commits invalidated: setup ratification
text and all 15 locales stop attributing execution doctrine to "mode
prompts", docs/MODES.md states Auto-Review's actual deterministic
allow/deny behavior, and the source-structure, dead-code, and
runtime-contract budgets are re-measured — the last locking down 25
decreased ceilings and the new mode-agnostic prompt-stage digests.
Run the isolated Skills Manager scenario through the pty integration-test binary and use its module-qualified name so --exact executes one test instead of zero. Lock the command into the workflow contract and update the durable TUI verification examples.
Verified with the exact ignored PTY command, the workflow contract test, actionlint YAML/expression checks, and git diff --check.
Keep provider-neutral auto selection in the provider-aware TUI and launch workflow lanes from the exact running executable. Repair CNB and installer alias contracts, refresh legacy website-installed TUI bytes during upgrades, and make account pull reject an unimplemented local import truthfully.\n\nVerified with focused CLI/config tests, strict Clippy, workflow contracts, shell syntax checks, and hermetic web installer tests.
Run the ignored persistence backlog receipt test from the TUI library after the integration target move. Pin the exact Cargo command in a hermetic harness test and reject successful zero-test or missing-receipt runs so future test relocations fail clearly.
Run each ignored receipt test against its exact library path and reject Cargo's successful zero-test result explicitly. Keep the standalone tool-catalog measurement and current verification guidance on the same target, with hermetic command and failure regressions wired into CI.
The repaired measurement exposed a real duplicate AGENTS.md injection from the bounded fragment importer. Keep canonical project-context sources single-owned while still importing additional rule formats through the typed, capped fragment boundary; this restores the checked-in prompt identity without raising the budget.
Verified with the full 55-metric runtime-contract checker, focused core and TUI regressions, both Python harness suites, cargo fmt, and all-target clippy for codewhale-core and codewhale-tui.
Build only the codewhale executable, then stage codewhale and codew from the same bytes for every retained nightly target. Native jobs smoke only the executable that Cargo actually produced, while tagged-release TUI bridge filenames remain out of the nightly command contract.\n\nExtend the workflow contract test to lock the six-target, twelve-artifact inventory, byte-identity check, native smoke selection, and 14-day retention.
Record the four human contributors whose work is present in the 0.9.5 candidate, update the public credit matrix and website snapshot, and add the missing canonical identity for PR #5257. The candidate heading and compare links remain explicitly pre-tag until publication approval.
Build once through the OpenNext adapter before preview or deploy, and remove Wrangler custom-build recursion so cache population and upload use the same bundle. Keep the manual main-only exact-SHA workflow and post-deploy receipt gate intact.
Verified with deploy-preflight tests, ESLint, a complete 288-page OpenNext build, Wrangler 4.113.0 deploy --dry-run, and diff checking.
Build codewhale once, expose the verified bytes as codew across release channels, and retain seven TUI-named release aliases solely so shipped v0.9.4 clients can discover and cross the transition. Current installers and containers expose only codewhale and codew.\n\nVerified with the npm asset suite, exact 34-asset assembly, workflow contract, Homebrew renderer, release-body, dogfood installer, and shell syntax tests.\n\nRefs #5259
Release CI 7 targets x 3 binaries -> 7 x 1. Removes tui_binary/tui_artifact from matrix, cargo build -p codewhale-cli only, drops tui smoke/stage/upload and windows installer copy. Single codewhale binary + codew shim only. Part of 5260 packaging sweep (I1 -> I2).
main.rs runs the owner thread and every tokio worker at
CODEWHALE_MAIN_STACK_BYTES (16 MiB) because the engine and runtime-thread
futures are genuinely deep — 8c98bedc75 landed the worker half of that
after a debug `agent` dispatch measured a 2.25-2.5 MiB high-water mark and
died on the guard page.
`#[tokio::test]` builds its own runtime and never sees that. So the test
lanes ran the same code on ~2 MiB, and ~1 MiB on Windows: a configuration
that never ships. That is what aborted the entire Windows test binary with
STATUS_STACK_OVERFLOW in start_turn_accepts_dynamic_tools_and_environment_
id, taking ~9.7k unreported results with it and masking every other
Windows failure — the same masking 78afd8d3d4 called out when it Box::pin'd
that one frame. Box::pin fixed the frame; the environment mismatch stayed,
so the abort returned as soon as codegen moved frame sizes again.
Set RUST_MIN_STACK to the same 16 MiB on the CI test lane and on
release.yml's parity gate. std reads it for any thread spawned without an
explicit size, which covers both libtest's per-test threads and tokio's
workers, so the whole suite gets production's stack instead of the
harness default.
This is not a bigger hammer for a deep-recursion bug: 16 MiB is exactly
what the product already guarantees this code, so the tests now measure
the shipped configuration rather than a stricter one no user runs.
Closes#5246 groundwork (issue stays open until release CI runs the dist
profile). [profile.release] was tuned for shipping (fat LTO,
codegen-units=1, strip) while AGENTS.md prescribes cargo build --release as
the routine pre-push gate — so every contributor paid the full fat-LTO
pipeline on a 680k-line crate for a smoke build (8-16 min community-reported
in #4991; 13m57s measured here).
Now [profile.dist] (inherits release; lto=true, codegen-units=1,
strip=true) carries today's exact shipping optimization, and
[profile.release] relaxes to lto="thin", codegen-units=16 for the local
gate — same command, same artifacts collected by nothing. The
panic-unwinding note moves with the dist profile; neither profile sets
panic=abort (the TUI's panic supervision needs unwinding).
Workflow audit: release-artifacts.yml (the only workflow uploading
user-facing binaries) builds with --profile dist and stages from
target/<triple>/dist on both build paths. nightly.yml deliberately stays on
--release: it has always overridden LTO off (CARGO_PROFILE_RELEASE_LTO=off,
codegen-units 16) for fast prerelease builds and its build/collect paths
are internally consistent. ci.yml's release-build gate stays --release and
gets the same speedup. scripts/release/prepare-local-release-assets.js
defaults to target/release but is the local npm-wrapper smoke flow with an
explicit dir argument, not a publish path.
Measured on the 14-core M-series dev machine, warm cache, one touched file
(crates/tui/src/elapsed.rs): --release rebuild 13m57s before -> 2m16s
after (real 136.67s). cargo build --profile dist compiles with the
inherited fat-LTO settings. Authored with agent assistance (Claude); spec
by the owner's build-time lane (#5246/#5249).
Adds `telemetry-ingest/`, a Cloudflare Worker that accepts the batches
`crates/telemetry` produces and writes them to Workers Analytics Engine.
Nothing is deployed and no Cloudflare resource was created; the shipped
`telemetry_endpoint` default stays unset, `config.example.toml` and
`docs/TELEMETRY.md` are untouched. Publishing is a separate, deliberate
step.
It lives at the top level rather than inside `web/` because the site is a
separate deploy with its own Next.js/OpenNext build, and a telemetry
change should not rebuild the marketing site.
The endpoint is write-only: one POST route, every other method 405, no
route that returns data, and a zero-byte body on every status. The client
reads only the status class and drops the batch on anything non-2xx, so a
rejection here is invisible to the user by construction — which is what
lets the endpoint fail closed and refuse anything it cannot account for.
Three properties are enforced by tests rather than by intent:
`docs/TELEMETRY.md` says "Batches are IP-stripped at ingest. No IP is
stored, logged, or joined to install_id." This Worker is the whole of what
makes that true. It reads exactly two headers, never touches the request's
`cf` property, logs nothing, and turns invocation logs off in the config;
the row builder's input type is the validated batch body, so it cannot see
the connection at all. `test/no-ip.test.ts` greps the shipped source for
the header and geo names and fails the build if one appears, if the set of
headers read grows, if a console call is added, or if a Response is ever
built with a body.
The validator is a closed field set, not a permissive parse: an unexpected
key anywhere — envelope, event, counters, errors, turn_wall — rejects the
whole batch with 400. A future client bug that starts attaching a path or
a prompt is refused rather than quietly stored.
`test/schema-doc.test.ts` parses the field names and enum spellings back
out of `docs/TELEMETRY.md` and asserts set equality against the validator,
and the suite posts `crates/telemetry/tests/golden/v1.json` — the client's
own pinned wire form — asserting it is accepted and that deleting any key
from it is rejected. Doc, client, and server cannot drift apart silently.
The body cap is computed, not guessed: `parse_events` bounds a batch at
200 events and 65536 bytes, plus 199 commas and ~375 bytes of envelope,
so 66110 bytes is the conforming worst case and the cap is 72 KiB. The
512-record ring is the disk cap, not the wire cap. Rate limiting is keyed
on `install_id` from the validated body, never on an address.
Analytics Engine layout is one data point per event, indexed on
`install_id`, with the ten counters, six error classes and four turn-wall
buckets filling all twenty doubles. The README documents the exact SQL for
the two questions this exists to answer — installs and sessions, and which
error classes and panic sites dominate — one query each.
Verified locally only: 87 vitest tests, `tsc --noEmit`, `wrangler deploy
--dry-run`, and `wrangler dev --local` driven with curl (204 on the golden
batch, 400 on an unknown key and on an out-of-allowlist panic site, 413
oversized, 405 GET, 415 wrong content type, 404 other path, 429 after 20
POSTs from one install id). `wrangler dev` caught a real defect the dry
run did not: the runtime maps every named export of the entrypoint to an
entrypoint, so the route constant moved to its own module.
crates/tui/tests/runtime_web_client.test.mjs (the embedded web
client's event/snapshot state machine) ran in no workflow; it is now
a step in the always-on Version drift job. A new Integrations job
runs the five dependency-free node --test suites under integrations/
(bridge-core, feishu, telegram, wecom, weixin) on every push/PR —
6,500 LOC of bridge code previously had zero CI. weixin ships without
a lockfile by design; every suite passes with plain npm test and no
npm ci (verified locally: 11/19/39/16/3 passing, 0 failing).
Evidence: cross-surface-tech-debt-audit-2026-08-03.md §11.3 CI-wiring
rows ('Wire runtime_web_client.test.mjs into CI', 'add existing
suites to CI').
Gate: node --test crates/tui/tests/runtime_web_client.test.mjs
-> exit=0; per-bridge npm test -> all exit=0.
extensions/vscode was 0.8.53, npm/runtime-sdk 0.8.60, and the
verifiers README claimed v0.9.1 while the workspace is 0.9.4. All
three now read 0.9.4. The release.yml version gate previously checked
only workspace + npm/codewhale, which is how the drift survived
release prep; it now also requires runtime-sdk and vscode package
versions to match the tag.
Evidence: cross-surface-tech-debt-audit-2026-08-03.md TL;DR 'Stale
version strings'; §11.3 version-sweep row.
Gate: node -p require(...).version -> 0.9.4 for both packages.
fleets/v0868-stopship.toml and scripts/v0867-setup-qa.sh (audit §11.3
'incident debris') are already absent from this tree; the only trace
left was the scripts/v0867-setup-qa.sh entry in ci.yml's light/heavy
path classifier and its comment. Both removed.
Evidence: cross-surface-tech-debt-audit-2026-08-03.md §11.3 incident
debris row; files verified absent with ls + rg before editing.
* fix(ci): skip the PR-issue link gate for bot-authored PRs
The `link` job fails on every dependabot PR (e.g. #5010-#5016): dependabot
bodies are machine-generated dependency bumps that never contain a closing
keyword, so the gate exits 1 ("This PR neither closes an issue nor says why
it doesn't", run 30665868640/job 91272641294).
Skip the gate step when the PR author is a GitHub-verified bot account
(`pull_request.user.type == 'Bot'`, set by GitHub so it cannot be spoofed).
This covers dependabot today and any other automation (renovate, etc.)
without hand-editing every bot body, while keeping the gate strict for every
human PR.
Policy choice: all verified bots, not just dependabot[bot]. Bots cannot
respond to a prompt, and their changes never close issues; the check's
purpose is keeping human work linked to its issue.
* fix(ci): provision the deny container's Rust toolchain explicitly
cargo-deny runs inside EmbarkStudios/cargo-deny-action@v2's alpine container
(host triple x86_64-unknown-linux-musl, rustup default 1.85.0-musl). The
repo's rust-toolchain.toml pins `channel = "stable"` with no target, so the
action's `rustup show` (entrypoint.sh) resolves it to
`stable-x86_64-unknown-linux-musl` — not installed — and every run prints:
error: override toolchain 'stable-x86_64-unknown-linux-musl' is not
installed: the toolchain file at '/github/workspace/rust-toolchain.toml'
specifies an uninstalled toolchain
then auto-installs mid-run (network-dependent ~12s detour; hard failure if
the download stalls; seen in run 30665967039/job 91272951661).
Pass `rust-version: stable` so the entrypoint provisions the channel
(`rustup default stable`, same musl host triple) before cargo-deny touches
the workspace; the toolchain file then resolves to an installed toolchain.
* fix(deps): bump event-listener 5.4.1 -> 5.4.2 (RUSTSEC-2026-0221)
cargo-deny (advisories) fails on every dependabot PR (e.g. run
30665967039/job 91272951661) with the unsound advisory:
error[unsound]: `event-listener` allows `!Send` tags to cross thread
boundaries via `StackSlot` (RUSTSEC-2026-0221)
event-listener 5.4.1 unconditionally implements Send/Sync for StackSlot,
allowing a !Send tag to cross threads via Event::with_tag. Pulled in via
codewhale-tui -> codewhale-workflow-js -> rquickjs -> rquickjs-core ->
async-lock (and event-listener-strategy). Advisory solution: >=5.4.2, the
latest published version.
`cargo update -p event-listener` lands exactly on 5.4.2 (also drops its now
unneeded concurrent-queue dependency). No deny.toml ignore needed.
* fix(ci): fetch the locked graph before the offline runtime-contract check
The Lint job's "Check runtime-contract budget" step fails on every
dependabot PR (run 30665967040/job 91273020141, PR #5016):
error: failed to download `assert-json-diff v2.0.2`
Caused by: attempting to make an HTTP request, but --offline was specified
subprocess.CalledProcessError: ... returned non-zero exit status 101.
[runtime-contract-budget] ERROR: runtime-contract measurement failed with exit code 1
Root cause: check-runtime-contract-budget.py hardcodes CARGO_NET_OFFLINE=true
and runs `cargo test --locked` on codewhale-tui, which needs the dev-dependency
graph (wiremock -> assert-json-diff). Clippy above builds no test targets, and
Swatinem/rust-cache keys its registry cache on Cargo.lock, so any lock-changing
PR (every dependabot bump) misses the cache and the offline test cannot
download the missing crate. Reproduced locally with the identical error by
removing assert-json-diff from the registry cache and re-running the exact
CI command.
Fix: fetch the full locked graph (`cargo fetch --locked`, dev-deps included)
once in the heavy path before the measurement, so the hermetic offline
measurement is deterministic on every branch. The budget contract itself is
unchanged and still enforced.
* fix(ci): reviewed source-structure budget update for merged LaTeX module
Merging #4981 (LaTeX rendering) added crates/tui/src/tui/history/
latex_render.rs (1734 lines) and grew aggregate owned Rust source to the
measured merged state (644756 lines). The ratchet's contract requires an
explicit reviewed update for new thousand-line modules and aggregate
growth; this commit allows the merged module and raises the ceiling to
the exact CI-measured value. Verified: check-source-structure-budget.py
PASS on the branch; runtime-contract budget still PASS 55/55.
Bind receipts to a clean exact source SHA and toolchain, measure the version production PendingState would actually apply, reject impossible payload and type claims, and fail closed when macOS RSS evidence disappears. Exercise adversarial checker tests in CI and run the live RSS budget on the heavy macOS lane.
Measure the production persistence request channel with a paused consumer, a frozen same-session workload, and macOS RSS samples. Record one-way retained backlog ceilings while requiring every send and the newest snapshot to survive, and run the provider-free checker in the heavy CI lane. Production channel behavior remains unchanged.
Add an offline source-only budget for workspace packages, binary targets, thousand-line owned module identities, the largest owned Rust file, and aggregate owned Rust lines. Permit deletion and line-neutral consolidation while requiring explicit reviewed updates for new structural surface, then run the checker in the existing heavy CI gate.
Measure the provider-free model-facing contract through production-owned seams. Keep deterministic minimal prompts by visible mode, build each Plan/Act/Operate tool surface through the canonical turn registry and request planner with inert wiring, and record full and active catalog counts, bytes, token estimates, sorted names, and stable identities.
Add a hermetic representative prompt fixture that cumulatively introduces project authority, configured instructions, one workspace skill, memory, goal, and handoff. Normalize temporary roots before hashing every stage, require stable repeated identities, prove each prior marker appears exactly once while future markers remain absent, and ratchet adjacent stage deltas plus the final envelope.
Enforce 55 one-way numeric ceilings and exact structural identities, while retaining the observable repeated skill-discovery baseline and future cached-zero payload. Reject equal-size tool substitutions, removals, missing identity, cross-mode swaps, and representative-stage substitutions; keep atomic permission-preserving budget updates and the locked offline heavy-lint gate.
Run the Skills Manager compatible-root scenario in its own Unix test process after the workspace suite. This preserves the exact real-PTY assertion while preventing suite-level event starvation on loaded Linux release runners.
Fixes#4941
Signed-off-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
The pinned rust-toolchain action requires an explicit toolchain input. Add stable so the exact-candidate resolver reaches its source and artifact checks.
Signed-off-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
Match edit_file searches on an LF-normalized view while mapping the unique match back to original bytes. Normalize replacement newlines to the file style and preserve non-unique/no-op safeguards.
Cover CRLF/LF, mixed duplicates, fuzzy UTF-8 matches, and reporter credit surfaces.
Reported and root-caused by @LmeSzinc in #4764.
Co-authored-by: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com>
Signed-off-by: Nightt <87569709+nightt5879@users.noreply.github.com>
#4785 asks for a 481-attribute sweep. This is not that sweep — it is the part
that can land without judgment calls, plus the gate that makes the sweep worth
doing.
The measurements are why the gate comes first:
issue filed 464 attributes / 143 files
2026-07-26 426 / 111
2026-07-28 481 / 155
The sweep was working and the total still rose, because two large landings this
week added state whose accessors only their own tests read. A sweep is a
snapshot; a budget is a direction.
scripts/check-dead-code-budget.py counts `allow(dead_code)` across crates/ and
fails when the total exceeds a committed ceiling. Lowering it is free; raising
it needs `--update` and a sentence in the PR, so growth becomes a visible
decision rather than an accident. On failure it prints per-crate now-vs-budget
and marks which crate grew. Wired into Lint next to the other check-*.py gates.
This covers a real blind spot: CI's clippy runs without `--all-targets`, so it
never lints cfg(test) or integration-test code — and a prior strip-and-check
measured 197 attributes alive *only* because a test references them.
The three zero-risk removals from the issue's cluster list:
- `codew_legacy_shim.rs` -> `codew.rs`. The issue calls this out as a rename,
not a deletion: it is the live `codew` binary and its own docs call it
permanent. Every sweep flags the filename; the binary name is unchanged.
- `clap` dropped from app-server and `thiserror` from lane — no source in
either crate references them. Cargo.lock loses exactly those two edges and no
package versions move.
The budget baseline is recorded at today's 482, not at an aspirational number,
so the gate starts truthful and the sweep can ratchet it down.
Extracts the marketing site's hard-coded English copy into per-locale
dictionaries (19 chrome keys + 40 home keys for en, es, ja, ko, pt-BR,
ru, uk, vi) and routes nav, footer, locale switcher, sitemap, and the
home page through them.
Also:
- `lib/i18n/detect.ts` pulls Accept-Language negotiation out of
middleware into a tested module. This fixes the pt-BR bug: a browser
sending `pt-BR` was matched on the bare `pt` prefix against the wrong
candidate and could land on the English page.
- page-meta derives hreflang alternates from the locale registry instead
of a hand-maintained list, so a new locale can no longer ship without
its alternates.
- `web/scripts/check-locales.mjs` is a new CI gate: it holds every locale
dictionary at exact key parity with `en` and fails if a translation
drops a `{template}` token. Wired into ci.yml and `npm run
check:locales`.
Gates: `npm test` 168 passed (24 files), `npx tsc --noEmit` clean, `npm
run lint` clean, `node web/scripts/check-locales.mjs` PASS.
Harvested from the v0.9.2 localization lane (#3092, #4749, #4791).
Associate the email used on PR #4909 with the contributor existing in the canonical author map so the release credit gate can verify the preserved co-author trailer.
Route /preview-request through the same prepared outbound value used by production Chat, Anthropic Messages, and Responses dispatch. Report typed route, role/lane, requested-to-wire reasoning, catalog/body hashes, and canonical JSON sizes while failing closed on state preview cannot observe.
Add an explicit base-prompt-only disclosure, preserve protected effective system hashes, and represent provider-reported usage as unavailable because preview performs no request. Keep the command human-only and carry current Work-state/fork semantics through the engine refactor.
Harvested from PR #1099 by @GTC2080
Co-authored-by: TaoMu <140309575+GTC2080@users.noreply.github.com>
Align the workflow trigger with the fail-closed deploy preflight so normal pushes and pull requests run validation without creating an impossible deploy job. Pin manual deploys to the dispatched SHA and cover the workflow contract with tests.
The check read the body out of the event payload. A rerun replays the payload
the run started with, so the obvious operator move — add the missing line,
rerun the failed check — re-read the old body and failed again with no hint
why, and the only way to go green was an edit that happened to fire a fresh
event. Observed on #4885: the same body failed a rerun at 14:04:36Z and passed
a fresh event at 14:09:10Z.
Fetch the current body through the API instead. The body stays in a variable
and is never interpolated into the script, so it remains untrusted text.