Commit graph

78 commits

Author SHA1 Message Date
Meta Vens
17ce907ff0 Eureka: Soul checks git history before recommending a mechanism
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:36:16 +02:00
Meta Vens
dc26be5fc8 Eureka: a general library is judged by consumer expectation, not our call counts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:11:35 +02:00
Meta Vens
da97acccfa ygg-verify: push over ssh:// with LFS push skipped
Git LFS's pre-push hook rejected the scp-style host:path remote and stalled
every Aetheria verify (found by Imagination and Soul, 2026-09-30). The mirror
never needs LFS objects; jobs that need one smudge it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:26:29 +02:00
Meta Vens
6c413cc110 ygg-verify: 5 slots at 8 GiB, and kill jobs that idle 15 minutes
On 2026-09-30 three hung test binaries held all three slots for up to an hour
at ~2% CPU while Yggdrasil sat at load 1.5 of 16. The operator wants
Yggdrasil pinned. CPU caps now oversubscribe on purpose (jobs run niced);
memory is the ceiling, so the per-job cap drops to 8 GiB. The watchdog samples
docker stats each minute and kills a container under 5% of a core for IDLE
seconds (default 900), reporting it red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:00:12 +02:00
Meta Vens
0cc222ab8b Stop ygg-verify printing "Terminated" on every run
The watchdog subshell reported its own killed sleep as "Terminated" on
every job, successful or not. Agents read that as the job failing: one
Hands reported "the script reports exit 0 after Terminated even when the
job fails", which is false. Tested: exit 3 gives rc 3, and true gives rc 0.
Now the subshell's stderr is /dev/null and the TIMEOUT notice goes out
through fd 3. Checked: TIMEOUT=20 on sleep 60 prints TIMEOUT and exits
137, and a clean run prints no stray line. The watchdog is also reaped
with wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 08:48:05 +02:00
Meta Vens
19487f0f2e Hands brief: file-tool paths stay inside the named worktree
A Hands pass on Aetheria Cut 12.3 edited and built the main checkout by
using absolute Read/Edit paths, while its shell sat in the worktree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 18:38:54 +02:00
Meta Vens
4855e60599 Treat split authority as a defect on sight, in Hands and Soul briefs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 16:24:01 +02:00
Meta Vens
bd8dc2e4ed Rename the Mind Steward faculty to Life
Operator ruling, 2026-09-25: Epiphany and Aquarium call this organ Life
(role id `reorientation`), and it is "a much better fit as the actual
load-bearing organ". The eureka-site landing page already says Life.

SKILL.md (frontmatter description, faculty table, dispatch lines, step 6,
model allocation), README.md and briefs.md now say Life. The role itself is
unchanged. SKILL.md defers to global doctrine for the agent type, which the
operator owns and which still says mind-steward. Dated records keep the old
name: changelog entries as written, a note on the 2026-09-15 Epiphany
comparison, and "then called Mind Stewards" in the CultCache postmortem.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:59:59 +02:00
Meta Vens
7e2f66a2c4 Make Self size each cut against Hands' context budget
Operator direction, 2026-09-25: moderate inference spend mainly by making
cuts smaller, so each Hands agent carries less context. Sonnet degrades at
about 0.5M tokens.

SKILL.md step 3 now owns cut sizing: Self estimates the brief, the source
Hands must read, one verification and one fix round, and the item count,
treats 0.5M as the danger line, splits when the estimate passes about half
of it, and checks the reported token total afterwards. The Self-owned
clauses ("Self sizes a cut", one fix batch per file) moved out of the Hands
brief template into step 3; the Imagination template's split clause names
the budget. Section 5 no longer frames the 2026-09-23 spend as misplaced:
the operator counts it as legitimate work, the regret being the model.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:58:51 +02:00
Meta Vens
82afae1afc Record quota sizing, per-file fix batches, and greens that ran nothing
From the StreamPixels transport campaign, 2026-09-23 to 2026-09-24:
- SKILL.md section 5: quota is spent through scope. One Soul gate per cut,
  fix batches only for critical-path findings, and narrow passes for
  small deltas.
