row 292 (#1797) packet: rebase onto b23783f39 with the 0585b3213 resolution (row 203's console and federation text kept word for word, Gander's text for any other key), range-diff, the three patch-ids, C1/N3, mutants M1-M9 (unrun) and the run plan (sage-3146, sage-3148)
This commit is contained in:
1 parent
4fa7d68348
commit
f8b9c2f73e
2 files changed
+255
No files matched your search
@@ -0,0 +1,205 @@
|
||||
# Row 292 (#1797): config_checks runs the target loader on any config
|
||||
|
||||
Builder Gizmoduck (sage-3146, mos-1387), reviewer Duckworth, gate owner Sage. Brief
|
||||
docs/plans/2026-10-22_config-checks-any-key-brief.md. Gander built round 1 on 01983cd3f
|
||||
(gander/config-checks-any-key, packet agents/gander/work/config-checks-any-key/, Duckworth static
|
||||
approve duckworth-0229 with C1-C2 and N1-N4). Gander doesn't build 292; questions about round 1 go
|
||||
to Gander. Nothing in this packet has run. No test, mutant or repo node ran, with one exception
|
||||
recorded under "Disclosure".
|
||||
|
||||
## Rebase (sage-3146 item 1, sage-3148)
|
||||
|
||||
Branch gizmoduck/row292, base b23783f39 (the row 270+271 landing 7328da8ca plus its docs commit;
|
||||
row 203 landed earlier). `git rebase --onto b23783f39 01983cd3f d4c796e44`.
|
||||
|
||||
| commit | rebased | patch-id (stable) before | after |
|
||||
|---|---|---|---|
|
||||
| 0585b3213 fix(release) | 3b17c7550 | a922252a85b1317e09a625193172167b7ea39edb | 15f1a38e3c1e6c72c38b77a460b6cd4ce60c6dd4 |
|
||||
| e7371025f docs (plan, packet) | a54baff21 | 8e9b78ac185a49548e94e76c83f7fea1a982038a | 8e9b78ac185a49548e94e76c83f7fea1a982038a |
|
||||
| d4c796e44 docs (approval) | 9c6e39b25 | fc50b630d432ff7134e3d6c711a054d15ff1f997 | fc50b630d432ff7134e3d6c711a054d15ff1f997 |
|
||||
|
||||
e7371025f and d4c796e44 keep their patch-ids. 0585b3213 changes, by the conflict below.
|
||||
|
||||
### The conflict and its resolution
|
||||
|
||||
Only `scripts/stack-release.sh` conflicted, in `config_checks` (now :1076-1107). Row 203
|
||||
(284d55cf2) made the old probe exit 0 on a `console` or a `federation` key and the refusal read
|
||||
"holds a console or federation key". Gander's commit replaced the probe and split the refusal into a
|
||||
console text and a text for any other key. The test file and docs/TOOLS.md merged without conflict.
|
||||
|
||||
Resolution, per sage-3146 item 1 and sage-3148:
|
||||
|
||||
1. The probe picks `console` first, then `federation`, then the first key outside the oldest set
|
||||
`configVersion, environment, dataRoot, execution`. Without the federation step, a config that
|
||||
holds a later key before `federation` would get Gander's text although the loader refuses
|
||||
federation.
|
||||
2. With no target loader, a console or federation key gets 203's reason "it has no
|
||||
scripts/mosaic-config.mjs", as at b23783f39. Any other later key gets Gander's "to validate
|
||||
configuration key: <key>".
|
||||
3. The refusal for a console or federation key is 203's line, byte for byte as 284d55cf2 and
|
||||
b23783f39 have it (compared by script, not by eye). Gander's text, "is refused by $TO's loader
|
||||
($first); remove the refused key first (restore the config.json backup from the window's step 1),
|
||||
...", applies only when the key is neither console nor federation. In both, `$first` is the
|
||||
loader's first stderr line, which names the refused key (`unsupported configuration key: "<key>"`).
|
||||
4. The comment names 203's protection. docs/TOOLS.md gains one sentence: a console or federation key
|
||||
keeps row 203's message.
|
||||
|
||||
Gander's console-only text "holds a console key" (round 1 at 01983cd3f, before 203) is gone: 203's
|
||||
wording covers console as well. Duckworth's N1 (console plus another refused key keeps the console
|
||||
text) holds, now for federation too.
|
||||
|
||||
Row 203's tests: every line of stack-release.test.mjs up to :2600 at b23783f39 is unchanged. The
|
||||
rebased file only appends. KEY_MSG (:1471), the item 3 console cases (:1575-1580), the row 203
|
||||
federation test (:1585-1595) and :1607 are byte-identical to the base. No 203 test or assertion
|
||||
changed, so nothing goes to Mos under mos-1272 for it.
|
||||
|
||||
### Range-diff
|
||||
|
||||
`git range-diff 01983cd3f..d4c796e44 b23783f39..9c6e39b25`:
|
||||
|
||||
```
|
||||
1: 0585b3213 ! 1: 3b17c7550 fix(release): validate non-console config keys with target loader
|
||||
@@ docs/TOOLS.md: onto the pointer's copy from the target, past any same-copy re-pi
|
||||
+key. Every existing parseable config is checked; a missing file passes. A target
|
||||
+without a loader accepts only the oldest top-level keys: `configVersion`,
|
||||
+`environment`, `dataRoot`, and `execution`; any later key refuses. The refusal
|
||||
-+names the rejected key and says to restore the config.json backup first. So a
|
||||
++names the rejected key and says to restore the config.json backup first. A
|
||||
++`console` or `federation` key keeps row 203's message, which says the config
|
||||
++holds a console or federation key. So a
|
||||
window re-pins to row 192 or later before it writes the key, and a rollback past
|
||||
row 192 removes the key first.
|
||||
|
||||
@@ scripts/stack-release.sh: slot_problem() {
|
||||
-# target's own scripts/mosaic-config.mjs validate; its output, the resolved config, is not kept.
|
||||
+# Row 200 item 3 and #1797: every existing parseable config must be one the
|
||||
+# target loader reads. Older copies without a loader accept only the four keys
|
||||
-+# in c3d29e796's first loader. Resolved config stdout is never kept.
|
||||
++# in c3d29e796's first loader. A console or federation key (row 203) keeps its own refusal text.
|
||||
++# Resolved config stdout is never kept.
|
||||
config_checks() {
|
||||
- local cfg="${MOSAIC_CONFIG:-$HOME/.config/mosaic-dev/config.json}" out loader="$COPIES/$TO/scripts/mosaic-config.mjs" first
|
||||
-- # 0: a console key; 1: no key, or no file; 2: the file doesn't parse, so whether it holds the key
|
||||
+- # 0: a console or federation key (row 203: a pre-203 loader refuses federation too); 1: neither, or no file; 2: the file doesn't parse, so whether it holds the key
|
||||
- # is unknown, and the run refuses rather than pass it (webby-0028 N2). load_config already exits on
|
||||
- # such a file before anything is read or stopped, so this is reached only if the file changes after it.
|
||||
+ local cfg="${MOSAIC_CONFIG:-$HOME/.config/mosaic-dev/config.json}" out loader="$COPIES/$TO/scripts/mosaic-config.mjs" first extra
|
||||
+ # 1: missing file; 2: unreadable JSON. This also fences a change after load_config.
|
||||
++ # extra: "console" or "federation" first (row 203: a pre-203 loader refuses federation too),
|
||||
++ # then the first key outside the oldest set.
|
||||
local key=0
|
||||
- node -e 'const fs=require("fs"),f=process.argv[1];if(!fs.existsSync(f))process.exit(1);let d;try{d=JSON.parse(fs.readFileSync(f,"utf8"))}catch{process.exit(2)}
|
||||
--process.exit(d!==null&&typeof d==="object"&&Object.hasOwn(d,"console")?0:1)' -- "$cfg" 2>/dev/null || key=$?
|
||||
+-process.exit(d!==null&&typeof d==="object"&&(Object.hasOwn(d,"console")||Object.hasOwn(d,"federation"))?0:1)' -- "$cfg" 2>/dev/null || key=$?
|
||||
+ extra="$(node -e 'const fs=require("fs"),f=process.argv[1];if(!fs.existsSync(f))process.exit(1);let d;try{d=JSON.parse(fs.readFileSync(f,"utf8"))}catch{process.exit(2)}
|
||||
+const oldest=["configVersion","environment","dataRoot","execution"];
|
||||
+if(d===null||typeof d!=="object"||Array.isArray(d)){process.stdout.write(JSON.stringify("[configuration shape]"));process.exit(0)}
|
||||
-+const k=Object.hasOwn(d,"console")?"console":Object.keys(d).find(k=>!oldest.includes(k));if(k!==undefined)process.stdout.write(JSON.stringify(k))' -- "$cfg" 2>/dev/null)" || key=$?
|
||||
++const k=Object.hasOwn(d,"console")?"console":Object.hasOwn(d,"federation")?"federation":Object.keys(d).find(k=>!oldest.includes(k));if(k!==undefined)process.stdout.write(JSON.stringify(k))' -- "$cfg" 2>/dev/null)" || key=$?
|
||||
[ "$key" != 1 ] || return 0
|
||||
if [ "$key" != 0 ]; then
|
||||
check_refuse config-console-key "$cfg cannot be read as JSON, so whether $TO's loader reads it is unknown; fix the config first, then run the $OP again"
|
||||
@@ scripts/stack-release.sh: slot_problem() {
|
||||
- if [ ! -f "$loader" ]; then out="it has no scripts/mosaic-config.mjs"
|
||||
+ if [ ! -f "$loader" ]; then
|
||||
+ [ -n "$extra" ] || return 0
|
||||
-+ if [ "$extra" = '"console"' ]; then out="it has no scripts/mosaic-config.mjs"
|
||||
++ if [ "$extra" = '"console"' ] || [ "$extra" = '"federation"' ]; then out="it has no scripts/mosaic-config.mjs"
|
||||
+ else out="it has no scripts/mosaic-config.mjs to validate configuration key: $extra"; fi
|
||||
elif out="$(MOSAIC_CONFIG="$cfg" node "$loader" validate 2>&1 >/dev/null)"; then return 0
|
||||
fi
|
||||
first="${out%%$'\n'*}"
|
||||
-- check_refuse config-console-key "$cfg holds a console key, and $TO's loader refuses it ($first); remove the key first (restore the config.json backup from the window's step 1), then run the $OP again"
|
||||
-+ if [ "$extra" = '"console"' ]; then
|
||||
-+ check_refuse config-console-key "$cfg holds a console key, and $TO's loader refuses it ($first); remove the key first (restore the config.json backup from the window's step 1), then run the $OP again"
|
||||
+- check_refuse config-console-key "$cfg holds a console or federation key, and $TO's loader refuses it ($first); remove the key first (restore the config.json backup from the window's step 1), then run the $OP again"
|
||||
++ if [ "$extra" = '"console"' ] || [ "$extra" = '"federation"' ]; then
|
||||
++ check_refuse config-console-key "$cfg holds a console or federation key, and $TO's loader refuses it ($first); remove the key first (restore the config.json backup from the window's step 1), then run the $OP again"
|
||||
+ else
|
||||
+ check_refuse config-console-key "$cfg is refused by $TO's loader ($first); remove the refused key first (restore the config.json backup from the window's step 1), then run the $OP again"
|
||||
+ fi
|
||||
2: e7371025f = 2: a54baff21 docs(release): record any-key config plan and static review packet
|
||||
3: d4c796e44 = 3: 9c6e39b25 docs(release): record static approval and post-compaction review conditions
|
||||
```
|
||||
|
||||
## C1 and N3 (sage-3146 item 3, duckworth-0229 C1)
|
||||
|
||||
Commit 4fa7d6834, test file only.
|
||||
|
||||
- LOADER_OLD (stack-release-fixture.mjs:301) is today's loader with a shorter first list, so it
|
||||
still reads `compactionReadBytes` and `compactionStreams` through the `.concat`. It is no pre-270
|
||||
witness. The new stand-in `PRE270` is C1's copy of today's loader with the
|
||||
`.concat(["compactionReadBytes", "compactionStreams"])` taken out. That leaves
|
||||
`["configVersion", "environment", "dataRoot", "execution", "console", "federation"]`, the list
|
||||
the loader had at bce491715^ (01b57f48b), before bce491715 added the first key. The stand-in
|
||||
asserts the replace changed something, so a reworded loader fails loudly rather than passing a
|
||||
no-op stand-in. The fixture module isn't edited (not an owned file).
|
||||
- New test "config-any-key: a pre-270 target refuses each row 270 key on a full activate and
|
||||
rollback, naming the key". It seeds on B, activates C1 and installs PRE270 as B's loader. For
|
||||
`compactionReadBytes` 1048576 and `compactionStreams` 1024 in turn, it first runs C1's own loader
|
||||
`validate` on the config (exit 0, so the refusal is the target's). Then it asserts the exact
|
||||
refusal for a full `activate B` and for `rollback`, using `refused()`: exit 3, the exact stderr,
|
||||
no file changed, no start or stop and one refused log line. The text is ANY_CONFIG_MSG with
|
||||
`mosaic-config: unsupported configuration key: "<key>"`. Afterwards it restores the loader and
|
||||
config, and the rollback to B passes.
|
||||
- N3: the missing-loader test sets `compactionStreams` to 1024, not `['messages']`.
|
||||
- The missing-loader test also checks 203's no-loader text word for word for `console` and for
|
||||
`federation`, each placed after `compactionStreams` in the file. This covers resolution items 1
|
||||
and 2, which no 203 test reaches: 203's tests all use a target with a loader.
|
||||
|
||||
## Files (manifest)
|
||||
|
||||
Paths changed from b23783f39 to the candidate, with sha256 at the candidate:
|
||||
|
||||
| path | b23783f39 sha256 | candidate sha256 |
|
||||
|---|---|---|
|
||||
| scripts/stack-release.sh | d3f52f72f2ea345118d30dc34f045f2b5b6abce41697e62f6de07d7a2f25748c | 8f535d53896fefe44525dea39169ac8df29f3d7845f83552a34ec1cd707595bb |
|
||||
| packages/cli/tests/stack-release.test.mjs | 45faa5d236d4cfb5cf3c026d64d039a5f29ce8868efeed4e9428cf85a5f0067f | 5a228aee7c3811f906513809f8a78a1fe4f3a26057089ae6c87918f432a5ba65 |
|
||||
| docs/TOOLS.md | 423babffc7be40a2d578ef00e97cdc49d887762e416fc20726d328d96b2a9533 | 611aeb79d603dd1af1917dcbb3db81b51ac0ae30c5b4991ea72e18bc45ee59de |
|
||||
|
||||
The rest of the diff is packets: Gander's agents/gander/work/config-checks-any-key/ (eight files)
|
||||
and this one (BUILD.md, mutants.py). The manifest with every path and sha256 goes to Duckworth with
|
||||
the round key.
|
||||
|
||||
## Mutants
|
||||
|
||||
agents/gizmoduck/work/queue-292/mutants.py. Each anchor appears exactly once, and each mutated
|
||||
script passes `bash -n` (checked with python3 and bash only, no node). The kill lines below come
|
||||
from reading the tests. None has run.
|
||||
|
||||
| id | edit | should die in |
|
||||
|---|---|---|
|
||||
| M1 | restore the console-only skip (brief's required mutant) | config-any-key non-console refusal; the pre-270 test; row 203 federation |
|
||||
| M2 | ignore a failed target validate | config-any-key non-console refusal (exit 0, not 3) |
|
||||
| M3 | no-loader target passes any later key | config-any-key missing-loader |
|
||||
| M4 | malformed JSON passes as absent | config-any-key malformed JSON |
|
||||
| M5 | skip the loader on an oldest-key config | config-any-key accepting loaders (no witness line) |
|
||||
| M6 | probe drops the federation preference | missing-loader federation case (gets the later-key text) |
|
||||
| M7 | probe drops the console preference | missing-loader console case |
|
||||
| M8 | final refusal treats only console as 203's | row 203 federation test; missing-loader federation case |
|
||||
| M9 | no-loader branch treats only console as 203's | missing-loader federation case |
|
||||
|
||||
M1-M5 are Gander's edits (changes.json) on the resolved function. M6-M9 test the resolution.
|
||||
|
||||
## Run plan (on Sage's CLEAR only, C2)
|
||||
|
||||
Gate per the brief. stack-release.test.mjs whole, the cli suite whole and the ten
|
||||
`scripts/test-*.sh`, then M1-M9, each on stack-release.test.mjs whole. Every mutant gets
|
||||
`git checkout`, sha256 equal to the candidate and a clean tree before the next. TMPDIR from
|
||||
`mktemp -d /mnt/storage/scratch/tmp/gXXX`, pipefail, tee, `timeout -k 10` everywhere.
|
||||
|
||||
Limits from the run 5 receipts (gizmo-271-out/r4, 01:49Z 10-23): stack-release.test.mjs whole took
|
||||
682 s (duration_ms 681648), the cli suite took 624 s, and the longest shell suite took 44 s. At 3x:
|
||||
2100 s for stack-release.test.mjs, alone or as a mutant run, 2100 s for the cli suite and 900 s per
|
||||
shell suite. Expect about 12 + 11 + 3 minutes for the base and about 9 x 12 minutes for the mutants,
|
||||
so roughly 2.2 hours in all. Sage may prefer a shorter mutant run; this packet doesn't assume one.
|
||||
|
||||
C2 (duckworth-0229, sage-3146 item 4): every B, C1 and C release now validates its copied real
|
||||
loader. Any red in an old cli test is a suspect from this change. It stops the run and goes to Sage
|
||||
and Mos under mos-1272, unplaced.
|
||||
|
||||
## Disclosure
|
||||
|
||||
During the hold (sage-3146 item 6, no repo node) I ran `node --check
|
||||
packages/cli/tests/stack-release.test.mjs` once, between the rebase finish (02:43:51Z, reflog) and
|
||||
the first C1 commit (02:45:41Z). It is a parse-only syntax check that lasted a few milliseconds. It printed nothing. No other node, test, queue write or https push/fetch has run.
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
# Row 292 mutants. M1-M5 are Gander's (agents/gander/work/config-checks-any-key/changes.json), with
|
||||
# the same edits on the resolved config_checks; M1 restores the console-only skip (the brief's
|
||||
# required mutant). M6-M9 are the resolution's: row 203's console and federation text. `apply <id>`
|
||||
# edits the worktree and prints the touched path; each edit asserts its anchor appears exactly once.
|
||||
# `check` prints each anchor's count and kill. K names the test that should fail (checked by reading,
|
||||
# not run). A restore is `git checkout -- <path>`, then sha256 against the blob in BUILD.md.
|
||||
import sys
|
||||
WT = "/mnt/storage/scratch/gizmo-292/"
|
||||
S = "scripts/stack-release.sh"
|
||||
T = "packages/cli/tests/stack-release.test.mjs"
|
||||
M = {
|
||||
"M1": (S, ' [ "$key" != 1 ] || return 0\n if [ "$key" != 0 ]; then',
|
||||
' [ "$key" != 1 ] || return 0\n [ "$extra" = \'"console"\' ] || return 0\n if [ "$key" != 0 ]; then'),
|
||||
"M2": (S, 'elif out="$(MOSAIC_CONFIG="$cfg" node "$loader" validate 2>&1 >/dev/null)"; then return 0',
|
||||
'elif out="$(MOSAIC_CONFIG="$cfg" node "$loader" validate 2>&1 >/dev/null)" || true; then return 0'),
|
||||
"M3": (S, ' [ -n "$extra" ] || return 0', ' return 0 # drop missing-loader later-key refusal'),
|
||||
"M4": (S, ' if [ "$key" != 0 ]; then\n check_refuse config-console-key',
|
||||
' if [ "$key" = 99 ]; then\n check_refuse config-console-key'),
|
||||
"M5": (S, ' if [ ! -f "$loader" ]; then', ' [ -n "$extra" ] || return 0\n if [ ! -f "$loader" ]; then'),
|
||||
"M6": (S, 'Object.hasOwn(d,"federation")?"federation":', ''),
|
||||
"M7": (S, 'const k=Object.hasOwn(d,"console")?"console":', 'const k='),
|
||||
"M8": (S, ' if [ "$extra" = \'"console"\' ] || [ "$extra" = \'"federation"\' ]; then\n check_refuse',
|
||||
' if [ "$extra" = \'"console"\' ]; then\n check_refuse'),
|
||||
"M9": (S, '[ "$extra" = \'"federation"\' ]; then out="it has no scripts/mosaic-config.mjs"',
|
||||
'[ "$extra" = \'"federation-off"\' ]; then out="it has no scripts/mosaic-config.mjs"'),
|
||||
}
|
||||
K = {
|
||||
"M1": "config-any-key: a non-console target refusal stops activate and rollback before any stop; also the pre-270 test and row 203's federation test",
|
||||
"M2": "config-any-key: a non-console target refusal stops activate and rollback before any stop (exit 0, not 3)",
|
||||
"M3": "config-any-key: missing-loader copies accept only the four oldest keys (compactionReadBytes passes)",
|
||||
"M4": "config-any-key: missing config passes but malformed JSON refuses without calling the loader",
|
||||
"M5": "config-any-key: accepting loaders run validate with the selected config (no witness line on an oldest-key config)",
|
||||
"M6": "config-any-key: missing-loader copies ... (federation after compactionStreams gets the later-key text)",
|
||||
"M7": "config-any-key: missing-loader copies ... (console after compactionStreams gets the later-key text)",
|
||||
"M8": "row 203: a config whose federation key the target loader refuses ... (Gander's text, not KEY_MSG); also the missing-loader federation case",
|
||||
"M9": "config-any-key: missing-loader copies ... (federation gets 'to validate configuration key')",
|
||||
}
|
||||
cmd = sys.argv[1]
|
||||
if cmd == "list":
|
||||
print(" ".join(M)); sys.exit(0)
|
||||
if cmd == "check":
|
||||
for k, (f, a, b) in M.items():
|
||||
print(k, f, open(WT + f).read().count(a), "kill:", K[k])
|
||||
sys.exit(0)
|
||||
f, a, b = M[sys.argv[2]]
|
||||
s = open(WT + f).read()
|
||||
assert s.count(a) == 1, (sys.argv[2], s.count(a))
|
||||
open(WT + f, "w").write(s.replace(a, b))
|
||||
print(f)
|
||||
Reference in new issue
Block a user