From bf95993e3bd1c71ea0b21c21b5cfa73c214a9cd3 Mon Sep 17 00:00:00 2001 From: Eureka Date: Sat, 7 Nov 2026 11:35:06 +0000 Subject: [PATCH] tools: dotnet-self-env.sh for Selves with a read-only HOME, and the scar in SKILL.md Co-Authored-By: Claude Sonnet 5.5 --- SKILL.md | 8 ++++++++ tools/INDEX.md | 1 + tools/dotnet-self-env.sh | 17 +++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100755 tools/dotnet-self-env.sh diff --git a/SKILL.md b/SKILL.md index eb50985..733f18d 100644 --- a/SKILL.md +++ b/SKILL.md @@ -782,6 +782,14 @@ Scripts that know a project's layout stay in that project. found another Self's live tree "clean and pushed", and `git worktree remove --force` emptied it mid-build. "Clean" says nothing about who is working in a tree. +- **A Self's HOME is read-only: source `tools/dotnet-self-env.sh` before every + dotnet command.** Setting only `DOTNET_CLI_HOME` and `NUGET_PACKAGES` still + fails with an `IOException` on the NuGet-Migrations mutex, because NuGet and + MSBuild keep other state under `HOME`. The file points `HOME`, both of those + and the node-reuse and build-server switches at one writable directory under + the working directory, per command; never change `HOME` in the unit. Hit by + Nibu, Aqua and Libby on 2026-11-07. **A long job needs `run_in_background`:** + `nohup` and `&` die with the call that started them. ## Adapting the pipeline diff --git a/tools/INDEX.md b/tools/INDEX.md index b7915dc..7225070 100644 --- a/tools/INDEX.md +++ b/tools/INDEX.md @@ -12,6 +12,7 @@ gets a Soul pass before merge. | `tools/wait-on.sh` | One general waiter for anything that is not a Yggdrasil job: `pid []`, `log ` (with `WAIT_PID=` a dead process with no match is exit 98; an invalid ERE is exit 3), `file `. The `pid` form prints `GONE (no completion evidence)`: prefer `log` whenever the job writes a done line. Same contract as `ygg-verify.sh wait`: blocks at most 270 s, exit 0 done with a tail of at most 60 log lines, `RUNNING s` exit 99, exit 98 gone with no completion evidence; call it with a Bash tool `timeout` of at least 280000 ms. Replaces hand-rolled poll loops and idle `true`/`echo`/`sleep` calls. | Hands, Soul, Imagination probes | `references/briefs.md` (Hands long jobs, Soul long jobs, detached scripts) | | `tools/mutation-table.sh` | One verify job that runs a list of hand mutants (file, before, after, test) against a revision and returns a kill table: CAUGHT with the killing test, SURVIVED, UNVIABLE, TIMEOUT, NOAPPLY, ERROR. Refuses a red or zero-test baseline and a red BUILD; rows print live. Isolation is Docker's: the baselines run once in a container that holds the revision, that container is committed to a throwaway image, and every mutant runs in a fresh `--rm` container from it, so nothing a run writes (tracked, untracked or ignored, such as `target/`) reaches the next mutant; the image is removed when the job ends, killed or not. The shared registry and sccache volumes and the network are not isolated. A file-level load failure (vitest `FAIL [ ]`, jest `FAIL ` plus `Test suite failed to run`) reads UNVIABLE. Replaces per-agent runmut/mutrun/strykrun scripts. | Soul, Hands (scoped run) | `references/briefs.md` (Hands verification, Soul reruns) | | `tools/cargo-mutants.sh` | `SUMMARY= tools/cargo-mutants.sh [workdir]`: cargo-mutants on a revision range as ONE verify job, the one owner of running it. Plan container lists the range's mutants (diff taken at the workspace root); stage 1 runs each file's mutants against that file's tests only (filter derived from the path, `FILTERS` overrides), deciding only CAUGHT and UNVIABLE; stage 2 confirms every survivor, timeout, unfiltered file and zero-test file on the full suite, sharded; merge counts each name once and prints `MUTANTS` totals, `FILTER` lines and the final `MISSED`/`TIMEOUT` names. Containers = min(`SHARDS`, slots, units, 1 + free slots), leased with `flock -n` from Yggdrasil's slot pool (none while IO pressure full avg60 > 20). `NAMES=` reruns chosen survivors (no stage 1), `TEST_ARGS` skips flaky tests, `JOBS`, `MIN_TEST_TIMEOUT`, `CONFIRM_TIMEOUT_MULT`, `TIMEOUT`. Exit 0 on a green baseline whatever survives; a red baseline or a failed container is non-zero with no totals. Run once per pass. | Hands, Soul | `references/briefs.md` (Hands verification, Soul reruns), `SKILL.md` (mutation bullet) | +| `tools/dotnet-self-env.sh` | Sourced env file for dotnet in a Self whose HOME is read-only: `. tools/dotnet-self-env.sh` sets `HOME`, `DOTNET_CLI_HOME` and `NUGET_PACKAGES` to `$PWD/.dotnet-home` (created; `DOTNET_SELF_HOME` overrides) and turns off telemetry, first-run, MSBuild node reuse, shared compilation and the build server. Source it per command; the unit's HOME is not changed. | Selves and their workers running dotnet | `SKILL.md` (Git and tooling rules) | | `tools/testparse.sh` | Sourced library: failing test names and pass/fail/skip counts from cargo, dotnet, vitest, jest and pytest logs, and test files that failed to load (`LOADFAIL`). Shared by the summary and the kill table. | `tools/stopgap/ygg-verify.sh`, `tools/mutation-table.sh` | not named by a brief; it lives and dies with them | | `tools/test-tools.sh` | Local fixture check of the parsers and the summary over `tools/fixtures/logs` (real cargo and mutation logs, plus hand-written dotnet, vitest, jest, pytest, exit-0-but-failed and no-verdict logs), and of the mutation runners' host drivers and container halves (`tools/mutation-table.sh`, `tools/cargo-mutants.sh`) under a mock docker, a fake `cargo mutants` and real slot locks. Run it before merging a change to the scripts above. | whoever changes those scripts | `SKILL.md` (Shared tools) | | `tools/context-pack.py` | `py -3 tools/context-pack.py [--since ] [--only \|r[,...]] [--spec-json ] [--max-lines ]`. Carries a cut_spec's `file_changes` and `reads` anchors (read rows `r`; a read absent at base is MISSING) from the spec's base to the worktree by `git diff` hunks (the working tree included) and prints one header, a status table (SAME, MOVED, TOUCHED, RENAMED, NEW, GONE, OUT_OF_RANGE, UNRESOLVED, MISSING, plus SYMBOL_MISSING when an anchor's `symbol` is absent from its window) and windows of at most 100 lines numbered in worktree lines, then one capped caller list per symbol (`git grep`, hits outside the windows), within `--max-lines` (800). Exit 3 when an anchor cannot be carried, 4 when the spec is unreadable (fetched by id through the eureka-state binding (`tools/mind.py`), retried once on Unavailable; `--spec-json` takes a saved view). `--since ` re-prints only the windows edited since that commit. Standard library plus git; writes nothing. | Hands, Soul, Imagination | `references/briefs.md` (Imagination map, Hands, Soul), `SKILL.md` (step 1) | diff --git a/tools/dotnet-self-env.sh b/tools/dotnet-self-env.sh new file mode 100755 index 0000000..4a37a8d --- /dev/null +++ b/tools/dotnet-self-env.sh @@ -0,0 +1,17 @@ +# Source this before dotnet in a Self whose HOME is read-only: +# . tools/dotnet-self-env.sh +# Points HOME, the dotnet CLI home and the NuGet cache at one writable directory +# under the current directory and turns off the node reuse, shared compilation +# and build server that outlive the command. Sourced per command; never set +# HOME in the unit. +DOTNET_SELF_HOME="${DOTNET_SELF_HOME:-$PWD/.dotnet-home}" +mkdir -p "$DOTNET_SELF_HOME" +export HOME="$DOTNET_SELF_HOME" +export DOTNET_CLI_HOME="$DOTNET_SELF_HOME" +export NUGET_PACKAGES="$DOTNET_SELF_HOME/.nuget/packages" +export DOTNET_NOLOGO=1 +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +export MSBUILDDISABLENODEREUSE=1 +export UseSharedCompilation=false +export DOTNET_CLI_USE_MSBUILD_SERVER=0 -- 2.47.3