- briefs.md: one fix batch per file (800k and 550k runs); require a
  non-zero test count for each named file; a failure called
  "pre-existing" still needs a one-line cause.
- changelog: the evidence, with StreamPixels commit refs.

Staged by path. The unrelated uncommitted Ghostlight hunks in briefs.md
and changelog.md are left in the working tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 16:09:38 +02:00
Meta Vens
dbe594e395 Stop ygg-verify's watchdog from holding a slot after its job ends
The watchdog added in 9359e9c ran in a subshell that inherited the slot
lock's file descriptor. When a job finished early, `kill $watchdog`
ended the subshell but not its `sleep` child. That orphaned sleep kept
the slot for up to TIMEOUT seconds. Two slots were held this way on
2026-09-23, and four jobs queued behind them.

The subshell now closes the lock fd before sleeping, and the cleanup
kills the watchdog's children before the watchdog itself.

Tested on Yggdrasil: a fast job exited 0, and a TIMEOUT=15 hang exited
137 with the TIMEOUT line. Afterwards no sleep process held any
slot lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 14:02:05 +02:00
Meta Vens
976c83bffa Add a DOCKER_ARGS passthrough for one-off network/sysctl variants
Investigating a QUIC interop failure (native bridge client vs .NET managed
server, both on libmsquic, both over loopback) needs to test IPv6-disabled
Docker networking as a hypothesis, which the script had no way to express:
no docker-args hook existed. DOCKER_ARGS is forwarded verbatim into the
container's own `docker run`, word-split by the remote shell, appended right
before the image name — e.g. DOCKER_ARGS='--sysctl net.ipv6.conf.all.disable_ipv6=0'
or DOCKER_ARGS='--network host'. Empty by default, so every existing caller is
unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 13:51:06 +02:00
Meta Vens
9359e9c7d8 Give ygg-verify.sh a watchdog so a hung job comes back red
A Cut 4 test process sat at 0% CPU for 25 minutes on Yggdrasil. It had
no JS work left, but its event loop never exited. The Hands agent
waiting on the job was never woken, and nothing moved until Self went
looking.

The job's container is now named, and a watchdog kills it after
TIMEOUT seconds (default 3600). Killing the docker client alone would
have left the container running. A timed-out job prints a TIMEOUT line
and exits 137.

Tested on Yggdrasil: TIMEOUT=20 on `sleep 90` exited 137 with the
TIMEOUT line, and a fast job exited 0. No containers were left behind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 13:37:12 +02:00
Meta Vens
76fc62986b Record two more lessons from the Ghostlight play agent pass
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 11:54:35 +02:00
Meta Vens
db1c84d3b0 Record two rakes in the stopgap's header
A job piped into tail loses its exit status, because the command runs under
bash -c and the sentinel then reports the pipeline's zero rather than the
job's. That is the same false-green the CRLF fix closed, arriving by a
different road, and it cost a Soul run today.

The second is smaller and just as costly to rediscover: CultLib has no
Cargo.toml at its root, so a rust job must cd into the package first or cargo
fails in a way that looks like a broken image.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 08:13:33 +02:00
Meta Vens
301df32107 Name a tool's absolute path in every brief, and probe a redundancy before deleting
A brief said "use ygg-verify.sh" where earlier ones gave the full path. The
agent could not find it, verified the whole batch on the operator's
workstation instead, and said so plainly rather than claiming the bar was met.
A name is a guess; a path is an instruction. The brief must also say what to
do when a named tool is missing: stop and report, never substitute, because a
substitution can silently relocate work onto a host the load budget forbids.

Second scar. A redundancy ruling of Self's was wrong for the second time in
one campaign, and this one shipped: a guard was deleted on the premise that
aliases are unique per descriptor, which the source contradicts, and a legal
document shape then faulted on every hop path. Both Self and Hands reasoned
from the same unchecked property. Build the input that would need the guard
and watch the other authority handle it, or the argument is just the claim
restated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 07:39:10 +02:00
Meta Vens
181e819131 Name the inference, not the line
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 06:45:25 +02:00
Meta Vens
b12016a3f2 Stop the stopgap reporting success for a job that never ran
Soul lost a run to CRLF and it still exited 0. The cause: this file's own line
endings travel with the heredoc, so the remote bash fails on `set -euo
pipefail\r` before `set -e` is in force, limps past the failure and exits
clean. A verification runner that can report success without running anything
is worse than no runner.

