Commit Graph

7375 Commits

Author SHA1 Message Date
Vincent Koc c40e787309 fix(ci): preserve iOS Fastlane root after cd (#128854) 2026-08-24 14:06:37 -07:00
Peter Steinberger 2a39c50227 fix: make full verification hermetic across local environments (#128777)
* test: harden full verification fixtures

* test: resolve main fixture overlap
2026-08-24 13:26:00 -07:00
Vincent Koc 88f417d328 fix(ci): pin iOS Fastlane toolchain (#128735)
* fix(ci): pin iOS Fastlane toolchain

* fix(ci): enforce locked iOS Fastlane path

* fix(ci): route iOS docs through bundle

* fix(ci): pin all documented iOS Fastlane commands

* fix(ci): make iOS bundle selection canonical

* fix(ci): enforce the locked iOS Fastlane path

* fix(ci): pin Bundler in iOS wrapper

* fix(ci): require the iOS Fastlane Gemfile

* fix(docs): pin direct iOS Fastlane bundle
2026-08-24 11:45:05 -07:00
Peter Steinberger 3c2667ca40 fix(gateway): fill older chat pages without duplicate transcript storage (#128810)
* fix(gateway): page visible chat history without duplicate storage

* chore: keep release changelog owned by release automation
2026-08-24 10:43:59 -07:00
Ayaan Zaidi 614e084cf8 improve: serialize Telegram Mantis proofs (#128811)
Queue Telegram-visible Mantis proofs at workflow level so only one shared-user run allocates a runner at a time.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-24 23:10:45 +05:30
Vincent Koc 7d08e40e2f fix(qa): run explicitly selected Telegram RTT scenarios (#128795)
* fix(qa): support explicit Telegram RTT scenarios

* docs(qa): document Telegram RTT scenario selection

* fix(qa): expose Telegram RTT scenario in manual E2E

* fix(qa): gate historical Telegram downgrade proof
2026-08-24 10:26:30 -07:00
Shakker 59a9221180 feat: persist bounded transcript display rows (#126939)
Persist bounded transcript display rows and lazy projection lifecycle in the per-agent SQLite store.

Refs #126914.
2026-08-24 17:28:49 +01:00
Peter Steinberger 70d7fb5771 refactor(tooling): remove test-only production helper exports (#128770) 2026-08-24 08:12:58 -07:00
Peter Steinberger e45e4ca69f fix(scripts): ignore superseded target dispatch checks (#128704) 2026-08-24 05:09:28 -07:00
Vincent Koc 41050b2f3b fix(ci): isolate release QA lane locks (#128458)
Extract the QA concurrency portion of #127140 while leaving the iOS runner authorization changes out.
2026-08-24 05:03:10 -07:00
Vincent Koc cf4f112425 perf(ci): split Telegram prerelease tests (#128459)
* perf(ci): split Telegram prerelease tests

* fix(ci): preserve Telegram test process recycling

* fix(ci): shard runnable Telegram tests

* fix(ci): cap Telegram prerelease jobs

* fix(ci): correct Telegram shard heredoc

* test(ci): type Telegram prerelease matrix

* docs(ci): explain Telegram prerelease shards
2026-08-24 03:02:46 -07:00
Peter Steinberger 234df15a6d chore: refresh dependencies after seven-day cooldown (#128414)
* build(deps): refresh dependencies after cooldown

Apply dependency, toolchain, action, image, and exact tool updates released by the inclusive 2026-08-16 seven-day cutoff. Adapt owner boundaries for the resulting CUA, logging, Teams, Markdown, native, and test-harness contract changes while retaining versions blocked by upstream compatibility constraints.

* fix(ui): align markdown renderer env typing

* fix(deps): align postcss and mistral peer contracts

* fix(deps): repair refreshed dependency contracts

* fix(deps): retain tslog startup budget

* fix(ci): verify Android tools with SHA-256

* fix(ci): fence Android SDK cache version
2026-08-24 03:01:54 -07:00
Alix-007 0c98de6b81 fix(cli): return JSON for curator and workshop failures (#128601)
* fix(cli): render curator and workshop JSON failures

* test(cli): cover inherited JSON on skill failures

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-24 02:45:44 -07:00
Peter Steinberger 5e253f4f42 fix(cli): render hooks report JSON failures (#128641) 2026-08-24 02:21:41 -07:00
Peter Steinberger 12d0fd2ef8 refactor(anthropic): replace handwritten Claude sessions with Agent SDK (#128131)
* refactor(anthropic): explore official Claude Agent SDK runtime

* refactor(anthropic): replace handwritten Claude sessions with SDK

* refactor(anthropic): collapse SDK live-session ownership

* refactor(anthropic): simplify SDK ownership and preserve live skills

* fix(anthropic): fence cancelled SDK runs before process startup

* fix(anthropic): harden SDK approvals, lifecycle, and packaging

* refactor(anthropic): own SDK process trees and streamline runtime

* fix(anthropic): repair rebased packaging and legacy test fixtures
2026-08-24 01:59:16 -07:00
Vincent Koc b13baca3fb fix(ci): prepare gateway profile live runtime (#128605)
Co-authored-by: Dallin Romney <dallinromney@gmail.com>
2026-08-24 01:30:48 -07:00
Peter Steinberger e62725aa5a fix(cli): render agent identity JSON failures (#128590) 2026-08-24 00:37:16 -07:00
Jesse Merhi 3d06659e31 refactor(scripts): consolidate build memory fixtures (#128506) 2026-08-24 17:10:09 +10:00
Peter Steinberger 8db6cd0e7e perf(test): preserve unit-fast project ownership (#128554) 2026-08-23 23:23:16 -07:00
Alix-007 d9c350ac7d fix(install): retain backup through final verification (#128502) 2026-08-23 23:21:32 -07:00
Ayaan Zaidi b41d5293b2 refactor(mantis): give Codex open-ended Telegram proof control (#128197)
* refactor(mantis): replace Telegram proof compiler with frozen visible replay

* refactor(mantis): let Codex own Telegram proof scenarios

* fix(mantis): isolate proof publication

* fix(mantis): bind baseline cache to revision

* chore(mantis): remove stale scenario-designer wording

* fix(mantis): align readable worktrees with trusted proof

* fix(mantis): register proof collector tooling

* refactor(mantis): keep collector functions private

* fix(mantis): publish agent-selected Telegram proof

* fix(mantis): trim proof media to final turn

* fix(mantis): fence lanes before evidence collection

* fix(mantis): verify lane termination before unlock
2026-08-24 11:47:26 +05:30
Peter Steinberger 2531b166c0 fix(cli): render agents add JSON failures (#128517) 2026-08-23 22:39:34 -07:00
Vincent Koc 1ece62a4d8 fix(ci): gate source package producers (#128463)
* fix(release): preflight package source metadata

* fix(ci): gate source package producers

* fix(ci): preflight canonical package producers

* fix(ci): skip no-package source setup

* fix(ci): normalize source package validation

* fix(ci): normalize Docker package reporting

* fix(ci): avoid generic dependency guard

* test(ci): cover gated package identity
2026-08-23 22:30:10 -07:00
ClawSweeper 447ffb0719 fix(exec): align Guardian approvals for bound compound plans (#128279)
* fix(exec): review bound compound gateway plans

* test(exec): allow optional plan revalidation

* fix(exec): publish Guardian review lifecycle

* fix(exec): bound compound Guardian reviews

* fix(exec): bind Guardian reviews to execution lifecycle

* fix(ci): tolerate bounded startup RSS variance

---------

Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-23 22:07:14 -07:00
Peter Steinberger ccc10bf098 perf(test): avoid duplicate focused Gateway suite execution (#128486) 2026-08-23 21:24:49 -07:00
Ayaan Gazali a6a765cd44 test(scripts): wait for the e2e state path file to have content (#128403)
* test(scripts): wait for the e2e state path file to have content

waitForFile returned as soon as the path file existed. The child creates that
file and writes it in separate syscalls, so the reader could observe it empty,
read "" as the state dir, and fail the existence assertion on a path that was
never written. That surfaces as "expected false to be true" under CI load.

Wait for non-empty content instead.

* test(scripts): make the state path publication race deterministic

The suite previously published the path in one write, so it passed with either
readiness predicate and protected nothing. Publish the empty file first and
delay the real write, which is the window a real writer leaves open, so the
case fails on the existence-only check and passes on the content-aware one.

* test(scripts): publish e2e state paths atomically

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-23 21:22:05 -07:00
Jesse Merhi 0e8faacd71 fix(scripts): build heap ignores its systemd memory budget and takes the full default (#123979)
* fix(scripts): size the tsdown heap from the build's own cgroup budget

The build heap probe only read the cgroup root (/sys/fs/cgroup/memory.max and
the v1 equivalent). Those files exist only when the process runs in a
namespaced container cgroup; under systemd the budget lives on the process's
own slice, and the v2 root carries no limit at all. So every systemd-managed
build found no limit, fell back to /proc/meminfo MemTotal, and took the full
12288 MB default heap regardless of its actual budget.

Observed on a 15.4 GiB host: openclaw-main-update.service ran tsdown with
NODE_OPTIONS=--max-old-space-size=12288 while its user@999.service slice was
bounded at 5 GiB, reaching 3.2 GB RSS and 6.25 GB peak before the host began
OOM-killing unrelated services.

Resolve the limit from /proc/self/cgroup and walk that chain instead, reading
memory.high alongside memory.max (memory.high throttles reclaim rather than
failing allocation, so a heap above it stalls the build instead of OOM-ing),
and take the tightest bound found. Root paths stay as the container fallback,
and an explicitly injected path list still disables detection.

Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>

* fix(scripts): resolve the build heap budget from the v1 memory controller too

The slice walk only accepted the unified 0:: record, so a legacy or hybrid
systemd host fell back to the root probe and kept taking host memory. One
resolver now walks both hierarchies leaf-to-root, which makes the static root
list its own depth-0 case and removes it.

Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>

* fix(scripts): read cgroup controller mounts instead of assuming their paths

v1 controllers can be co-mounted at the cgroup root, where memory.limit_in_bytes
sits under the slice with no per-controller directory, so the hardcoded
/sys/fs/cgroup/memory probe missed the budget and the build took the full
12288MB default. Mount points now come from mountinfo.

Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>

* fix(scripts): translate cgroup records through the mount root

mountinfo field 4 is the subtree a cgroupfs mount exposes. Under a container
mount the /proc/self/cgroup record stays host-absolute, so walking it verbatim
probed paths below the visible mount and the build fell back to host memory.
Records now translate through the mount root before the walk.

Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>

* fix(scripts): skip cgroup mounts that cannot represent this process

Falling back to the mount root for a record outside the mount's subtree sized
the build from an unrelated cgroup: an inherited namespace clamped the heap to
the 2048MB floor from a foreign 1GiB limit. Non-representable mounts are now
skipped, and the blind root probe only runs when no memory record exists.

Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>

* fix(scripts): keep every cgroup mount view, not just the last one seen

One hierarchy can be visible through several mounts and only some expose a
subtree containing this process. Retaining only the last view dropped the
budget whenever a non-representable bind view came later, sending the build
back to host MemTotal.

Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>

* fix(scripts): decode octal-escaped mountinfo paths before matching cgroups

ClawSweeper P2 on 7e64ad61f7: the cgroup resolver compared mountinfo's mount
root and mount point verbatim. The kernel escapes space, tab, newline, and
backslash in those two fields, so any cgroup mounted under such a path never
matched, the bounded slice was missed, and heap sizing silently fell back to
host memory.

Decode both fields before matching. The decoder lives in scripts/lib beside the
other shared script helpers rather than inline, so the scripts program has one
copy rather than a new ad hoc one.

Regression test fails pre-fix: a v2 mount at "/sys/fs/cgroup\040dir" with a
5 GiB memory.high yields --max-old-space-size=12288 (host fallback) before the
fix and 4352 after.

Follow-up, deliberately not bundled here: src/infra/sqlite-wal.ts,
src/commands/doctor-state-integrity.ts, and src/plugins/bundled-source-overlays.ts
each carry their own private copy of this same decoder. Consolidating all four
into @openclaw/normalization-core is the right end state, but it touches a
shared package plus three core modules and belongs in its own reviewable change.

Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>

* fix(scripts): resolve cgroup-namespace-relative records to their mount

ClawSweeper P1 on d6fe49dd3f: inside a cgroup namespace /proc/self/cgroup
reports the namespace root ("0::/") while mountinfo field 4 stays the host
subtree the cgroupfs was mounted from ("/docker/<id>"). relativeCgroupPath then
found no prefix match and returned null; because a memory record had already
been seen, the root probe was skipped and the build fell back to host MemTotal.
A constrained container therefore missed its own budget entirely.

That namespace root is exactly what the mount exposes at its mount point, so it
resolves to "/" rather than failing closed.

Regression test fails pre-fix: a "0::/" record against a /docker/2f1a9c mount
root with a 5 GiB memory.max yields --max-old-space-size=12288 before the fix
and 4352 after.

Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>

* fix(scripts): reject inherited cgroup mount views instead of guessing

ClawSweeper P1 on b4d200c5d2: the previous commit resolved a namespace-relative
record against any mount root, including the inherited views cgroup_namespaces(7)
documents, whose field-4 root reads "/..". Which cgroup such a view exposes is not
derivable from mountinfo, so probing it can size the build from an unrelated
cgroup's limit.

Reject non-canonical mount roots outright. An undecidable view now falls back to
host sizing, which is current main's behavior, rather than silently adopting the
wrong budget.

Regression test covers the "/.." inherited mount: it must yield host MemTotal
sizing, not the 5 GiB limit sitting behind that mount.

Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>

* fix(scripts): fail closed on namespace-root records against non-root mounts

ClawSweeper P1 on 731d3bbc8e: a "0::/" record does not prove that a mount
rooted at some other subtree exposes this process's cgroup. Resolving that pair
could cap the build heap from an unrelated cgroup's limit.

Return no mapping for it. An undecidable pair now falls back to host sizing,
which is current main's behavior, so the failure mode is a missed optimisation
rather than a wrong budget. The "/.." inherited-mount rejection stays; this
covers the broader ambiguous mapping it did not.

The namespace-relative test is repointed accordingly: an unrelated mounted
subtree must yield host sizing, not that subtree's limit.

Net production change: none (4 lines swapped).

Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>

* fix(build): cap tsdown heap to the real budget and refuse hosts that cannot build

The 2048MB floor was applied on top of a discovered cgroup limit, so a small
container was handed a heap larger than it could honour. Measured in real
cgroups, that does not OOM-kill, it thrashes: a 1500MiB container sat pinned at
its ceiling for 10 minutes with oom_kill at 0, never finished the second of
eleven invocations, and starved every other process on the host.

Cap to the discovered budget, then refuse up front when that budget cannot hold
the build. The threshold is the whole-build peak, not a single pass: a full
eleven-invocation build peaks at 4730MiB, so a 5GiB slice completes while 4GiB
and 2816MiB slices are both killed partway through the third invocation.

The refusal runs before any output is cleaned, so a host that cannot rebuild
does not also lose the build it has.

* fix(build): harden tsdown heap admission

* fix(build): guard the default tsdown plan

* fix(build): preserve runtime-only Docker builds

* fix(build): admit only declaration cache misses

* fix(build): scope heap admission to real budgets

* fix(build): guard direct unified declarations

* fix(build): guard the canonical tsdown config

* fix(build): satisfy cache planning lint

* fix(gateway): release empty orphan leases

* fix(build): cap cgroup budget by host memory

* fix(build): serialize the canonical tsdown config

* test(build): freeze host memory fixtures

* fix(build): honor cgroup v1 soft limits

* fix(build): respect cgroup v1 hierarchy mode

* fix(build): admit unified runtime plans

* fix(build): admit every unified runtime path

* fix(build): collect repeated tsdown filters

* fix(build): ignore cgroup v1 soft limits

* fix(build): use explicit heap override as opt-in

* refactor(build): simplify memory admission

* fix(build): harden constrained build recovery

* fix(ci): prebuild runtime before real CLI shards

* fix(build): honor runtime-only runner environment

* fix(ci): satisfy tooling shard lint
2026-08-24 14:18:48 +10:00
Peter Steinberger 4d617f44f3 feat(telemetry): opt-in anonymous usage stats on a daily update ping (#128476)
* feat(telemetry): add opt-in anonymous usage reporting

* docs(telemetry): document anonymous update and privacy controls

* feat(telemetry): report show state as JSON

Give the reporting command a machine-readable form and classify the
group and mutations against the CLI JSON-output policy.
2026-08-23 21:18:04 -07:00
Peter Steinberger eef9aaa2ea fix(cli): render agent binding JSON failures (#128484) 2026-08-23 21:16:19 -07:00
Peter Steinberger 2aa5eee34e feat(channels): custom emoji discovery via emoji-list across Discord, Slack, Telegram (#128435)
* feat(channels): custom emoji discovery via emoji-list across Discord, Slack, Telegram

Make custom emojis discoverable by the agent. The message tool's emoji
param now documents custom-emoji syntax per channel (gate-aware, only
naming emoji-list when the action is actually advertised). Discord
emoji-list defaults guildId from the current conversation and returns
reaction-ready { name, identifier, animated? } entries; Slack returns
normalized shortcodes with aliasOf. Telegram gains emoji-list backed by
one canonical allowed-reactions owner (getChat available_reactions,
custom_emoji entries preserved), numeric custom-emoji reactions, and
replaces the dead 'reaction disallow list' error advice with a bounded
sample of the chat's allowed reactions.

* test(channels): expect telegram emoji-list provider-owned read gate in plugin shape contract

* test(telegram): prove emoji-list authority chain via mock-gateway e2e

Ephemeral gateway + mock Bot API + mock OpenAI provider: current-chat
emoji-list returns normalized standard and custom_emoji identifiers with
exactly one getChat call; a delegated cross-chat request is rejected with
the conversation-binding error and zero Bot API requests reference the
foreign chat.
2026-08-23 20:39:06 -07:00
Peter Steinberger 8b18d22db1 test(e2e): share canonical auth store reader (#126960)
* test(e2e): read shared auth store after onboarding

* test(e2e): harden shared auth proof

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-08-23 18:00:36 -07:00
xingzhou bbb644353d fix(cli): emit JSON for trajectory export failures (#128351)
* fix(cli): render trajectory export JSON failures

Co-authored-by: zhang-guiping <zhang.guiping@xydigit.com>

* fix(cli): route invalid trajectory stores through root errors

Co-authored-by: zhang-guiping <zhang.guiping@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-23 17:57:03 -07:00
chengzhichao-xydt fef6c6d3b4 fix(ci): bound docs sync publish fetches (#110294)
Punchcard-Session: ember-lantern-harbor-24
2026-08-23 17:35:47 -07:00
wahaha1223 9560227ccf fix(release): fill log windows across short reads (#109424)
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-08-23 17:06:11 -07:00
mikasa 8a1c18c778 fix(ci): bound plugin-clawhub-release git fetch with timeout (#110736)
Punchcard-Session: clear-orchard-harbor-ez
2026-08-23 17:03:15 -07:00
coaiMax 0f57d3f797 fix(release): bound beta verifier command execution (#111581)
Punchcard-Session: silver-meadow-cedar-x6
2026-08-23 16:14:47 -07:00
sunlit-deng 5833f53c18 fix(gateway): invalidate stale worker disk-space projections (#123442)
* fix(gateway): invalidate stale worker disk-space projections

* test(qa): prove worker disk-space projection eviction

* test(qa): narrow operator client in proof

* test(gateway): keep retired disk projection fence stable

Co-authored-by: sunlit-deng <yang.jiajun1@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-23 16:12:14 -07:00
Vincent Koc ab013fac6c fix(test): provide runtime to Ollama parity registration (#128396)
Punchcard-Session: ember-valley-orchard-5e
2026-08-23 15:59:26 -07:00
Vincent Koc 3d302c58c6 fix(release): register focused beta evidence on main (#128391)
* fix(release): repair frozen beta validation blockers (#128137)

* fix(release): repair frozen beta validation blockers

* fix(release): bind reviewed scanner evidence

* test(release): align frozen beta validation contracts

* fix(release): accept statusless 9a83 QA evidence (#128188)

* fix(release): admit final beta QA evidence (#128231)

Admit the exact final beta candidate to the existing fail-closed statusless QA compatibility profile and its parameterized validation matrix.

* fix(release): authorize focused beta evidence
2026-08-23 15:48:04 -07:00
Peter Steinberger a3e69b2ba0 fix(cli): render sessions list JSON failures (#128316) 2026-08-23 14:11:26 -07:00
Peter Steinberger 917b5b258c test: deduplicate platform and runtime fixtures (#128323) 2026-08-23 12:05:26 -07:00
Vyctor H. Brzezowski 7941c05e44 test(control-ui): guard chat streaming runtime budgets (#128154)
* test(control-ui): gate chat stream runtime budgets in e2e

* test(control-ui): strengthen stream runtime budget gates

* test(control-ui): close stream budget review gaps

* test(control-ui): prove deferred tool projection
2026-08-23 13:50:35 -03:00
Peter Steinberger 7d154d32c1 fix(cli): render session validation JSON failures (#128224) 2026-08-23 09:43:52 -07:00
Peter Steinberger 6cde399d52 fix(release): install Bun smoke from candidate payload (#128243)
* fix(release): bind Bun smoke to candidate payload

* test(release): align Bun payload workflow contract
2026-08-23 09:25:46 -07:00
zengLingbiao a599b50065 fix(ci): bound trufflehog installer download timeouts (#117480)
Punchcard-Session: cobalt-orchard-harbor-y7
2026-08-23 08:30:48 -07:00
Vincent Koc e27fb6680f fix(ci): validate complete plugin binding proof logs (#128235)
* fix(ci): validate complete plugin binding proof logs

* fix(ci): preserve bounded parser diagnostics

* fix(ci): route plugin binding parser proof
2026-08-23 08:23:48 -07:00
Jesse Merhi ee6e0251b4 fix(scripts): clean up tsgo process trees on timeout or signal (#123975)
* fix(scripts): bound tsgo runs with the managed-command watchdog

run-tsgo bypassed the repo's managed-command seam and called spawnSync
directly, so a wedged tsgo blocked its caller indefinitely: no timeout, no
process-group cleanup, and no SIGKILL escalation.

Observed in the wild as a tsgo holding 2.85 GB for 90+ minutes on 41s of
total CPU with RSS frozen to the byte, ignoring SIGTERM, with its wrapper
reparented to init. Because shouldReclaimLock() treats a live PID as a valid
lock owner, that orphan also held the heavy-check lock until every other
invocation hit the 10-minute lock timeout and threw.

Route the run through runManagedCommand, which already owns process-group
termination and SIGKILL escalation on timeout, and bound it with
OPENCLAW_TSGO_TIMEOUT_MS (default 45m) through the shared readPositiveEnvInt
helper, mirroring OPENCLAW_CLI_STARTUP_BUILD_TIMEOUT_MS in
ensure-cli-startup-build.mts.

* fix(scripts): saturate the tsgo watchdog at Node's timer ceiling

An OPENCLAW_TSGO_TIMEOUT_MS above 2147483647 reached setTimeout unchanged,
where Node collapses it to a 1ms delay, so raising the override killed
healthy typechecks immediately instead of loosening the bound.

* fix(scripts): make the tsgo watchdog opt-in and stop the harness leaking

ClawSweeper review on 6ba02c9d0a raised two findings.

[P1] The 45-minute default applied an unproven deadline to every tsgo
invocation. No supported duration contract covers every host and project, and
CI already bounds its own tsgo jobs at 15-20 minutes, so the default could only
ever fire outside CI where it was least validated. Drop it: an unset
OPENCLAW_TSGO_TIMEOUT_MS keeps the pre-existing unbounded wait, so no existing
run changes behavior, and operators opt in per host. Documented in
docs/help/testing.md beside the sibling Vitest watchdog.

[P2] The regression harness could leak its wedged child. The fake compiler
ignores SIGTERM by design, so a pre-fix or otherwise failing run left the tree
running after spawnSync gave up. Bound the fixture's loop as a backstop.

* fix(scripts): set the tsgo watchdog default from measured lane duration

ClawSweeper on c7a699ee82 reversed its earlier guidance: the opt-in default
adopted last iteration "deliberately preserves the indefinite tsgo hang that
this PR is meant to fix". Its objection was never that a default existed, only
that 45 minutes was unmeasured.

Measured instead of guessed: hosted tsgo lanes (check-test-types, and its core
stripes) complete in 1-2 minutes across recent successful main runs, against CI
job caps of 15-20 minutes. 30 minutes is 15-30x the observed duration, leaves
room for a far slower local host, and still bounds the 90-minute and multi-hour
wedges that motivated this PR. OPENCLAW_TSGO_TIMEOUT_MS remains the documented
override for hosts that need longer.

* test(scripts): reap the wedged fake tsgo tree on the harness outer timeout

ClawSweeper on 0d9f3604e8 flagged that the harness can still leave a detached
pre-fix process tree alive after its outer timeout. The bounded fixture loop
added earlier only capped the leak; it did not terminate the tree.

spawnSync's killSignal reaches the direct child only. runManagedCommand spawns
the compiler detached into its own process group, so the fake tsgo is a
grandchild that never receives that signal. The fixture now records its pid and
the harness reaps that group in a finally, with the bounded loop kept as a
last-resort backstop.

Verified: pid file written with the live pid, and killing that group terminates
the tree; focused suite 16/16 with no surviving fake-tsgo processes.

* fix(scripts): harden the tsgo watchdog after two-phase code review

Review fixes on top of the watchdog change, from one native pass and six cold
passes:

- A rejected OPENCLAW_TSGO_TIMEOUT_MS escaped main() as a raw module rejection.
  It now reports one actionable line and exits 1. Strict validation was kept
  rather than switching to coercion, so a typo cannot silently fall back to the
  30-minute default.
- The rejection message named a numeric range while the parser enforces plain
  decimal digits, so 1e5 and 007 were refused by a message saying they
  qualified. It now names the real format and states that the watchdog cannot
  be disabled.
- The timer ceiling is declared locally rather than imported from packages/.
  A static import there resolves before the sparse-checkout guard runs, which
  turned a clean sparse skip into ERR_MODULE_NOT_FOUND and flipped
  check-changed's typecheck lane from exit 0 to exit 1.
- The wedge test asserted the kill message but not the outcome; it now captures
  the wedged pid and asserts the process group is gone.
- Three near-duplicate "not killed" cases are table-driven.
- Doc bullet corrected: values ABOVE the ceiling saturate at it, and the
  rejected-value list now includes non-decimal input.

Deferred follow-up, not fixed here: scripts/lib/tsx-cli-shim.mjs shares a
5000ms force-kill delay with managed-child-process, so Ctrl-C can still orphan
a wedged compiler about one run in three. Measured base 4/4 orphaned versus
4/10 here, so this change improves it; the fix is out of diff and shared with
four other wrappers.

* fix(scripts): close tsgo signal cleanup race

* fix(scripts): make tsgo watchdog opt-in

---------

Co-authored-by: ClawSweeper <steipete+clawsweeper@gmail.com>
2026-08-23 14:45:40 +00:00
Peter Steinberger e87ff850e1 test(agents): isolate provider failover mocks (#128207)
Amp-Thread-ID: https://ampcode.com/threads/T-01a021f5-984a-7628-a30c-491c166ff247

Co-authored-by: Amp <amp@ampcode.com>
2026-08-23 06:12:20 -07:00
Peter Steinberger 0d18fa8d8b fix(release): preserve native prebuilds in git fixtures (#128210) 2026-08-23 05:33:05 -07:00
Peter Steinberger 0a70356c55 fix(cli): render channel capability JSON failures (#128155) 2026-08-23 02:36:36 -07:00