77 Commits
Author SHA1 Message Date
Martin Vogel 35b50b9ca0 Merge branch 'main' into dependabot/github_actions/actions/download-artifact-8.0.2 2026-11-03 04:28:16 +01:00
Martin Vogel c03e99c54a Merge branch 'main' into dependabot/github_actions/actions/download-artifact-8.0.2 2026-11-02 18:25:22 +01:00
Martin Vogel b076877680 Merge branch 'main' into dependabot/github_actions/actions/upload-artifact-7.0.2 2026-11-02 18:25:19 +01:00
Martin Vogel 3f12a3f24b Merge branch 'main' into dependabot/github_actions/actions/upload-artifact-7.0.2 2026-11-02 01:44:30 +01:00
Martin Vogel df92904bcb Merge branch 'main' into dependabot/github_actions/actions/download-artifact-8.0.2 2026-11-01 23:20:26 +01:00
Martin Vogel c04e322d61 Merge branch 'main' into dependabot/github_actions/actions/setup-python-7.1.0 2026-11-01 23:20:24 +01:00
dependabot[bot] 1efa00eee3 build(deps): bump actions/upload-artifact from 7.0.1 to 7.0.2
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 7.0.1 to 7.0.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/043fb46d1a93c77aae656e7c1c64a875d1fc6a0a...cf430e030ddbb5b0abf93d22962f4752f3646cd9)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-01 19:21:49 +00:00
dependabot[bot] 11f20c578b build(deps): bump actions/setup-python from 7.0.0 to 7.1.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/5fda3b95a4ea91299a34e894583c3862153e4b97...6d4fd22b20465ccc0f7354476588e8df7c9ca0e0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-01 18:34:44 +00:00
dependabot[bot] f683b85667 build(deps): bump actions/download-artifact from 8.0.1 to 8.0.2
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 8.0.1 to 8.0.2.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c...9000827ccba6bdab643e8b6fd33ac0654aef8333)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-01 18:34:34 +00:00
dependabot[bot] 1f56c7d6bb build(deps): bump msys2/setup-msys2 from 2.32.0 to 2.33.0
Bumps [msys2/setup-msys2](https://github.com/msys2/setup-msys2) from 2.32.0 to 2.33.0.
- [Release notes](https://github.com/msys2/setup-msys2/releases)
- [Changelog](https://github.com/msys2/setup-msys2/blob/main/CHANGELOG.md)
- [Commits](https://github.com/msys2/setup-msys2/compare/66cd2cce69caa17b53920067426061ca1de3a884...ec48f7c5447b3140e2b088413ae3a55687bccb6e)

---
updated-dependencies:
- dependency-name: msys2/setup-msys2
  dependency-version: 2.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-01 16:06:58 +00:00
Martin Vogel e3a9fa34ed ci: let apt fail over between mirrors instead of hanging
The hosted Ubuntu x64 runners read the archive through
mirror+file:/etc/apt/apt-mirrors.txt. With Acquire::Retries above 0
(including the apt default of 3), a failover from a dead first entry
of that list (connection refused, DNS failure or a stalled host) to
the next entry goes idle: apt-get fetches a few InRelease files from
the second mirror and then waits until the step timeout. With
Acquire::Retries=0 the same failover completes. Seen on noble
(apt 2.8.3) and jammy (apt 2.4.14).

Every apt step this branch hardened now picks the retry count at
runtime, applied to both update and install: 0 when the runner has the
mirror list (moving to the next mirror is the retry), 3 otherwise
(arm64 runners and any host with plain sources keep their retries).
--error-on=any, the http and https timeouts and the step timeouts are
unchanged.

Proof: the step text extracted from the edited _build.yml (update plus
install of zlib1g-dev), run under bash -e in ubuntu:24.04 and
ubuntu:22.04 containers with runner-shaped mirror lists:
- first mirror refused: exit 0 in 8 s / 9 s, every index and deb
  fetched from the second mirror
- first mirror stalled (192.0.2.1): exit 0 in 68 s / 69 s, one 30 s
  timeout each for update and install, then the second mirror
- healthy first mirror: exit 0 in 7 s / 9 s
- no mirror list, plain sources: the prefix keeps Retries=3, exit 0
  in 8 s / 7 s
- every mirror dead: update exits 100 in under 1 s, install skipped
- revert check, the previous step text with a dead first mirror
  (refused or stalled): idle with no Packages index at the 180 s cap
  on both releases

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-11-01 12:32:45 +01:00
Martin Vogel a545a0aa8c ci: fail apt-get update on any fetch error
apt-get update exits 0 when index fetches fail: it only prints
"W: Some index files failed to download. They have been ignored, or
old ones used instead." and the job breaks later at install time with
a misleading "Unable to locate package", or installs from stale lists.
Add --error-on=any to every apt-get update this branch hardens, placed
after the -o options as in test-infrastructure/Dockerfile.msan, so the
update step itself fails when an index cannot be fetched.

Proof in ubuntu:24.04 (apt 2.8.3) and ubuntu:22.04 (apt 2.4.14)
containers with a runner-shaped mirror+file:/etc/apt/apt-mirrors.txt
source and the same -o options CI passes:
- every mirror unreachable: exit 100 with the flag (E: lines), exit 0
  without it (W: lines, zero Packages lists), on both apt versions
- first mirror refused or stalled (30 s connect timeout) and the real
  archive second: the Ign: failover lines do not trip the flag, exit 0
  with every index fetched (measured with Acquire::Retries=0)
- the image sources with the flag: exit 0 and no Ign/W/E lines, so the
  option is understood by apt 2.4 and 2.8
- green hosted-runner logs (ubuntu-24.04 and ubuntu-22.04 legs) show no
  Ign/Err/W/E lines in the update step

With Acquire::Retries=3 a mirror failover leaves apt idle after the
InRelease failover, with or without the flag and also for a plain
apt-get update; that apt behaviour predates this change and is
unchanged by it.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-11-01 10:08:28 +01:00
Martin Vogel 339cd51660 ci: fail fast when an apt mirror stalls
On 2026-11-01 two Linux jobs hung in `sudo apt-get update` right after
"Get:5 https://archive.ubuntu.com/ubuntu noble-security InRelease" until
their job limit; the build never started in either:

- PR 2641, test-tsan x86 (run 41251045506, job 124688326533): the
  "Install deps (Ubuntu)" step ran 2 h until the 120-minute job limit.
- PR 2395, test-unix gcc x86 (run 41256855177, job 124705168264): the
  same step ran 4 h until the 240-minute job limit.

Both logs show the azure mirror ignored after about 15 s, the fallback to
archive.ubuntu.com, and then no output until the cancel. The default apt
http timeout (120 s) did not end the stall.

Rule applied to every workflow step whose run block calls apt:

- A step-level timeout-minutes. 10 for steps that only provision packages
  (green runs on 2026-11-01 and the last green dry run: 5 to 76 s); 20 for
  the three pr.yml steps that also run scripts/build.sh (green: 246 to
  331 s). A stalled mirror now fails the step in minutes instead of
  holding the runner until the job limit.
- `-o Acquire::Retries=3 -o Acquire::http::Timeout=30
  -o Acquire::https::Timeout=30` on every apt-get update and install,
  with the -o options before the subcommand as in
  test-infrastructure/Dockerfile.msan.

Nothing else changes: no action, runner, package or step-order changes.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-11-01 09:23:09 +01:00
Martin Vogel 9bb891b669 ci: move macOS runners to macos-15 and pin the deployment target to 14.0
GitHub retires the macos-14 runner image on 2026-11-02, and its brownouts
already cancel our macos-14 jobs. Move every macos-14 runner reference to
macos-15 (arm64): the release darwin-arm64 build, the unix-macos14 and
tsan-mac test legs, the smoke-mac PR leg (its two matrix.os guards move in
lockstep), the release smoke, both soak legs, the memwaste event lane, the
bug-repro board, the cache warmer and the all-platform smoke. Lane names
stay unchanged: select-lanes.sh, ci-ok and the lane contracts key on them,
never on the runner label.

Two broad legs already ran on macos-15 (_test.yml BROAD_UNIX, _smoke.yml
BROAD_UNIX) and smoke.yml listed both images, so the moved legs would have
duplicated them. Those duplicates are dropped; PR CI keeps exactly the
same legs.

Without an explicit minimum, clang stamps the binary with the SDK's
default (LC_BUILD_VERSION minos), so a release built on macos-15 would
refuse to start on macOS 14. Makefile.cbm now folds
-mmacosx-version-min=14.0 into CC/CXX next to ARCHFLAGS for Apple targets
(detected from the compiler's target, not the host), so it reaches every
compile and link, vendored objects included, on every build path.

Proof on macOS 26.6 / SDK 26.5: the production binary built from
origin/main reports minos 26.0; with this change it reports minos 14.0,
with zero warnings under -Werror, no weak imports and an identical
undefined-symbol set. The test runner (direct make, no env.sh) is minos
14.0 too; platform and daemon_ipc suites pass.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-30 23:44:25 +01:00
Martin Vogel 7f6f567840 wip: Smart CI narrows PR CI for real (pre-rebase checkpoint)
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-17 14:48:52 +02:00
Martin Vogel 7644e8bae1 ci: upload failing suite logs when a test job fails
The job log of a red test job carries a summary of each failing suite,
not its log, and the only artifact was shard-manifest.txt. A failure
whose summary was not enough could only be understood by reproducing it.

The four jobs in .github/workflows/_test.yml that run the parallel
harness (test-unix, test-windows, test-diag, test-lsan-macos) get one
more step, "Failing suite logs". It runs only when the job failed and
uploads build/c/test-logs/failed/: the logs of the suites that did not
end green, plus results.txt, as the harness leaves them there. One red
suite is typically a few kilobytes; a whole run's logs are about 25 MB.
Retention is 7 days. The artifact names follow the shard-manifest ones
(suite-logs-<os>-<compiler or msystem>-<job index>, suite-logs-diag,
suite-logs-lsan-macos), and the step uses the upload-artifact reference
the file already pins.

A job that failed outside the suite waves has no such directory; the
step then warns and uploads nothing. The step cannot change a job's
result: it is skipped on a green job, and a failed job stays failed
whatever the upload does. Triggers, permissions, job dependencies,
timeouts, the matrix and the shard-completeness download pattern are
unchanged.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-12 20:18:19 +02:00
Martin Vogel 4d9592b5fb Merge pull request #2068 from DeusData/feat/codeatlas-web
CBM Lens Beta: graph exploration and architecture workspace
2026-10-12 05:38:30 +02:00
Martin Vogel dbc6ac486b fix(msan): reports print with fast unwinding; five suites run again
Five suites (grammar_regression grammar_labels pipeline lang_contract
grammar_probe_e) were excluded from the MSan lane for "stack-overflow
under instrumentation". That was never deep recursion. MSan aborted while
REPORTING real uninitialized reads:

- every abort was on the main thread. MSan's "T<n>" is the OS thread id
  and equalled the pid in all five x86-64 aborts (CI job 91868259558);
  the old block read it as a worker-thread ordinal, so the worker-stack
  knob, MSAN_ORIGINS and CBM_WORKERS=1 could never change anything;
- with the default slow unwinder (the image's MSan-instrumented
  libunwind) a report exhausted the main thread's 256 MiB stack, and the
  overflow handler then failed with "nested bug in the same thread";
- with fast_unwind_on_fatal=1 the same test prints the
  use-of-uninitialized-value report instead (measured on one tree and
  test: slow unwind aborts at the stack limit, fast unwind reports
  wit/scanner.c:13).

The two reads are fixed in the previous commits of this series, so:

- MSAN_OPTIONS defaults to fast_unwind_on_fatal=1 (the build already
  keeps frame pointers);
- MSAN_EXCLUDE is down to cli, whose install-path failure is a separate,
  still-undiagnosed cause;
- the known-red block, the header (it still claimed CI runs without
  exclusions) and the MSAN_ORIGINS and stack comments say what is true;
- the partial-lane warning and the exclude-name typo check lived in a
  branch behind `elif true` that never ran. They now run in the
  per-suite path, the check as a case match instead of a grep -q pipe
  that pipefail can misreport;
- the comments in scripts/ci/msan-lane.sh and above the test-msan job
  in _test.yml no longer say the five suites overflow.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-11 18:26:26 +02:00
Martin Vogel 0392068d49 Merge main into CBM Lens Beta
Reconcile the complete Atlas and Lens stream with current main while retaining both histories. Preserve architecture functionality, current process and watcher safeguards, test infrastructure, and dependency security updates. Add coverage for Git-environment isolation combined with child-local overrides.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-11 16:57:48 +02:00
Martin Vogel 867e5f883d Merge origin/main into the smart-CI branch
Ten files changed on both sides; every conflict was a union of independent
additions:

- workflows (_lint.yml, _test.yml): keep the branch's lane `if:` and main's
  pinned ubuntu-24.04 runner with its reason.
- internal/cbm/cbm.h: main's callable-identity and module-doc fields plus the
  branch's configured-test fields.
- Makefile.cbm: both new test files; prod_lsp_all.o keeps main's
  .build-config dependency and the branch's coverage flags.
- scripts/test.sh: main's setup-scripts contract becomes Step 0y3, ahead of
  the branch's 0z series and the contracts-only exit.
- src/foundation/subprocess.c: main's cbm_posix_close_fds_from (it closes the
  error descriptor too).
- src/mcp/mcp.c: main's repository default base for detect_changes, with the
  early returns released through the branch's detect_release.
- src/pipeline/lsp_resolve.h, tests/test_main.c: both includes, suites and
  start-up steps (main's git environment reset first).
- scripts/memory-core-baseline.txt: recounted on the merged tree and lowered
  (mcp.c 773, config_toml_edit.c 73).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-05 18:53:37 +02:00
Martin Vogel 776bb119ac ci(test-impact): team artifact producer, PR shadow and the PR-CI narrowing switch
Producer. test-impact-artifact.yml publishes, on every push to main, the
commit's team artifact bundle: its frozen graph, built incrementally from the
previous bundle, and its per-test coverage map, re-run only for the suites
the push selects (scripts/ci/test-impact-publish.sh). A weekly run rebuilds
both from scratch and fails when the incremental graph's topology differs
from the full one. Bundles are Actions artifacts kept 30 days, never
committed.

Shadow. The report-only test-impact-shadow job also builds the checkout and
runs `test-impact select` against the base side of the PR merge commit
(scripts/ci/test-impact-select.sh). A bundle is used only when it comes from
a successful main-branch producer run for exactly that commit
(scripts/ci/test-impact-bundle.sh). The job stays continue-on-error and
outside ci-ok.

Narrowing switch, PR CI only. While the repository variable
TEST_IMPACT_MODE is `gate`, a select-tests job turns the engine's answer into
the tests the test-unix and test-windows legs run. Unset, nothing changes.
It fails open by construction:
- scripts/test-impact/selection.py is the only place an answer becomes a
  runner filter. Only a well-formed selected lane whose filter matches its
  own suite list narrows; run-all answers, a lane that selects nothing,
  unknown schemas or tokens, and any crash run everything.
- `test` passes a selection only from a successful, narrowed select-tests
  run; ci-ok does not need select-tests. A broken selection costs minutes,
  never coverage.
- scripts/run-tests-parallel.sh launches only the selected suites and keeps
  every guard on the selected list. A selected suite the build does not
  register fails the run, unless the engine saw its registration as
  conditional; then it is dropped with a notice, exactly as a full run of
  that build never runs it. Each runner judges its own tokens, so a selected
  test that does not exist fails its suite.
- The shard manifest records the selection's digest, and
  verify-shard-union.sh requires every shard of a leg to agree on it.
- Dry runs and releases never pass a selection (lane-wiring contract).

Also fixed on the way:
- publish crashed on any run-all push: a run-all lane carries
  "suites": null.
- the git-facts hostile-diff probe re-ran the runner with the parent's
  CBM_TEST_ONLY_FILE still set; under a narrowed run naming its suite it
  re-ran the spawning test, which spawned again. The child now clears it
  (a new test fails without that).
- the select summary crashed on the same null suites.

Contracts: tests/test_test_impact_selection.sh (Step 0z8),
tests/test_harness_selection.sh (Step 2c, the real runner through the real
harness), and new cases in the lane-gate and lane-wiring contracts.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-05 18:52:31 +02:00
Martin Vogel ae5bb0bdea ci: PR shard profile (x86 and Windows whole, arm64 x2)
_test.yml takes a shard_profile input: "standard" (the default; dry run and
release pass nothing and keep ubuntu x3 / windows x2, byte-identical
matrices) or "pr", which pr.yml now uses: ubuntu x86 and Windows run as one
job each and only arm64, the slowest leg, splits in two. A PR runs few legs
and every shard re-pays its setup, so fewer, longer jobs cost less. An
unknown profile is an error. The wiring contract pins both the default and
PR's use of the profile.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-30 23:30:57 +02:00
Martin Vogel adec281d1a ci: run only the selected lanes on PRs; dry run and release unchanged
pr.yml's `changes` job now lists the changed files (renames with their old
path too), runs scripts/ci/select-lanes.sh in a credential-less step and
outputs tier, full, lanes and the pr-smoke matrix. security, lint and test
pass the lanes to their reusable workflows, which take a `lanes` input
defaulting to "all": dry-run.yml and release.yml pass none and run exactly
as before (setup-matrix output for every broad/shard combination is
byte-identical; the TSan matrix moved into setup-matrix with the same legs).

Under a selection, setup-matrix keeps only the tagged legs of the selected
lanes (an untagged leg is an error, never a silent drop) and each lane job
has an `if:` on its lane. pr-smoke and memwaste run on their lanes instead of
the old product regex. Docs-only PRs (T0a) get a `contracts` job running the
new `scripts/test.sh --contracts-only` (Step 0 contracts, no build). ci-ok
needs contracts, lists the run's jobs (actions: read) and passes LANES/JOBS
to require-all-green.sh; shard-completeness passes --lanes.

tests/test_lane_wiring_contract.sh (Step 0z4) pins it: dry run and release
pass no lanes and the inputs default to all, pr.yml wires the selection
through, and every lane is served by a tagged leg or an `if:` naming it.
The smoke-fixture contract now checks the selector's decisions for the
smoke-harness paths instead of the removed regex; select-lanes.sh joins the
venue-parity interface probes.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-30 23:21:43 +02:00
Martin VogelandRares Popa a45a99c9c7 ci: pin the noble-dependent runners, and attest the SBOM without the wrapper
Distilled from #2109 by Rares Popa. Two slices only; the rest of that PR is
either frontend work the UI rebuild makes redundant, or a gating change that
needs re-siting first.

1. Three jobs move from ubuntu-latest to ubuntu-24.04

   _lint.yml `lint` and `lint-mem`, and _test.yml `test-diag`, each install a
   toolchain from an apt.llvm.org repo whose name contains the release:

       deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main
       deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main

   noble IS 24.04. So these jobs already depend on a specific release while
   asking for whatever ubuntu-latest happens to mean that morning. The day
   GitHub advances ubuntu-latest, all three break at once, on a schedule
   nobody here chose, and the failure surfaces as an apt error far from its
   cause. Pinning the runner to the release the repo line already names costs
   nothing and removes the trapdoor.

2. actions/attest-sbom -> actions/attest

   attest-sbom is deprecated. Its remaining behaviour is to print
   "::warning::actions/attest-sbom has been deprecated" and forward to
   actions/attest with NODE_OPTIONS set, passing sbom-path through unchanged.
   Calling attest directly yields the same attestation minus the wrapper.

   Verified rather than assumed, because this sits on the release path:
   59d89421af93a897026c735860bf21b6eb4f7b26 is exactly tag v4.1.0 of
   actions/attest, and sbom-path is a real input on that action at that SHA.

   The step keeps no continue-on-error, so a failed SBOM attestation still
   fails the release. That is the intended behaviour and the comment now says
   so: an unattested SBOM is worse than an absent one, because the artifact
   ships looking complete.

Deliberately NOT taken from #2109, each for its own reason:

  - the actionlint job, as sited. _lint.yml is consumed by pr.yml, dry-run.yml
    and release.yml, pr.yml gates its whole test stage on `if lint succeeded`,
    and release.yml gates build on needs.lint.result == 'success'. A job there
    is merge-blocking and release-blocking with no branch-protection edit to
    make it visible, and this one curls a tarball from github.com releases with
    no cache and no fallback. actionlint is worth having; not on that path, and
    not before it has been run against current main.
  - the test-package-wrappers node matrix. It reruns the Go and PyPI suites a
    second time per OS to vary a Node version only the npm leg reads.
  - everything frontend: the test-frontend job, .node-version, and the
    dependabot graph-ui block. The UI is being rebuilt from the ground up, so
    gating on the current one buys coverage of a surface that is going away.

Co-authored-by: Rares Popa <2606875+rarepops@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-27 23:47:35 +02:00
Martin Vogel ac8f5b8a81 ci(test): report whether unprivileged userns is actually available
The sysctl that lifts the AppArmor restriction is best-effort, and the test
runner prints per-shard aggregates only -- 'N passed, M skipped', never
individual test names. So if the sysctl stopped working, the #1830 namespace
test would quietly return to skipping and the log would stay green and look
identical. That is the same false assurance that let a security relaxation ship
behind a test which never executed.

Probe it the way the test does (unprivileged, no sudo) and print the answer, so
there is one greppable line of evidence per ubuntu leg. Deliberately not a gate:
a runner that cannot lift the restriction should still run everything else.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-13 10:35:41 +02:00
Martin Vogel d21e6cc762 fix(daemon,ci): stop caching the overflow uid, and run the userns guard on the core matrix
Three maintainer decisions from the challenger read on the #1830 unit.

1. DELETE THE CACHE (not just fix its comment).

   posix_ancestor_overflow_uid() memoised via pthread_once behind a comment
   claiming "immutable /proc state". Both halves were false: unshare(CLONE_NEWUSER)
   rewrites /proc/self/uid_map, and pthread_once state survives fork() already
   marked done. A process that forked and then entered a namespace kept the
   parent verdict and refused a directory it should have accepted.

   It was harmless only because every caller today runs in a freshly exec'd
   process -- the daemon double-fork execs, workers go through
   cbm_subprocess_spawn's fork+exec, worker_pool.c never forks, and src/ contains
   no setns or unshare at all. That made a security decision depend on an
   invariant nothing enforced. The next fork-without-exec caller would have
   inherited a stale verdict silently.

   Now derived fresh per call: two small /proc reads against an openat + fstat +
   fchmod + ACL check per path component in the same walk.

   With a test that would have caught the original bug and needs no namespace.
   daemon_ipc_posix_overflow_uid_is_never_cached_issue1830 counts real
   derivations through the production entry point and asserts the count rises on
   every ancestor check. Re-introducing a cache fails it by name on every
   platform. Proven both ways on linux/arm64 as uid 1001: cache re-introduced
   49 passed 1 failed; removed 50 passed 0 skipped.

   Worth knowing why that test is not redundant: with the re-exec probe in
   place, the userns smoke test PASSES even with the cache restored, because the
   probe is a fresh process and immune to it by construction. The two tests are
   complementary -- the smoke test binds the namespace wiring, this one binds
   the absence of the cache.

2. RUN THE GUARD ON THE CORE MATRIX.

   Ubuntu 23.10+ ships kernel.apparmor_restrict_unprivileged_userns=1, so the
   #1830 smoke test skipped on every 24.04 leg and its only real-namespace
   coverage sat on the two ubuntu-22.04 legs. Those are broad-matrix only, so
   the test ran in NO pull request, and GitHub retires those images on
   2027-04-17 -- after which it would have become a permanent silent skip, and a
   skip is not a red. A deliberate relaxation of a security check was guarded by
   a test that ran nowhere a change would be reviewed, on a clock.

   _test.yml now lifts the restriction with one sysctl on every ubuntu leg
   (hosted runners have passwordless sudo), best-effort so a runner that refuses
   simply returns to skipping. The O10 skip-whitelist comment no longer presents
   the AppArmor restriction as immovable.

3. KEEP THE TOLERANCE, CORRECT THE SAFETY ARGUMENT.

   The comment claimed that in a single-uid map "nobody reachable can have
   created it or can mutate it". The overflow uid is what EVERY unmapped host
   uid maps to, not only host root (user_namespaces(7)), so overflow-owned does
   not prove root-created: on a shared host another local user's sticky
   directory is indistinguishable from root-owned /tmp from inside the namespace.

   The tolerance stays, because there is no in-namespace discriminator that
   could tell them apart -- and narrowing it would refuse the legitimate
   root-owned 01777 /tmp that #1830 exists to accept, i.e. it would revert the
   fix in practice. What changes is the claim: the real guarantee is that no
   principal reachable from inside the namespace can create or mutate such an
   ancestor, and the leaf is never tolerated as overflow (0700, euid-rechecked),
   so a hostile host-side ancestor owner is bounded to denial of service and
   socket-path control.

macOS daemon_ipc 53 passed 2 skipped; linux/arm64 50 passed 0 skipped;
lint-ci clean.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-13 10:21:16 +02:00
Martin VogelandClaude Fable 5.1 557cb01028 ci: repin the apt.llvm.org ASan + analyzer lanes to clang-21
apt.llvm.org rotates its per-major noble snapshot channels; the
llvm-toolchain-noble-22 channel transiently emptied, breaking every lane that
installs clang from it. #2124 already moved Dockerfile.msan (clang-22 ->
clang-21) for the same reason. The two GitHub-hosted lanes that install from
apt.llvm.org were still on 22 and would break identically on the next
rotation: _test.yml test-diag (ASan+UBSan) and _lint.yml lint-mem
(clang-analyzer, clang-tidy). Repin both to clang-21 / clang-tidy-21 /
llvm-toolchain-noble-21 (the channel #2124 verified apt.llvm.org serves on
amd64+arm64). The macOS LSan lane's Homebrew llvm@22 pin is intentional and
unrelated to apt churn (left unchanged); the clang-format-20 CI-lint lane is
untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-09 21:05:29 +02:00
Martin Vogel 7cd5c93136 Merge feat/atlas-r1 (current with main) into feat/codeatlas-web
Re-stacks the CodeAtlasWeb branch on the updated base (feat/atlas-r1 at
a44732b6, which merges main 7b0f553c). Ten conflicts, all in the seam
between the previous frontend and its replacement:

- README.md: main's surface count (45) with this branch's Atlas line.
- graph-ui/package-lock.json, graph-ui/src/App.tsx: CodeAtlasWeb's own
  files stay; the previous frontend's edits to the same paths do not
  apply.
- Seven previous-frontend files main had modified (GraphTab,
  NodeDetailPanel and its test, StatsTab test, useProjects, rationale
  and its test) stay deleted, and two tests main added for that
  frontend (App.test.tsx, useProjects.test.tsx) are not brought in;
  their subjects no longer exist in this tree.

Nothing under graph-ui changes relative to this branch; the C side
takes main's lean-output work and the branch's merge resolution.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-06 03:23:48 +02:00
Martin Vogel 453d6cc9bc ci(ui): the CodeAtlasWeb leg is one canonical script, here and in CI
tests/test_venue_parity_contract.sh holds every venue to one harness: a
workflow step may provision, move artifacts, or call a canonical leg
entry, and the product-exercising commands live inside scripts/. The
test-ui job ran npm inline and tripped it on the local ladder.

scripts/ci/test-ui.sh is now the whole leg (install without a browser
download, vitest, the style and promise scans, the portable frozen
acceptance checks, the production build); the job calls it, and running
the same script locally is the same leg. Contract green again
(22 workflows marker-checked, 10 walked).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-05 20:43:13 +02:00
Martin Vogel f490238120 ci(ui): test-ui lane gates CodeAtlasWeb; docs describe the new Atlas
- .github/workflows/_test.yml: a `test-ui` job (ubuntu, node 22) runs
  the frontend's vitest suite, its hardcoded-string and promise scans,
  the portable frozen acceptance checks and a production build, with
  the same browser-download skip the product build uses. The browser
  proofs (tools/smoke-*.mjs) need a running server and Playwright
  browsers and stay a local, manually dispatched venue.
- graph-ui/package.json: `test:acceptance` runs every frozen check except
  the two release-binding files, which tie the recorded release report
  to the commits of the frontend's original repository and cannot pass
  anywhere else (203 checks pass here; the full `npm test` fails exactly
  those four assertions).
- README.md: the Atlas section describes CodeAtlasWeb, credits
  Bernhard Jackiewicz and points at #1964.
- graph-ui/README.md: a maintainers' preface on how this repository
  builds, embeds, proxies and gates the frontend, and what it added;
  his README follows unchanged.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-09-05 20:41:18 +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 9099bc383f fix(ci): install zip for Windows package contract
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-08 23:58:58 +02:00
Martin Vogel 8018561cfe fix(release): externalize runtime assets and harden VT verification
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-08 17:35:05 +02:00
dependabot[bot] e0bf963910 build(deps): bump actions/checkout from 7.0.0 to 7.0.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-04 09:58:51 +00:00
Martin Vogel d6c8d1dd0a fix(msan): split the known-red block by cause; fix the RSS one properly
The x86-64 leg runs this lane without exclusions on purpose, to settle
which limits are architectural. It has now run, and it disproves part of
what the previous block asserted. That block claimed all seven excluded
suites "abort with stack-overflow". Five do. Two do not, and lumping
them together hid two different problems behind one rationale.

  (A) stack-overflow, five suites: grammar_regression grammar_labels
      pipeline lang_contract grammar_probe_e. Confirmed on BOTH arm64 and
      x86-64 (CI logged 5), so it is not the aarch64 artifact an earlier
      note claimed. The recursion guards bound DEPTH while the resource
      exhausted is BYTES; that follow-up stands unchanged.

  (B) cli: no overflow at all. On x86-64 it runs to completion, 253
      passed / 5 failed, every failure in the install or activation path,
      with "agent_config agent=OpenClaw op=mcp_install" above them. Green
      on every other venue. MSan reported zero use-of-uninitialized-value
      in it, so the exclusion costs no uninit coverage. Recorded as
      undiagnosed rather than guessed at: the local lane is arm64 where
      these suites hit (A) before reaching this code, so there is no
      faithful venue to iterate in and each attempt is a ~30min round
      trip. That is a follow-up with an owner, not a dismissal.

  (C) incremental: an RSS BUDGET failure, 3054MB against a 2304MB limit
      -- not an overflow either. MSan maps shadow (and origin) memory for
      every allocation, so the budget cannot separate a leak from shadow.
      FIXED rather than excluded: the assertion is now skipped under
      __has_feature(memory_sanitizer) only, so the guard keeps its teeth
      on every other platform, where inflating the budget would have
      blinded it. The suite stays IN the lane.

Verified: with (C) fixed, incremental is 163 passed / 0 failed and ZERO
stack-overflows under the local arm64 MSan container -- so it never
belonged in the overflow list on either architecture.

msan-lane.sh no longer forces MSAN_EXCLUDE empty. That override existed
to ask the architectural question; it is answered, and keeping it would
re-red the gate for causes already recorded. Both venues now read the one
authoritative list in scripts/msan.sh, which still warns loudly that the
lane is partial.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-04 04:58:44 +02:00
Martin Vogel 7d390c5120 fix(ci): unbreak the MSan and diag lanes on x86-64
MSan: vendored zstd fails to compile. Its MSan-only block (guarded by
MEMORY_SANITIZER) declares __msan_test_shadow returning intptr_t and
reaches for the type with

    #define ZSTD_DEPS_NEED_STDINT
    #include "zstd_deps.h"

but the amalgamator that produced zstd.c collapsed that second include
into a "skipping file" comment, so the define pulls nothing in and
intptr_t is undeclared. Only this lane compiles that block at all, and
only where <stddef.h> does not drag stdint.h in transitively -- which is
why it built on the local aarch64 container and failed on CI's x86-64.
The lane now forces the header. Patching the vendored amalgamation would
be silently undone by the next re-vendor.

diag: detect_invalid_pointer_pairs comes back out. It fires during static
initialisation inside vendored simplecpp -- a std::string global at
simplecpp.cpp:101 -- with a second "pointer" of 0xfffffffffffffff3, a
sentinel rather than an address: libstdc++ string internals, not
anything this codebase wrote. It is a process-wide runtime flag with no
per-file scoping, so unlike the analyzer's path filter it cannot be
aimed away from vendored code. Keeping it would mean a permanently red
lane reporting a non-defect, which is how a lane gets ignored. The
instrumentation it needed comes out with it.

The other three off-by-default checks stay: stack-use-after-return,
stack-use-after-scope, strict-string-checks. Those are the ones covering
bug classes nothing else in the matrix looks for, and none of them
fired.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-03 16:23:19 +02:00
Martin Vogel f5baf1baef fix(ci): route the new lanes through canonical leg entries
The MSan and memory-analyzer jobs drove docker and make directly from
workflow steps, and the diag step used folded `run: >`. All four are
venue-parity violations: a venue may provision, plumb artifacts, or call
a canonical leg script, and nothing else. Anything that actually
exercises the product belongs inside a scripts/ entry so that every
venue runs the same code instead of each workflow growing its own
slightly different invocation.

So the docker work moves to scripts/ci/msan-lane.sh (build | run | all)
and the analyzer gate to scripts/ci/lint-mem.sh, both of which the local
paths already reach through run.sh and the Makefile. The folded step
becomes `run: |`.

Found by the contract itself, running as step 0j of the local Linux leg.
It had never run against these jobs, because they were added and then
exercised only through GitHub CI -- which is exactly the gap the local
ladder exists to close, and the reason the contract runs as step zero of
every leg rather than as a job of its own.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-03 15:37:33 +02:00
Martin Vogel 0fe0f453be build(test): close the gaps in the sanitizer matrix
Every lane here was either asserting coverage it did not have, or was
reachable only by bypassing the entry point that is supposed to define
the ladder.

TSan: no exclusions any more. The three suites the Makefile documented
as excluded are back. daemon_ipc and daemon_frontend no longer reproduce
the harness race and thread leaks they were excluded for. daemon_runtime
did not deadlock as the comment claimed -- it reported a real production
data race on the log sink, fixed separately. Excluding a suite from a
sanitizer lane hides exactly the class of bug the lane exists to find,
so the comment block now records what was actually true rather than what
was assumed.

TSAN_OPTIONS gains report_thread_leaks=0. This disables the thread-
HYGIENE check only; race detection is untouched. Several daemon fixtures
fork after the process has gone multi-threaded, and in the forked child
TSan sees the parent's already-finished threads as never-joined even
where the fixture joins them. It fires on macOS and not Linux, i.e. it
tracks fork semantics rather than anything about this code. The
alternative was dropping whole suites, which costs real race coverage;
this costs none.

UBSan: tre.o no longer builds with -fno-sanitize=alignment. Alignment
was switched off for a vendored regex engine that ships in the product,
which is where the check is least redundant, not most.

LSan on macOS: new test-lsan target and test-lsan-macos CI leg. LSan is
on by default under ASan on Linux, so the Linux legs have always had
leak coverage. On macOS it is off by default and Apple's clang refuses
to enable it outright, so that platform had none at all. Apple's refusal
is not a darwin limitation -- upstream LLVM supports LSan on darwin/
arm64. The lane is the ordinary ASan suite built with Homebrew LLVM and
run with detect_leaks=1; it runs the full suite clean and was checked to
still catch a deliberately leaked allocation.

MSan: reachable from the local ladder. The image and compose service
existed but run.sh had no leg, so the only way in was to drive docker
compose by hand -- which means it was not part of the ladder in any
meaningful sense. The image also moves to clang 22, matching the diag
and analyzer lanes instead of sitting four majors behind on noble's
default. The leg documents the aarch64 shadow-mapping failure so a local
arm64 stack overflow in the grammar suites is not mistaken for a code
defect; the GitHub leg runs x86-64, which is the mapping that matters.

Off-by-default ASan checks: the diagnostic lane, and its CI twin, now run
detect_stack_use_after_return, detect_stack_use_after_scope,
detect_invalid_pointer_pairs (with the -fsanitize=pointer-compare,
pointer-subtract instrumentation it requires) and strict_string_checks.
Running ASan is not the same as running all of it, and these four cover
bug classes nothing in the matrix was looking for. They stay on the
diagnostic lane rather than the gating ones until they have a clean
history there; promoting them is a separate deliberate step.

Verified: macOS TSan 940 passed / 3 skipped / 0 races over the full
suite set; the macOS leak lane 7375 passed / 4 skipped / 0 leaks, with
LeakSanitizer confirmed armed under that exact toolchain and option set
by checking it still reports a deliberately leaked allocation.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-03 15:37:33 +02:00
Martin Vogel 54dd5ad781 ci(msan): restore the lane; x86-64 CI is its authoritative venue
Two corrections.

First, removing the CI job while keeping the compose service and the local
bindings left the ladder carrying a lane CI did not have — the venue
asymmetry the unification work exists to prevent. The lane now lives in both
venues again.

Second, and the reason the removal was wrong: the evidence behind it was
entirely from the LOCAL arm64 container, while the job that got deleted would
have run on x86-64. MSan's shadow and stack handling are materially better
supported on x86-64, so the thread-stack overflows that drove the exclusions
may well be architectural. I never tested the architecture CI uses, and the
local ladder cannot emulate it faithfully — so 'it cannot run in CI' was an
inference from the wrong platform stated as a fact.

The CI leg therefore runs with MSAN_EXCLUDE="" — no exclusions — so the
first run settles the question with real evidence on the real architecture.
The script's exclusion list stays as the LOCAL default, documented as such.
An accepted local/CI divergence for this lane specifically.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-03 15:37:33 +02:00
Martin Vogel f96ddf5f93 chore(msan): keep the lane local and exploratory, not in CI
Running the lane to completion does not currently work: with three suites
already excluded for thread-stack overflow it now dies with a plain SIGSEGV
elsewhere. Wiring an auto-running job that cannot finish would be exactly the
structurally-red lane O10 forbids, so the test-msan CI job is removed and the
lane is documented as exploratory and local-only.

What it IS worth, and why the infrastructure stays: every suite it does run
is clean under MSan, including the C++ preprocessing path that justified
building the instrumented-libc++ image in the first place, and it correctly
identified one convincing-looking report as a mixed-build artifact rather
than a bug. The image, the script, the compose service, the MSAN_ORIGINS and
CBM_THREAD_STACK_MB knobs, and the full record of what was tried all remain,
so picking this up is a continuation rather than a restart.

The blocker is one problem, stated at the exclusion site: threads whose
stacks are sized outside cbm_thread_create overflow under instrumentation,
and the failure follows cumulative process state rather than any single
suite's depth — which points at per-suite process sharding or finding that
thread creator as the fix.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-03 15:37:33 +02:00
Martin Vogel c9886d4f25 feat(ci): MemorySanitizer lane — instrumented libc++/zlib image, full C++ coverage
Stage 2 of the memory-diagnostics program (user decision: go directly to the
instrumented image rather than a C-only probe). MSan detects uninitialized
READS, the one memory-error class no other lane covers dynamically, and it
requires every linked library to be instrumented -- vendored C deps compile
in-tree and instrument for free; the two external links do not:

- test-infrastructure/Dockerfile.msan: pinned-base image building
  libc++/libc++abi/libunwind (llvmorg-18.1.8, LLVM_USE_SANITIZER=
  MemoryWithOrigins) and static zlib v1.3.1 into /opt/msan, with the
  symbolizer and MSan runtime in a separate last layer so tool additions
  never invalidate the ~30-min libc++ build.
- scripts/msan.sh: the canonical lane entry. ALWAYS clean-builds its
  BUILD_DIR: make does not encode flags into dependencies, and a stage-1
  probe's libstdc++ objects surviving into the libc++ lane produced a
  convincing-looking uninitialized-value report at preprocessor.cpp:168 --
  the uninstrumented .so string constructor wrote the temporary, the
  instrumented move constructor read it. The clean rebuild proved it an
  artifact: extraction (incl. the C++ preprocessing path) runs 272/272 with
  zero reports.
- Makefile.cbm: CXX_STDLIB / CXX_STDLIB_FLAGS hooks so the lane can swap
  libstdc++ for the instrumented libc++ (defaults identical; the shipping
  build is byte-for-byte unaffected).
- docker-compose test-msan service: same aarch64 seccomp/setarch remedy as
  the TSan service (MSan's shadow layout hits the same personality() block).
- CI test-msan job (_test.yml): buildx local-cache via the repo's existing
  pinned actions/cache -- no new third-party action pins; a warm run skips
  the libc++ build entirely.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-03 15:37:33 +02:00
Martin Vogel 80afcd6bbe fix: close the memory-error paths the clang-analyzer lane surfaced
The memory-diagnostics report's priority-4 lane (path-sensitive clang-analyzer,
memory checks only) run over all 111 production files. 21 findings triaged;
the real ones, all cold-path (none can explain #581's per-query residual):

LEAKS
- mcp get_architecture: scope_path leaked on the missing-store early return
  (REQUIRE_STORE frees only `project`); allocate after the gate.
- pass_definitions: cancellation mid-extraction leaked the pass-owned result
  cache including already-extracted entries; mirror the end-of-pass cleanup.
- store package-boundary scan: the row-scan abort path freed the node arrays
  but not the boundary accumulators or their duplicated package strings.
- cbm quarantine set: a duplicate path line leaked the replaced value (and a
  fresh key copy -- the table borrows key pointers); a partial strdup failure
  leaked the surviving half. Reuse the stored key for duplicates.
- pass_githistory: unchecked malloc/strdup -- an OOM dereferenced NULL and a
  failed strdup leaked the index cell. Allocate before claiming the slot.

NULL/UB
- cli config subcommand: NULL argv with nonzero argc slipped the guard (the
  inner `argv &&` shielded only the help comparison) into argv[0].
- store bfs_multi: a negative max_results broke out before any row was
  written, then freed fields of an unwritten negative-index slot. Clamp.
- pass_calls emit_http_async_edge: the service-pattern call sites pass a NULL
  target behind a hand-duplicated URL predicate; a drift between the copies
  turned target->id into a null deref. The callee is now total.
- sqlite_writer: both leaf-array OOM paths left leaf_count stale with a NULL
  array, walking pb_finalize_* into leaves[0]; consistent empty state routes
  them to the existing root=0 failure return.

HARDENED (invariants true but invisible to path-sensitive analysis)
- Leiden CSR + aggregate arrays, SCC adjacency: calloc + endpoint guards, so
  a future degree/collection miscount degrades benignly instead of UB.
- SCC cycle fill: the ncyc==0 no-slot invariant made local.

RECORDED FALSE POSITIVES (no code change)
- yaml sequence starts (loop bound == alloc bound), cypher agg arrays (same
  count both sides), mcp read_message ch (assigned by fgetc each iteration),
  pkgmap clean buffer, mcp csize (Tarjan: ncomp>=1 when nverts>=1), vendored
  verstable x2.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>

GATE + LANES (user decision: runner cost accepted)
- make lint-mem (local triage) and lint-mem-ci (gating: vendored-filtered,
  any remaining finding fails). The gate is green because every false
  positive above was restructured for provability -- calloc'd fill-cursor
  arrays, explicit Tarjan invariant, zeroed buffer tails, min-1-element
  allocations -- never suppressed.
- make diag: pinned newest-LLVM ASan/UBSan lane with straighter stacks.
- CI: lint-mem job (_lint.yml) and test-diag job (_test.yml), both on the
  pinned LLVM 22 apt toolchain. Cost disclosure: roughly +25-40 min and
  +25-60 min (ccache-warm) per push respectively.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-03 15:37:33 +02:00
Martin Vogel c180ec2e64 Merge pull request #1157 from DeusData/dependabot/github_actions/actions/setup-node-7.0.0
build(deps): Bump actions/setup-node from 6.4.0 to 7.0.0
2026-07-31 01:56:54 +02:00
Martin Vogel c1425d2e27 ci: write the shard manifest before any suite runs; warn on absent upload
The manifest content (leg, slice, list hash) is fully determined at
slicing time, so write it up front: a red run's manifest is exactly as
load-bearing for the cross-shard union proof as a green one's.

With that, a missing manifest at upload time can only mean the job
died before the harness started (contract step / build failure) — that
failure is already the job's red, so the upload warns instead of
stacking a second error on top (the recurring 'No files were found
with the provided path: build/c/test-logs/shard-manifest.txt'
annotation). The shard-completeness job still gates the union.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-27 02:00:59 +02:00
Martin Vogel c400dc1665 ci: hosted runners cannot enable Defender RTP — revert runner-side gating
Dry run 30225685743 failed at the ensure-defender step on every
Windows job: the hosted images policy-lock real-time protection OFF
(WinDefend starts, Set-MpPreference accepts, RTP stays off —
untamperable from the job).

Revert to the pre-gate runner behavior: no ensure-defender step in
runner jobs, the release Defender scan is on-demand and fail-soft
again. The local VM preflight KEEPS the fail-closed Defender-ON gate
(same canonical script) — AV-interaction coverage is a deliberate
local superset where the platform allows it.

The venue-parity contract now enforces the INVERSE for runners
(ensure-defender must not gate hosted jobs) and still requires the VM
preflight gate; VM README documents the verified posture delta.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-27 01:56:33 +02:00
Martin Vogel 0a40fe8bea feat(ci): local-remote parity lanes — artifact-flow smoke, glibc floor, Defender-ON everywhere
Closes the closable local-vs-remote venue gaps so that, outside arch
legs / YAML glue / release plumbing, every local red is a remote red
and vice versa on shared legs:

- scripts/package-release.sh: THE canonical archive step (names +
  five-file Windows bundle layout defined once); _build.yml's eight
  inline archive blocks become calls to it.
- scripts/ci/smoke-artifact.sh: the artifact-flow smoke lane — build,
  package, extract, then the canonical wrapper in artifact mode; wired
  as run.sh smoke-artifact (compose service), win.sh smoke-artifact,
  and directly runnable on macOS. Archive-layout bugs now surface
  locally instead of in a release dry run.
- glibc-floor leg (Dockerfile.glibc22 + compose + run.sh): portable
  binary smokes on ubuntu-22.04/glibc 2.35; the dynamic binary must
  refuse there (2.38+ floor by design).
- Defender-ON parity (user directive): scripts/ci/ensure-defender.ps1
  enables + VERIFIES real-time protection, fail-closed; runs in every
  Windows CI job (_test x2, _soak x3, _smoke, pr.yml) AND in the VM
  preflight; _smoke.yml's scan engine-failure soft-skip becomes a red
  gate. Expected cost: slower Windows jobs (AV scanning during
  install/build/test I/O); the next dry run proves the runner side.
- Contracts: launcher-bundle five-file check retargeted onto
  package-release.sh + per-archive canonical-call association;
  venue-parity contract requires the new lanes, counts one
  ensure-defender step per Windows job, adds --help probes for the new
  entries. Extended contract fails on the pre-change tree (verified:
  20 violations on HEAD).
- VM README: ephemerality/Defender posture documented — utmctl has no
  snapshot verb, so per-run revert stays a manual qcow2 option; the
  sweep preflight remains the standing mechanism.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-26 22:37:48 +02:00
Martin Vogel 3fc93d12da ci: unify smoke/soak/test venues onto canonical leg entries
The staged venue-unification bundle: _smoke.yml onto the shared
wrappers with an extracted-artifact input (CBM_SMOKE_ARTIFACT_DIR),
soak-legs.sh as the one canonical soak entry (quick + query-leak legs
across all venues), protected per-user TEMP roots shared across
venues, clean-disk preflights before every VM/Docker run, win.sh and
run.sh routed through the canonical scripts, soak.yml retired, and
the venue-parity contract (whitelist walker: workflows may provision
or call canonical entries, nothing else).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-26 22:23:05 +02:00
Martin Vogel 62821375c4 fix: harden daemon release paths and verification
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-24 16:08:42 +02:00
Martin Vogel 9fb3bdaff1 ci,test: keep a spaced SANITIZE value one make argument end to end
The windows-11-arm leg's first execution (broad matrix, release dry
run) died before any test ran: the trap-UBSan SANITIZE value passed
unquoted through the workflow ternary, so -fsanitize-trap=undefined
arrived as its own word and make consumed the leading -f as its
makefile flag ('No rule to make target sanitize-trap=undefined').

- The workflow quotes the ternary value so test.sh receives one
  argument.
- test.sh forwards make arguments through an ARRAY instead of a
  re-split string, so a VAR=VAL whose value contains spaces survives
  every boundary (empty-array expansion kept bash-3.2-safe for the
  macOS runners under set -u).

Verified: argument-parse proof (argc=1 with the four-flag value) and a
full macOS suite run through the changed plumbing, 6776 passed,
0 failed — identical totals.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-23 07:11:38 +02:00
dependabot[bot] a49b1db23a build(deps): Bump actions/setup-node from 6.4.0 to 7.0.0
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.4.0 to 7.0.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e...820762786026740c76f36085b0efc47a31fe5020)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-23 01:55:06 +00:00