Two guards, because one was not enough to notice this for a day. The remote
now strips carriage returns before bash sees them, and it prints a verdict
sentinel the local side requires; a missing sentinel is treated as failure
rather than as silence. A .gitattributes keeps the file LF on disk so the
first guard is rarely needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 06:19:13 +02:00
Meta Vens
75efac6d56 Confirm a survivor list at -j1 before ruling on it
The same commit mutated three times at -j4 gave three different survivor sets,
while every -j1 run reproduced the same one. Likely the shared cargo target
directory under concurrent builds, which is this file's existing scar wearing
a new hat.

A score survives concurrency roughly intact, because a genuinely killed mutant
stays killed. A survivor list does not, and survivors are what drive rulings.
Stryker's concurrency flag is in the same position, so figures taken at four
in earlier campaigns are provisional; the rulings they informed rest on the
hand-confirmed mutations beside them.

Second entry: three fixtures in two days passed by coincidence rather than by
rule. Values whose bit pattern happened to match the assertion, hashes that
happened to sort the right way, a file wrapped in a directory so a different
path ran. Breaking the rule and watching the test fail is what separates these
from coverage, and it is already in the brief.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 05:21:44 +02:00
Meta Vens
93a2d54dc3 A fixture chosen for convenience is worse than a gap
Three Soul passes over one file and a selection batch found the same thing
three ways. Every artifact-digest test wrapped its file in a directory so a
different code path ran, while production passes a bare file immediately
before launch. A row-tiebreak test passed with the rule reverted and swapped,
because of which real hash happened to sort first. An xattr test pinned
presence while the privilege lives in the value.

None of these looked like gaps. They looked like coverage, which is why they
survived. Write the fixture at the shape production uses, and derive keys from
ids discovered at runtime rather than hoping their order cooperates.

Second scar, same campaign: a counter was an honour system, so it became a
port, and the port is an honour system one level up. Soul restored the bug
around it with the suite green. Where a rule says all access goes through one
place, the enforcement belongs in the build, not in a habit. If the fix is
another thing to remember, it is the same defect one level up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 04:55:34 +02:00
Meta Vens
ae0149c034 Pin a cost with a count, and check a tool before ruling it be configured
Three scars from one Idunn batch.

A test pinned a cost regression with a ratio whose clamp always fired, so it
was really a fixed 200 ms threshold, on a host that now runs three jobs at
once. Clean code had already measured 198.9 ms. The reported 33x separation
was an artifact of one pair straddling the clamp; the real figure was 1.63x.
Count the calls instead, and treat a reported ratio as a claim to check rather
than a result to accept.

Two fixtures in the same batch would have lied, and mutating caught both. A
setuid fixture chowned after chmod, which silently clears the bit. An
observe_frozen test tampered where a different check also fires, so it stayed
green with the check it pinned deleted. A third term could not be pinned
honestly at all, and Hands deleted the fixture rather than keep one that
implied otherwise.

Last, Self ruled a mount carry nosuid without checking that Docker offers it.
It does not, and the stronger mitigation was already in place. A ruling naming
a flag is a factual claim about a tool.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 23:40:35 +02:00
Meta Vens
8f0d29b932 Surviving deletion alone is not surviving deletion together
Soul reported four containment checks where one sufficed, each surviving
deletion on its own, and Self ruled three deleted. With all three gone a
symlink whose whole target is ".." at the root escapes containment. The method
was sound; the conclusion did not follow from it. A redundancy finding has to
be probed at the combination that will actually be deleted.

A Hands hand-trace caught it, because the brief says a deletion judged unsafe
comes back with evidence rather than being quietly kept. That rule stays, and
it applies to deletions Self ordered.

