Commit Graph
2818 Commits
Author SHA1 Message Date
Martin Vogel 17d61ea9be test(mcp): bind lean detect-changes accounting
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-05 06:05:08 +02:00
Martin Vogel dbadb736dd chore(output): satisfy final static checks
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-05 06:05:08 +02:00
Martin Vogel 5f58e233dc fix(output): preserve compact output safety signals
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-05 06:05:08 +02:00
Martin Vogel e842fd78cf fix(mcp): preserve lean output continuations
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-05 06:05:08 +02:00
Martin Vogel 3909e5a0e1 feat(mcp): make tool output lean and lossless
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-05 06:05:08 +02:00
Martin Vogel ceba42728a Merge pull request #1796 from liuchong/fix/daemon-start-survives-ui-config
fix(cli): a refused UI configuration is not a failed daemon start
2026-09-05 06:02:36 +02:00
Martin Vogel b3d898e12f Merge pull request #1914 from DeusData/fix/elf-hardening-relro-now
fix(build): put the whole GOT inside the RELRO window on ELF targets
2026-09-05 05:30:45 +02:00
Martin Vogel b6e4beb9b1 Merge pull request #1849 from DeusData/fix/issue-1733-windows-hook-wrapper-resume
fix: register Claude hooks without a shell
2026-09-05 05:15:47 +02:00
Martin Vogel 202c8e42b5 Merge pull request #2055 from DeusData/fix/test-wave-descendant-probe-budget
test: give the Windows descendant probe its own budget
2026-09-05 04:44:26 +02:00
Martin Vogel d97ee2be28 Merge pull request #2042 from DeusData/dependabot/npm_and_yarn/graph-ui/npm_and_yarn-34ef287347
build(deps): bump the npm_and_yarn group across 1 directory with 1 update
2026-09-05 04:32:00 +02:00
Martin Vogel a1e1beb242 Merge pull request #1851 from DeusData/fix/issue-391-sparse-absolute-loss
fix(dump): detect sparse absolute persistence loss
2026-09-05 03:23:27 +02:00
Martin Vogel 5c8ce584ea Merge pull request #2043 from DeusData/fix/lock-registry-deadline-test-deterministic
fix(tests): make the lock-registry absolute-deadline fixture deterministic
2026-09-05 00:07:04 +02:00
Martin Vogel ad479b61e9 Merge branch 'main' into fix/daemon-start-survives-ui-config 2026-09-04 22:51:27 +02:00
Martin Vogel 39c74bdd5c test: converge hook tests and smoke on the exec-form registration
The rebase onto main brought the Bash-enabled PreToolUse matcher
(Grep|Glob|Bash), which the exec-form tests still counted as Grep|Glob.

