docs: retire the deployed-and-unproven label on isWithdrawn, and name dab989b
Two things, and the second was found by doing the first.
v1.9.1's notes closed with an explicit open item: isWithdrawn had 17 assertions
and 4 mutation kills but had never been exercised on a released image against the
live logstream, which is a different state from untested and a worse one to leave
unlabelled. It has now been exercised, from a container recreated onto v1.9.1, and
the label is retired with the measurement rather than with an assurance.
What the entry records is the instrument as much as the result, because the result
is only as good as the thing that produced it: the shipped extractor was lifted
out of the toolkit's own test script and used to cut the four predicates out of
the BAKED mempalace.ts, and deriveOwed's three queries were replayed with their
exact shipped parameters. No second copy of the logic was written, which is the
same discipline the suite itself is built on. Baseline owed was established by
three independent routes BEFORE any probe was planted, because without a recorded
baseline a withdrawal that appears to work proves nothing. Predictions were
written down before each measurement; the table reports both columns.
The per-predicate attribution is in the entry deliberately. "The count went back
to baseline" is a claim about arithmetic and would have been satisfied by a
pre-filter or by an unrelated reply; isAnswered=false with isWithdrawn=true on a
candidate that was still in the raw set is a claim about mechanism. The negative
controls carry the same weight as the positive one -- a rule that lets a third
party or a prose sentence empty someone's mailbox would have passed a
count-only check.
Also recorded: the rule fires on the real incident it was written for (seq 112),
which is worth more than any synthetic probe, and the one path still unwitnessed
(the extension's own in-process poll, which only fires when the agent is idle) is
named as unwitnessed rather than folded into the pass.
Second: reaching for the suite as corroboration showed it cannot run on this image
at all -- its own precondition gate rejects the source it exists to check, because
`node --check` does not type-strip on any node and v1.9.1 moved to node 24. All 17
assertions had been skipped since the bump. Fixed in toolkit dab989b, named here
per the rule this repo adopted two commits ago after the withdrawal fix shipped
unnamed in v1.9.1. The gate now also distinguishes "cannot run" from "does not
parse", since collapsing those is how the defect pointed readers at the wrong
file.
The rebuild cost is stated and, having been measured, is smaller than the earlier
draft of this entry claimed: 9aaff26 already moved base_tag by refreshing the
vendored skill snapshot under rootfs/, so the base rebuild was pending before this
fix existed and the toolkit pickup rides along with it.
This commit is contained in:
+83
-4
@@ -166,10 +166,89 @@ measured against both files rather than read off the diff (`new=1/old=0` and
|
||||
`new=0/old=1`), then the canary body was **executed** against each: new → `rc=0
|
||||
ok`, old → `rc=1` empty.
|
||||
|
||||
Still outstanding, and not fixable from this device: `isWithdrawn` has 17
|
||||
assertions and 4 independent mutation kills, but has **never been exercised on a
|
||||
released image against the live logstream**. It is deployed and unproven — a
|
||||
different state from untested, and a worse one to leave unlabelled.
|
||||
**`isWithdrawn` is no longer deployed-and-unproven — and the suite that pins it
|
||||
had been dark since the node 24 bump.** The rule was exercised on the released
|
||||
image against the live logstream on 2026-09-14, from a container recreated onto
|
||||
v1.9.1 (born 13:21:41Z, confirmed by entrypoint-written mtimes and docker-written
|
||||
`/etc` files agreeing to the second; `/proc/uptime` and `ps -o lstart=` were not
|
||||
used, per their retraction). Baked toolkit `e68ee20`, `grep -c isWithdrawn` = 3,
|
||||
mempalace.ts sha256 `7c16fe14…`, and the file pi actually loads verified to be
|
||||
that same path and hash rather than a stale copy.
|
||||
|
||||
The instrument matters as much as the result: the shipped extractor was lifted
|
||||
out of `scripts/test-owed-withdrawal.sh` and used to cut `TERMINAL_STATUS`,
|
||||
`isStrictlyAfter`, `isAnswered` and `isWithdrawn` out of the **baked**
|
||||
mempalace.ts by brace matching, then `deriveOwed`'s three queries were replayed
|
||||
with their exact shipped parameters over the same `/mcp` transport the extension
|
||||
uses. Shipped bytes, live data, no second copy of the logic. Baseline owed = 1,
|
||||
agreed by three independent routes (the extension's own wake-up card; a hand
|
||||
derivation of 23 candidates; the shipped predicates). Every expectation was
|
||||
recorded before its measurement:
|
||||
|
||||
| probe | predicted | observed |
|
||||
|---|---|---|
|
||||
| positive control planted | owed 1 → 2 | 2 |
|
||||
| requester withdraws it | back to 1 | 1, and `isAnswered=false isWithdrawn=true` |
|
||||
| **third party** retracts someone else's ask | no effect | still owed |
|
||||
| requester withdraws in **prose**, no marker | no effect | still owed |
|
||||
| cleanup by ordinary replies | owed == baseline | 1, then 0 |
|
||||
|
||||
The count returning to baseline is only arithmetic; the per-predicate verdict is
|
||||
what makes it a statement about mechanism. Probe A remained a raw candidate
|
||||
throughout and no terminal reply of ours existed on its correlation, so its
|
||||
removal is attributable to the withdrawal rule alone. Final attribution: A
|
||||
cleared by `isWithdrawn` only, the two negative controls cleared by `isAnswered`
|
||||
only — each probe retired by the predicate it was built to exercise. Both marker
|
||||
spellings now have live witnesses (`metadata.withdraws` naming the ask's event id,
|
||||
and naming its correlation). Unplanned and worth more than the probes: against
|
||||
live data the rule also fires on the incident it was written for — seq 112 is
|
||||
reported withdrawn-by-requester, i.e. mbp-m1-2020's seq 119 withdrawal now works,
|
||||
so the 41h false obligation cannot recur. Full record in the coordination log at
|
||||
`project/pi-devbox` seq 140; harness preserved as artifact
|
||||
`art_20260914T141704_a7a9a294a4bb`.
|
||||
|
||||
Not proven, and deliberately not claimed: the extension's **own in-process
|
||||
mailbox poll** surfacing a withdrawn ask. That poll fires at `agent_settled` when
|
||||
the agent is idle; two probes were left owed across the rest of the session to
|
||||
give it a window and it did not fire. Calling that confirmed would be a claim
|
||||
about the session's patience, not about the code.
|
||||
|
||||
**Toolkit pickup `dab989b` — the owed-set suite could not run on this image, and
|
||||
its own gate was why.** Reaching for the suite as corroboration exposed a second
|
||||
defect: its precondition line `node --experimental-strip-types --check "$SRC"`
|
||||
exits 2 on an unmodified mempalace.ts under node 24, and with `set -euo pipefail`
|
||||
that skipped all 17 assertions and both regression guards. The cause is narrower
|
||||
than the error suggests — it names the inline type-import, but `node --check` does
|
||||
not type-strip at all: a file containing only `const x: number = 1` fails
|
||||
identically, while executing the same file works. So the gate could never
|
||||
validate TypeScript on any node; v1.9.1's bump from v22.23.2 to v24.21.0 is the
|
||||
most likely trigger, though with no node 22 on the box that half stays labelled
|
||||
inference rather than measurement. It failed **closed** — loud exit 2, never
|
||||
vacuously green — which is the good direction, and the reason it went unnoticed is
|
||||
that nothing in CI runs this suite.
|
||||
|
||||
That matters more than a red test, because this suite is the only thing that makes
|
||||
`isWithdrawn`'s failure mode visible: a wrong rule there does not throw and does
|
||||
not log, it makes a real unanswered ask vanish from a mailbox forever. `dab989b`
|
||||
strips first and syntax-checks the emitted JS, and splits the exit codes so that
|
||||
`3` means the gate cannot run while `2` means the source does not parse —
|
||||
collapsing those is how the defect disguised itself as "mempalace.ts does not
|
||||
parse" while mempalace.ts was fine. Verified in six directions with expectations
|
||||
written first: clean source `rc=0` 17/17; malformed TypeScript `rc=2`; stripper
|
||||
made unavailable `rc=3` without the misleading message; and the four mutation
|
||||
kills back at e2b060a's counts of 3/1/2/1, so sensitivity is restored rather than
|
||||
asserted.
|
||||
|
||||
> **Cost, and it is smaller than it looks:** `MEMPALACE_TOOLKIT_REF` is resolved
|
||||
> by CI to the head of the toolkit's `main` and folded into the `base_tag` hash —
|
||||
> verified at `docker-publish.yml:126-128`, whose own comment gives the reason
|
||||
> ("otherwise a toolkit-only fix never lands"). So `dab989b` moves `base_tag` and
|
||||
> the next tag rebuilds the base, with nothing to remember to trigger. But it adds
|
||||
> no rebuild that was not already owed: `9aaff26` refreshed the vendored skill
|
||||
> snapshot under `rootfs/`, which is also hashed into `base_tag`, so a base
|
||||
> rebuild has been pending since before this fix existed. The toolkit pickup rides
|
||||
> along with it, and the same rebuild is what finally bakes skillset `e9e45f7` and
|
||||
> turns the snapshot canary above green against the image's own floor.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user