Second scar, same batch: Soul's HTTPS rig had evaporated by the time Hands
was told to commit it. Probes stay throwaway, but once Self rules one becomes
a committed test it is no longer scratch, and the handover has to happen while
it still exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 22:33:01 +02:00
Meta Vens
a565ae1c0c A dynamic dependency resolved by name is unproven until its identity is checked
A QUIC test suite passed every scenario on Windows against an unrelated
msquic.dll picked up from PATH, a tenth the size of the pinned build. The
same missing copy failed loudly on Linux. Windows' search order makes silent
substitution the default failure mode.

Two rules follow. A verification step that loads a pinned native library
reports what it actually loaded, by size or hash, rather than only that the
run passed. And a defect found on one platform gets checked on the other
before it is closed, because both earlier fixes for this lived inside
if(NOT WIN32) and nobody looked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 21:45:13 +02:00
Meta Vens
fb2f2d722f Self creates the worktree before dispatch, and names it in the brief
Two Hands were dispatched into one working tree. The second switched the
branch out from under the first's running build, then found an uncommitted
edit it had not made and stopped rather than forcing the checkout. Nothing was
lost because of that refusal, not because of anything Self did.

The rule was already written and Self ignored it, so the fix is not a louder
warning to Hands. A brief that names a repo path without naming a worktree is
a defect in the brief.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 21:35:04 +02:00
Meta Vens
8b9d31e293 Maps go on main, and a refused instruction that was real
Two scars from one Hands report. The map it read was five days old because
Self commits maps to whatever branch it is standing on; SKILL.md now says
main, and says every brief names the map's branch.

The same agent refused a mid-task instruction from Self that it could not
distinguish from an injection. The instruction was genuine. The fix is not to
make the agent more trusting: scope expansion should arrive as a new dispatch,
not appended after a tool result.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 21:15:48 +02:00
Meta Vens
0bca98a64e Stop restating the stopgap's caps in prose; name the script as their owner
A Mind Steward pass falsified SKILL.md's claim that the stopgap runs at most
two jobs at once. The operator raised it to three this morning and the script
carries that; SKILL.md did not. An agent steering by SKILL.md would have
under-parallelized verification and quietly contradicted an operator ruling.

The changelog's entry for the same day is worse: it records caps of 6 CPUs and
16 GiB, which match neither the script nor SKILL.md. I cannot tell whether
that was ever live. The correction says so rather than inventing a history.