The Windows lifecycle test seeded the .cmd shell spelling as the converged
identity; the shell-free registration converges every owned shell spelling
onto one exec-form entry per matcher instead. The test now asserts that,
plus their removal on uninstall, and hands cbm_install_agent_configs the
'/'-spelled binary path cbm_cmd_install registers (cbm_mkdtemp returns a
native '\' temp root on Windows, which uninstall never derives).

Smoke 8aq proved Claude's gate registration by grepping the shim name out
of settings.json; the exec form carries no shim name, so it now checks the
binary-plus-'hook-augment' PreToolUse hook. 9d's shim-name marker would
otherwise pass vacuously after uninstall; the 'hook-augment' argument is an
owned marker there too.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-04 22:34:30 +02:00
Martin Vogel 7b92d2a621 ci(composition): scope A1d-bind-now to release links, declare fixtures by name
Step 0o (MCPB bundle contract) and Step 0q (release candidate derivation)
hand the composition gate a stub compiled straight from a .c file with the
compiler's defaults. That stub exists to exercise packaging, format detection
and the needle scans; it is not linked with -z now and never ships. A1d
measured eager binding on it anyway, so on a toolchain that binds lazily by
default (test-diag: clang-22) Step 0o died deterministically with

  A1d-bind-now stage/codebase-memory-mcp: GOT section(s) .got.plt fall
  outside PT_GNU_RELRO [15824,16384) — they stay WRITABLE after relocation

The property A1d protects is a property of the release link (-z now in
Makefile.cbm), so the assertion is scoped to it: a caller that feeds the gate
a packaging fixture declares so with CBM_COMPOSITION_FIXTURE=1 and A1d is
reported n/a — printed on every run as a named exemption, never a silent
skip. Every other assertion still runs on the fixture unchanged, and release
derivation never sets the variable.

Verified in the Linux arm64 container (Ubuntu 24.04, gcc 13.3): a stub
linked -z lazy reproduces the CI failure text on the gate without the
variable and reports n/a with it; both contracts pass under a CC wrapper
that links lazily (the shape that was red), and unchanged on macOS.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-04 22:02:47 +02:00
Martin Vogel 7d20ff9398 fix: register Claude hooks without a shell
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-04 22:00:37 +02:00
Martin Vogel 20c7dd3dbb Merge branch 'main' into fix/issue-391-sparse-absolute-loss 2026-09-04 21:54:36 +02:00
Martin Vogel 132e8fc323 test: give the Windows descendant probe its own budget
When a suite leader has already exited, taskkill /T cannot walk its tree,
so run-test-wave.py proves cleanup by asking Windows whether anything is
still parented to the dead PID. That probe was timed with --kill-grace,
which the parallel gate passes as 1s. A cold powershell.exe + CIM start
routinely costs more than that on a runner, so TimeoutExpired -- a fact
about interpreter start-up, not about the process tree -- was read as
"assume the worst" and failed an already-clean shard with exit 2.

Give the probe its own stable-state budget (15s) plus one retry, keeping
the fail-closed semantics: an exhausted or failed probe still counts as
"cannot prove absence". The refusal now names which of the two happened,
an unfinished probe or a counted set of live descendants, instead of
reporting both as a leaked tree.

The contract test pins this structurally -- no sleeps, no timing
thresholds: the probe must be bounded by the module constant rather than
by the kill-grace argument, must retry a timed-out probe, must never read
an unusable probe as absence, and must report the two failures
distinctly. Its Windows leader-exit case now waits on the scheduler's
refusal rather than on a deadline shorter than the new budget.

Refs #1741

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-04 17:58:37 +02:00
dependabot[bot] 6b0c44afcf build(deps): bump the npm_and_yarn group across 1 directory with 1 update
Bumps the npm_and_yarn group with 1 update in the /graph-ui directory: [fflate](https://github.com/101arrowz/fflate).


Updates `fflate` from 0.8.2 to 0.8.3
- [Release notes](https://github.com/101arrowz/fflate/releases)
- [Changelog](https://github.com/101arrowz/fflate/blob/master/CHANGELOG.md)
- [Commits](https://github.com/101arrowz/fflate/compare/v0.8.2...v0.8.3)

Updates `fflate` from 0.6.10 to 0.6.11
- [Release notes](https://github.com/101arrowz/fflate/releases)
- [Changelog](https://github.com/101arrowz/fflate/blob/master/CHANGELOG.md)
- [Commits](https://github.com/101arrowz/fflate/compare/v0.8.2...v0.8.3)

---
updated-dependencies:
- dependency-name: fflate
  dependency-version: 0.6.11
  dependency-type: indirect
- dependency-name: fflate
  dependency-version: 0.8.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-04 14:25:23 +00:00
Martin Vogel 7e47043acc fix(tests): make the lock-registry absolute-deadline fixture deterministic
lock_registry_absolute_deadline_survives_repeated_wakes has failed on the
test-unix (macos-15-intel) leg of three unrelated pull requests in one
week -- #1342 (08-28), #1811 (09-02) and #1819 (09-03, run 33799475629,
job 100823626965) -- always with the same signature:

  FAIL tests/test_lock_registry.c:1153: ASSERT(tail_queued)
  7648 passed / 1 failed

The registry is not racy. cbm_lock_registry_acquire enqueues the waiter
synchronously under the registry mutex before any wait, so waiter_count
and the attempting count are exact. The defect is in the fixture: it
raced two wall-clock windows against each other, both anchored to a
timestamp the observer thread took before the tail thread had even been
scheduled.

  deadline_start = cbm_now_ms();
  tail.deadline_ms = deadline_start + 200;  /* the tail's acquire deadline */
  queue_deadline   = deadline_start + 100;  /* the observer's budget */

Because both windows start before the tail runs, a loaded runner breaks
the fixture two different ways:

  * the tail is scheduled inside its deadline but after the observer's
    100 ms budget has expired -- the queued state existed and was simply
    no longer being looked at; or
  * the tail is scheduled more than 200 ms late, in which case its
    deadline has already passed when it finally calls acquire, the
    pre-registration deadline check in lock_registry_acquire_internal
    returns BUSY immediately, and the tail never enqueues at all -- the
    asserted state can then never occur, however long the observer waits.

lock_registry is in the parallel wave of run-tests-parallel.sh, not the
serial tail, so on a small CI runner it competes with a full wave of
sanitized suites -- exactly the scheduling delay both paths need.

Widening the observer's budget would only paper over the first path, and
the state it waits for is transient by construction: it exists only
between the tail's enqueue and the tail's own deadline. So the fixture is
rebuilt to observe states that cannot evaporate.

  * The tail anchors its absolute deadline itself, in its own thread,
    immediately before the acquire it bounds. Scheduling delay can no
    longer consume the deadline before the call starts, so the enqueue is
    unconditional, and elapsed is measured from the tail's own anchor --
    it now times the registry instead of timing the scheduler.

  * Enqueue is exposed as a monotonic counter,
    cbm_lock_registry_waiter_enqueue_count_for_test, next to the existing
    test_condition_wait_calls counter it is modelled on. Because the
    count only ever grows, the observer reads it once after the tail has
    returned rather than trying to catch a live queue depth: the polling
    loop, and with it the window, is gone.

  * The fixture's remaining 500 ms and 600 ms budgets become the file's
    LOCK_REGISTRY_TEST_TIMEOUT_MS backstop, and each loop exits on the
    state it waits for instead of on the clock, so the backstop only
    fires when the product is actually broken.

The contract is unchanged: the tail must still return at its absolute
deadline (150 <= elapsed < 350 ms for a 200 ms deadline) despite ~40
unrelated cancel broadcasts, still with BUSY and no lease, with the head
still holding the attempt. The broadcast loop now starts immediately
after the tail is released, so it overlaps the tail's wait at least as
much as it did before.

Verification, all on macOS arm64 with the sanitized runner:

  * The mechanism was reproduced locally by delaying only the tail thread
    after its start gate, with production untouched. A 120 ms delay (the
    state exists, outside the observer's budget) and a 250 ms delay (the
    tail never enqueues) each produced exactly one failure,
    ASSERT(tail_queued) -- the CI signature.
  * After the rebuild the same injections are green at 120, 250, 400 and
    900 ms: an arbitrary scheduling delay no longer decides the verdict.
  * lock_registry 30/30 green plain and 30/30 green under four CPU hogs;
    16/16 inside the saturated 18-job parallel wave.
  * private_file_lock, lock_registry, daemon, project_lock and the
    daemon_* suites: 222 passed, 1 skipped.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-04 15:42:15 +02:00
Martin Vogel ffb5ebe9e8 Merge pull request #1721 from henry-hz/upstream-pr/elixir-call-arguments
fix(elixir): populate first_string_arg for Elixir calls
2026-09-04 15:38:50 +02:00
Martin Vogel 8e1de0403e Merge pull request #1709 from benrfairless/fix/ruby-superclass-base-classes
fix(extraction): keep Ruby superclass names bare
2026-09-04 15:36:08 +02:00
Martin Vogel 13b7d06701 Merge pull request #2047 from DeusData/fix/2046-cohort-conflict-retry-until-deadline
fix(daemon): retry a mismatched cohort holder until the caller's deadline
2026-09-04 15:35:39 +02:00
Martin Vogel 184a00a442 Merge pull request #2048 from DeusData/ci/msan-image-network-retry
ci(msan): retry transient network failures while building the MSan image
2026-09-04 15:35:23 +02:00
Martin Vogel f404dfbc7a ci(msan): retry transient network failures while building the MSan image
The test-msan lane dies whenever the buildx layer cache misses and the
builder's resolver hiccups. PR-CI runs 33816056229 and 33818108315 both
failed in "Build MSan image (cached layers)" with:

    #7 26.77   Could not resolve 'apt.llvm.org'
    #7 27.34 W: Failed to fetch http://apt.llvm.org/noble/dists/\
        llvm-toolchain-noble-22/InRelease  Could not resolve 'apt.llvm.org'
    ERROR: failed to build: failed to solve: process "/bin/sh -c apt-get \
        update && ..." did not complete successfully

The wget of the signing key from that SAME host had succeeded seconds
earlier in the same layer, so this is a transient DNS failure inside
buildkit, not a wrong URL or a dead mirror. 2 of the last 15 PR runs.

Every network-touching step in Dockerfile.msan now runs under a small
POSIX-sh helper written into the image before the first layer that needs
it: /usr/local/bin/retry makes 5 attempts with growing backoff
(5s/10s/20s/40s) and still exits non-zero once they are exhausted, so a
genuine breakage keeps failing the build instead of being swallowed.
Wrapped: both apt-get update/install pairs (also given
-o Acquire::Retries=3), the wget of the LLVM signing key, and the
llvm-project and zlib clones. Two shapes needed care:

- the key is fetched with `wget -O <file>` instead of `-qO- > <file>`,
  because a redirect is opened once for all attempts and a partial write
  from a failed attempt would be prepended to a later successful one;
- each clone clears its destination first, or a half-finished clone makes
  every later attempt fail on "destination path already exists".

The pinned base digest, the pinned llvmorg-22.1.0 / v1.3.1 tags,
--no-install-recommends and the layer structure are unchanged; on a
healthy network the image is byte-for-byte the same work as before.

Verified on Colima:
- `docker buildx build --check -f test-infrastructure/Dockerfile.msan
  test-infrastructure/` -> "Check complete, no warnings found."
- scratch image = FROM + helper + the real apt.llvm.org layer, built
  --no-cache: layer completes, clang-22/llvm-22 installed.
- scratch image where the wrapped command fails twice then succeeds:
  attempts 1 and 2 logged with 5s/10s backoff, attempt 3 succeeds,
  build exit 0.
- scratch image where the wrapped command always fails: 5 attempts,
  "retry: giving up after 5 attempts", retry_exit=1, build exit 1.

The full MSan image was deliberately not built locally (it compiles
libc++ with MSan); CI builds it on the branch.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-04 12:42:50 +02:00
Martin Vogel 8f64c36d85 Merge pull request #1918 from CaptainMittens/fix/return-star-respects-with-scope
fix(cypher): RETURN * must read the live scope, not the query pattern
2026-09-04 12:27:44 +02:00
Martin Vogel 54a434743e Merge pull request #1880 from CaptainMittens/fix/env-int-parse-strict
fix(env): stop an unreadable environment setting from picking a wrong default
2026-09-04 12:27:18 +02:00
Martin Vogel b95b33af86 Merge pull request #2029 from DeusData/ci/smoke-8al-node-pi-host-modules
smoke(8al-node): load the Pi extension with the modules the Pi host provides
2026-09-04 12:26:40 +02:00
Martin Vogel c803525258 Merge pull request #2028 from DeusData/ci/pin-llvm22-lsan-macos
ci(lsan-macos): pin the Homebrew LLVM toolchain to llvm@22
2026-09-04 12:26:09 +02:00
Martin Vogel 44fa3aeb21 Merge pull request #2026 from DeusData/fix/windows-guard-daemon-isolation
test(windows): isolate the daemon guards' rendezvous and stop hiding their failures
2026-09-04 12:25:34 +02:00
Martin Vogel dc703dad4a Merge branch 'main' into upstream-pr/elixir-call-arguments 2026-09-04 09:49:54 +02:00
Martin Vogel 2efb7a6323 Merge branch 'main' into fix/ruby-superclass-base-classes 2026-09-04 09:49:50 +02:00
Martin Vogel 3bbabf187c Merge pull request #1811 from pcristin/fix/issue-1682-dotted-ts-imports
fix(pipeline): preserve dotted TypeScript import basenames
2026-09-04 09:45:57 +02:00
Martin Vogel 9ed01a478c Merge pull request #2024 from DeusData/feat/cshtml-razor-pages
feat(extract): discover Razor Pages / MVC views (.cshtml) and route their @page
2026-09-04 09:45:53 +02:00
Martin Vogel 91a1723175 Merge pull request #1897 from CaptainMittens/fix/resolve-receiver-chain-mismatch
fix(registry): refuse a name-only match the receiver chain contradicts
2026-09-04 09:45:49 +02:00
Martin Vogel 9d0c6a1f1a Merge pull request #1881 from CaptainMittens/fix/span-duration-unreadable-timestamps
fix(traces): stop an unreadable span timestamp from becoming a real duration
2026-09-04 09:45:45 +02:00
Martin Vogel 9104feb67f fix(daemon): retry a mismatched cohort holder until the caller's deadline
A participant admitted to the version cohort holds the cohort lifetime lock
SH until its lease is released at exit. An internal daemon that has just
lost its last stdio client keeps that lock through its teardown, so a local
CLI (`config get`, `index_status`, ...) arriving in that few-hundred-ms
window met a holder whose cache root differed and was refused immediately
with "active account daemon uses a different cache directory" — a pure
lifecycle race, previously masked by the slow scalar self-hash and exposed
as soon as the hash got fast (#1767 pr-smoke on macOS).

cbm_version_cohort_acquire now retries a CONFLICT until the caller's finite
deadline_ms, holding no guard between attempts, exactly as host.c already
waits out the same handoff for the daemon claim marker. Every production
caller passes a finite deadline; UINT64_MAX keeps failing fast so a genuine
conflict against a long-lived peer is never waited on indefinitely.

Tests (deterministic, no timing assertions on transient windows): one proves
the retry runs until the deadline and a UINT64_MAX caller still fails
immediately; one proves a waiter that met the mismatched holder is admitted
the moment the holder releases. Both fail with the retry removed.

Fixes #2046

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-04 09:41:30 +02:00
Martin Vogel 2f9828d6de Merge pull request #1697 from angusgastle/docs/team-artifact-lfs-guidance
docs: recommend Git LFS for team-shared graph artifact
2026-09-03 22:09:06 +02:00
Martin Vogel f47cd47afe Merge branch 'main' into fix/issue-1682-dotted-ts-imports 2026-09-03 21:58:07 +02:00
Martin Vogel 380b1c5af1 Merge branch 'main' into feat/cshtml-razor-pages 2026-09-03 21:58:04 +02:00
Martin Vogel 429141261d Merge branch 'main' into fix/resolve-receiver-chain-mismatch 2026-09-03 21:57:58 +02:00
Martin Vogel 4d4af28672 Merge branch 'main' into fix/span-duration-unreadable-timestamps 2026-09-03 21:57:55 +02:00
Martin Vogel e63292a8d4 Merge branch 'main' into fix/env-int-parse-strict 2026-09-03 21:57:52 +02:00
Martin Vogel 74700067c4 Merge branch 'main' into fix/return-star-respects-with-scope 2026-09-03 21:57:49 +02:00
Martin Vogel 951e02e2f9 Merge pull request #2027 from DeusData/fix/posix-probe-counter-unused-but-set
fix(cli): drop the POSIX probe-counter store that Clang 23 rejects
2026-09-03 21:47:36 +02:00
Martin Vogel be66a2d2c6 smoke(8al-node): load the Pi extension with the modules the Pi host provides
The 8al-node probe imports the generated Pi extension in bare Node with an
empty node_modules. That was a sound stand-in for the Pi host only while
the extension imported nothing but node: builtins; the first package
import (`typebox`, Pi's schema library for tool parameters — a real Pi
dependency, verified against pi-coding-agent 0.84.4 on #1809) can never
resolve there, so the probe fails on a correct extension and would pass a
wrong one just as readily.

Give the probe what the host gives the extension: a Proxy-backed
`typebox` stub in the probe's node_modules (any Type.X(...) returns a
descriptor, so the probe never tracks which helpers the emitter uses — it
only exercises the tool lifecycle), plus an explicit allowlist: every
import specifier in the generated file must be a node: builtin or a
package on PI_HOST_PACKAGES, otherwise the probe fails naming the
specifier. That turns the guarantee the empty node_modules gave by
accident (no surprise dependency) into an asserted contract.

Verified locally by running only the 8al-node block against extensions
generated by two binaries:
  main@5802ccdd extension, old probe: OK; new probe: OK
  #1809 extension, old probe: ERR_MODULE_NOT_FOUND typebox (the CI red)
  #1809 extension, new probe: OK
  #1809 extension + an injected 'lodash' import, new probe:
    FAIL 8al-node: ... packages the Pi host does not provide: lodash

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-03 16:16:51 +02:00
Martin Vogel 1d38be3331 ci(lsan-macos): pin the Homebrew LLVM toolchain to llvm@22
The test-lsan-macos leg installs the unversioned `llvm` formula, which
tracks Homebrew's current stable. That moved from 22.1.8 to 23.1.0 today,
so the leg's compiler now depends on which bottle the runner image draws:
run 33745916176 (PR #1811) poured 23.1.0 and died at compile on a new
Clang 23 diagnostic (-Wunused-but-set-global under -Werror); the run an
hour earlier poured 22.1.8 and built fine. Same code, two verdicts.

Pin the formula (and the `brew --prefix` lookup) to llvm@22 — the version
every green run of this leg has used so far. Moving to a newer major
becomes a deliberate edit of these two lines instead of an accident of
the runner image.

The diagnostic itself is fixed independently in #2027 so main compiles
under both majors; this change is about determinism of the leg, not the
warning.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-03 16:07:42 +02:00
Martin Vogel 0ac290e2e2 fix(cli): drop the POSIX probe-counter store that Clang 23 rejects
`g_mcp_command_path_probe_counter` is only ever read inside
`cbm_json_mcp_probe_command_path`, which is compiled for Windows alone.
On POSIX test builds the setter stored a pointer nothing read, and Clang
23 (Homebrew LLVM 23.1.0, which `brew install llvm` now pours on the
macOS LSan leg) flags that as `-Wunused-but-set-global`; with `-Werror`
the whole test-runner build dies at `src/cli/cli.c:1768:21`.

Keep the counter where a probe can happen (Windows) and make the POSIX
setter an explicit no-op with the reason spelled out. The three tests
that install a counter keep asserting it stays at zero on every platform,
which on POSIX is exactly what "no classifier" means.

Verified in the failing arena: `origin/main` `src/cli/cli.c` fails under
clang 23.1.1 with the CI diagnostic; with this change it compiles clean,
as do all 105 built `src` TUs and all 155 test + extraction TUs under the
same flags. `cli` suite: 292 passed with Homebrew LLVM 22.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-03 15:48:59 +02:00
Martin Vogel 5802ccdd77 Merge pull request #1896 from CaptainMittens/fix/swift-call-arguments
fix(extraction): reach Swift call arguments through call_suffix
2026-09-03 15:12:27 +02:00
Martin Vogel 73f4a432e4 test(windows): isolate the daemon guards' rendezvous and stop hiding their failures
Every Windows guard shared ONE daemon. test_daemon_stability.py said each
section "runs under its OWN cache directory: daemon coordination is
cache-scoped" -- it is not. The rendezvous key is FNV-1a over a compile-time
domain string (service.c: cbm_daemon_rendezvous_key) under a machine-global
parent (win_default_runtime_parent -> SHGetFolderPathW(CSIDL_LOCAL_APPDATA),
which ignores the LOCALAPPDATA variable). So the guard's own `daemon stop`
and kill -9 sections, the sibling guards, and anything else on the runner
all met the same daemon, and a daemon wedged anywhere failed every check
after it. That is the shape of this week's test-windows-guards red on
#1874: lifecycle's `daemon start` connected, the UI-config handshake never
answered, and stability / hook_augment / ui_drive / cli_non_ascii went red
in a row with an orphan daemon killed at cleanup -- on a diff and a base that
were each green elsewhere.

CBM_RUNTIME_DIR is the only relocation hook (bootstrap.c, resolved in
cbm_daemon_bootstrap_endpoint_new for every product endpoint). Each stability
section now gets a private runtime parent next to its cache, created before
the binary runs -- the endpoint validates the parent and never creates it --
and the daemon inherits it from the client that spawns it. Proven on macOS:
a daemon on the default rendezvous keeps its pid across the whole guard
(three stops and a kill -9 inside it).

The reporting was hiding the cause as well:
* every SETUP FAIL printed nothing at all, and every RED printed a 300-400
  character HEAD slice, which is the allocator preamble, never the error.
  Excerpts now show the tail (1500 chars); setup paths print the daemon's
  full output.
* a subprocess timeout raised on a parallel-client worker thread was lost
  and the client reported as "rc=none" with no output; it is now kept as a
  failed result with its partial output. A section-level timeout is a
  named RED with the command and output, not a bare traceback.
* the MCP session's stderr went to DEVNULL; it now goes to a file that is
  printed when initialize never answers -- and that case is a RED against a
  running daemon, not a "setup" skip.
* exit 2 was documented and never produced. Only `daemon start` failing
  raises it now (test_daemon_lifecycle.py owns that surface as a
  regression, so the board loses nothing); main() runs every section and
  names all of the failing ones.
* test_hook_augment.py / test_cli_non_ascii_arg.py print full stdout AND
  stderr on their index-setup failures instead of 300 chars of stdout.

On POSIX (local iteration only) the namespace lives under /tmp: sun_path
caps the socket at 104 bytes on macOS and TMPDIR there is ~50 bytes deep.

Out of scope, noted: test_daemon_lifecycle.py, test_non_ascii_path.py and
test_ui_drive_listing.py still use the machine-global rendezvous; a too-long
CBM_RUNTIME_DIR on POSIX is refused (ipc.c sun_path check) with no
validation detail.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-03 14:56:42 +02:00