Both surfaces now name ygg-verify.sh as the owner of its own numbers. Prose
that restates a running tool's configuration goes stale in silence, which is
how three surfaces disagreed inside one day.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 21:02:01 +02:00
Meta Vens
f4baf368e6 Raise the stopgap to three slots, on the operator's ruling
The operator: "throw in a third slot, the other cores are mostly sitting idle".
Verification now takes 12 of Yggdrasil's 16 CPUs at most, and the live
services keep four.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 20:48:34 +02:00
Meta Vens
2e0ea97e27 A test's inputs come from the production path
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 19:56:52 +02:00
Meta Vens
5afd75c385 Say that the stopgap's root container cannot judge permission tests
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 19:49:07 +02:00
Meta Vens
d1756ddb54 Changelog: the splash sign flip was an undefended mutant, not a shipped bug
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 18:36:45 +02:00
Meta Vens
e7cb45f554 Record the harness retirement in the changelog
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 18:36:38 +02:00
Meta Vens
37c80e4a06 Retire the hand-written mutation harness, with no fallback
Operator ruling 2026-09-22: better nothing than a harness that punishes
refactoring. Behavioural tests plus the ecosystem's mutation tools on a cut's
diff; Soul's hand mutants stay one-off probes. The stopgap image carries
cargo-mutants instead of pwsh and is tagged by its Dockerfile's hash.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 18:36:19 +02:00
Meta Vens
fd01545fb7 Hand mutations target the rule at the layer it protects
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 18:33:03 +02:00
Meta Vens
7f468c5553 Cap stopgap jobs at the ruled Q-V6 policy: 4 CPU and 12 GiB each
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 17:12:08 +02:00
Meta Vens
fe5bb89c14 Clone instead of worktree in the stopgap, and never edit it in place
A worktree's .git points at a host path the container cannot see, so
Ghostlight's build.rs failed asking git for the commit. Editing the running
script in place killed a job with 'unexpected EOF': bash reads scripts from disk
as it runs, so new versions go in by rename.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 17:06:59 +02:00
Meta Vens
0d9ae24b51 Take whichever stopgap slot frees first, and let dead ssh sessions die
A job that found both slots busy blocked on slot 1 alone while slot 2 freed,
and ssh sessions whose remote end was gone hung on Starfire for over an hour
with no keepalive to notice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 17:05:17 +02:00
Meta Vens
a0813f1110 Mutation testing moves to the ecosystem's tools; anchors become the fallback
Hands and Soul measure suites with Stryker.NET, cargo-mutants, StrykerJS or
mutmut scoped to the cut's diff, and triage every survivor by name. The
hand-written anchor harness is demoted to code no tool reaches. Evidence in
the changelog: Aetheria's 23 harnesses stranded on refactors and one anchor
silently re-targeted; a Stryker spike killed everything they defended and
found the splash-handedness bug they missed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 16:42:58 +02:00
Meta Vens
2fdc056c09 Give stopgap containers the host machine-id, which Idunn's tests require
Soul found 36 Idunn tests fail without it, so Hands' numbers only reproduced
after a hand-made machine-id inside the container.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 16:03:15 +02:00
Meta Vens
a6f86a9836 Keep the stopgap's shell script and Dockerfile LF on any clone
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 14:57:59 +02:00
Meta Vens
dbe952933b Move verification to Yggdrasil: a stopgap runner until Idunn verify lands
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 14:57:45 +02:00
Meta Vens
266faf5ea8 Give the operator's workstation a load budget that Self owns
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 14:19:31 +02:00
Meta Vens
1912d435d3 Log the H14 fix batch: F1 refusal gate, F2 uniqueness, case-sensitivity
Records the three defects Soul's H14 pass found and this pass fixed:
the startup-repair marker check for H1's kept sidecar, the unique
overwritten-copy naming, and the case-sensitive content/hash/no-op
comparisons. Points at the fixing commits.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 12:16:26 +02:00
Meta Vens
42adfb03ee Make content, hash and no-op comparisons case-sensitive
Every hash comparison in the script (sidecar verification, mutant
verification, restore verification: Assert-TargetUnedited, Restore-
Targets, the startup repair loop, and M0) used PowerShell's plain -eq/
-ne, which is case-insensitive. Hashes are hex strings where case never
carries meaning here (Get-Hash and Get-BytesHash always produce a fixed
case each), so this was latent, not exploitable -- but it left every
"is this file still what I think it is" check one string-comparison
operator away from silently accepting a corrupted file whose hex
happened to differ only in case. Switched all eight sites to -ceq/-cne.
Hash case is chosen to matter, both for defence in depth and because
these functions are the harness's only proof that a restore or a
mutant write landed the intended bytes.

The no-op guard that rejects a mutation whose old and new text come out
identical also used -eq: `if ($old -eq $new)`. Under case-insensitive
comparison a mutant that differs from the source only in case, such as
`"query"` to `"Query"`, compared equal and was refused as a no-op even
though it is a real source change. Switched to -ceq.

Anchor matching (Measure-Sites, and the splice-site IndexOf that
follows it) already used [System.StringComparison]::Ordinal, which is
case-sensitive; checked and left unchanged.

Demonstrated in a scratch repo (demo-case) with a single-mutation suite
whose only edit is `"query"` -> `"Query"` against `fn f() -> query { 1
}`, and a fake test command that fails unless the unmutated lowercase
text is present (i.e. is killed by the mutation).

Before: "CaseDemo: replacement changes nothing in t.txt.", exit 1,
mutation never ran.

After: the mutation runs, the fake test fails against the mutated text,
verdict "killed", M0 green, exit 0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 12:15:26 +02:00
Meta Vens
5ac4fea8f5 Refuse startup repair over a sidecar H1 kept on purpose (F1)
H1 (Assert-TargetUnedited, :~166-220) keeps a target's sidecar rather
than removing it when it catches an edit the harness did not make, so
the M0 original survives beside the lost edit. Startup repair (the
sidecar loop after "Repair a previous run that died mid-mutation") could
not tell that kept sidecar apart from a crash's: on the next run it
restored the M0 original over whatever an operator had since hand-
cleaned into the target, moved the operator's clean file to
`.eureka-mutation-overwritten` (clobbering H1's own copy there before
the F2 fix), printed "a previous run died mid-mutation", removed the
sidecar, and finished green. The hand-cleaned edit was gone one run
later with no non-zero exit to notice it by.

Get-H1MarkerPath names a marker, `<target>.eureka-h1-stopped`, that
Assert-TargetUnedited now writes beside the sidecar whenever it keeps
one. Startup repair now scans for these markers before touching any
sidecar; if any exist it refuses the whole pass, restores nothing,
prints which files to reconcile and where their marker is, and exits
non-zero. An operator deletes the marker (and the sidecar, once done
with it) by hand to let repair run again.

Demonstrated in a scratch repo (demo-f1): H1 fired (a fake test command
appended stray bytes to the target mid-run), leaving a sidecar, a
marker, and an overwritten copy. The target was then hand-cleaned to
"alpha-HANDS-EDIT" and the harness rerun.

Before (original script, ignoring the marker): exit 0, target silently
reverted to the M0 original ("alpha"), the hand-cleaned edit gone.

After (fixed script): exit 1, "Startup repair refuses to run: 1 file(s)
were stopped by H1 in a previous run, not crashed...", target still
"alpha-HANDS-EDIT", sidecar/marker/overwritten all untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 12:14:13 +02:00
Meta Vens
6ec92de112 Never clobber .eureka-mutation-overwritten (F2)
Assert-TargetUnedited (tools/eureka-mutations.ps1:172-173 before this
change) and the startup sidecar repair (:342) both wrote an unconditional
"$path.eureka-mutation-overwritten" whenever they preserved bytes that
were about to be overwritten. A second EDIT LOST or a second dead-mid-
mutation repair against the same target silently destroyed the first
preserved copy -- and so did an operator's own file already sitting at
that name.

Get-UniqueOverwrittenPath now picks the first unused name in the family
(".eureka-mutation-overwritten", then ".eureka-mutation-overwritten.1",
".2", ...) before either site writes. Every preserved copy now survives
beside every other.

Demonstrated in a scratch repo (demo-f2): a file was planted at
t.txt.eureka-mutation-overwritten holding "PRECIOUS-PRE-EXISTING-COPY",
then an H1 edit-lost event was triggered against the same target.

Before: the planted file's content was replaced with the newly-lost
bytes; only one copy survived.

After: the planted file is untouched (still "PRECIOUS-PRE-EXISTING-COPY")
and the new loss lands at t.txt.eureka-mutation-overwritten.1; both
copies survive.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 12:13:17 +02:00
Meta Vens
d40d20021c Name what a dev-only seam costs: a release split and an outward gap
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 11:44:17 +02:00
Meta Vens
5d2bd3e93c Fix H1: stop silently discarding an edit made while a mutation run is in flight
Restore-Targets never checked what a target's bytes actually were before
restoring over them, so a build step or stray edit landing mid-run or
between entries was overwritten without a trace (Soul's finding on the
Huginn memory-organ Cut 10 fourth fix batch, 2026-09-22; that same batch
had lost a Hands edit this way). Restore-Targets and the entry loop's
pre-write step now hash the target first: a match against the M0 original
or this entry's own mutant restores as before, anything else is saved to
<target>.eureka-mutation-overwritten, warned about loudly, and fails the
run with a non-zero exit and no further entries.

Demonstrated in the scratchpad with a throwaway repo whose test command
appends a line to the target mid-run: pre-fix the run went green and the
target came back pristine; post-fix it failed loudly and kept the
appended line. Recorded in references/changelog.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 07:32:57 +02:00
Meta Vens
8648fcaec8 Kill by the PID you started, never by image name
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 07:04:59 +02:00
Meta Vens
11ef23ec9c Say when to stop probing a rule and move the observation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 05:11:24 +02:00
Meta Vens
a84669739b Say that recording a ruling is not specifying it, and qualify hand-back
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 03:18:20 +02:00