Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0c6142f3a | ||
|
|
42ba0f5bc0 | ||
|
|
68c08b4e76 | ||
|
|
76f84465fd | ||
|
|
1bd214e382 | ||
|
|
56c1845505 | ||
|
|
72a3125a6e | ||
|
|
6a5e125113 | ||
|
|
529e121379 | ||
|
|
9042754c47 | ||
|
|
c98cd41d8d | ||
|
|
94d9e90a6a |
No files matched your search
@@ -5475,3 +5475,4 @@ remain; no live state, schema or bus policy change.
|
||||
2026-10-24 | Goldie | Row265 / #1770 first slot STOP (sage-3932) | New parser14/14, rooms18/18 green; serialized bus stops at old project-runtime.test.mjs:14 import-closure pin, partial619 ok/1 not ok. New pure address.mjs import is absent from expected imports. Static trace also needs address.mjs in seat RUNNER_FILES. No old-test/product correction, no mutant/restored run, no green commit/push; old-pin and out-of-owned-files mount change sent to Sage for ruling. Audit prose missed the direct source pin despite raw grep hit; correction recorded in BUILD.md.
|
||||
|
||||
2026-10-24 | Goldie | Row265 / #1770 verified (mos-1567, sage-3942) | Approved correction adds solely address import pin + Row265 comment to project-runtime.test and one pure-source RUNNER_FILES mount; new block checks parser has no imports. Serialized fresh-short-TMP timeout-k10: unchanged sandbox10/10, parser15/15, rooms18/18, full bus957/957, twelve named refusal mutants killed (each Node1, driver0), restored parser15/15 + rooms18/18. All positives zero fail/skip/cancel; every mutation restored full broker SHA71b32ee4. Old first-red evidence retained, no other old-test change. Slot returned goldie-0034; Boyd packet explicitly includes mount-list review, candidate push next.
|
||||
2026-10-24T07:18:53Z | Sage | Row 327 (#1825, Bradford, Poe and Dewey) LANDED 68c08b4e7. Before: refactor at 94d9e90a6; the push guard was a scratch script with no reviewed-binary check. After: scripts/push-check.sh and scripts/test-push-check.sh on refactor, 3 commits cherry-picked onto 6a5e12511 with blobs equal to the round 3 tip d858c9f8b, plus doc commits 76f84465f and 68c08b4e7. Binaries are found by numstat or a NUL in the first 8000 committed bytes, regardless of attributes; each needs its sha256 and path in an approved latest-round candidate; an unconfirmed queue tail exits 2. Reviews: Poe c31525 rev 3882, Dewey c31526 rev 3883. Gate: 1st run 06:57Z RED, not the candidate: canonical node_modules was empty from 05:45:15Z (sage-4023). Rerun with its own root npm ci GREEN 07:15:49Z: 17 package suites 0 failures (cli 986, plugins 119, webui 418), 11 scripts exit 0, push-check 6 mutants KILLED; skips are the queue canonical-root check and the row 244 live provider cases. The landed push-check.sh passed its own push (NO_BINARY, NO_ATTRIBUTION, PUSH_READY). mos-1575: gates install their own node_modules; canonical restored with npm ci (128 packages).
|
||||
@@ -0,0 +1,162 @@
|
||||
# Row 327 — reviewed binary push check (#1825)
|
||||
|
||||
2026-10-24 Bradford, branch bradford/row327, base b617b80f6.
|
||||
|
||||
## Plan and boundaries
|
||||
|
||||
Implement the scratch check's ordered tokens in scripts/push-check.sh, using an
|
||||
embedded Node program for byte-safe git paths and SHA-256 blob hashing. Bash is
|
||||
the public entry point; Node is already a repository requirement. Git commands
|
||||
use explicit resolved object IDs, disable external diff/textconv, and inspect
|
||||
committed bytes. No queue writes, hook changes, binary evidence, or live state.
|
||||
Fetch is opt-in. Read reviewed rows with the queue package rows() API rooted in
|
||||
the caller's repository; this preserves canonical/witness/replay checks.
|
||||
|
||||
Only the latest round counts; any changes receipt blocks that round. A matching
|
||||
approval authorizes exactly the manifest path/hash, or a path changed from the
|
||||
commit candidate's first parent with matching committed blob hash and mode.
|
||||
Deleted binaries need no candidate. Multiple --row values form a union.
|
||||
|
||||
Add scratch-only integration coverage in scripts/test-push-check.sh. Reuse queue
|
||||
test fixture helpers to bootstrap real scratch queues, then use queue verbs to
|
||||
open rounds and record receipts. All git writes, bare remotes and queues remain
|
||||
under TMPDIR. Test fail-closed ordering and five required mutants, generated as
|
||||
text copies in scratch. No old tests are modified. Add the TOOLS entry.
|
||||
|
||||
Every run needs Sage CLEAR; run timeout -k 10. Record plain-text results here or
|
||||
in this packet. After green checks, commit and push only bradford/row327 through
|
||||
the repository helper, send Sage and Poe tip/base/path sha256 manifest, and move
|
||||
327 in-review. Sage owns request posting and landing; Dewey reviews once there.
|
||||
|
||||
## Recovery and choices
|
||||
|
||||
Initial worktree queue next refused linked-worktree access as designed. Queue
|
||||
claim is performed from the canonical checkout; production reads in the guard
|
||||
retain the same refusal rather than invent a worktree override.
|
||||
|
||||
Requested Sage ruling on AGENTS log appends outside the brief's owned paths.
|
||||
|
||||
Sage sage-3989: lead handles BUILD-LOG and SESSIONS at landing. Session entry:
|
||||
2026-10-24 Bradford — row 327 / #1825 — implementation and scratch verification
|
||||
in this packet; outcome to be appended after acceptance.
|
||||
|
||||
Implementation note: commit candidates use an explicit first-parent diff (empty
|
||||
tree for a root commit), with rename detection disabled, like verify-commit.
|
||||
The changes-veto case has both an approval and a changes receipt; a veto mutant
|
||||
would otherwise still fail for lack of approval and provide false confidence.
|
||||
A receipt for another candidate cannot be produced through queue verbs: the
|
||||
suite corrupts only a scratch queue and expects the production reader to refuse.
|
||||
Diff color, external diff and textconv are disabled so scans inspect plain git
|
||||
output. Paths are read from NUL-delimited records (including spaces/tabs).
|
||||
|
||||
## Verification (Sage CLEAR sage-3991)
|
||||
|
||||
First new-suite run red: my whitespace-path fixture attempted a manifest path
|
||||
that the queue intentionally forbids. Corrected the fixture to a commit
|
||||
candidate, which can contain that git path. Production queue code and old tests
|
||||
remain unchanged. Raw first-run output is test-run-1.txt.
|
||||
|
||||
Second run green: PASS push-check 22 checks; KILLED skip-hash,
|
||||
accept-changes, earlier-round, working-tree, no-row. test-run-2.txt holds the
|
||||
plain-text receipt. bash -n both scripts and git diff --check passed. Runs used
|
||||
TMPDIR recorded in tmpdir.txt and timeout -k 10 240; all generated binary bytes
|
||||
were in disposable scratch repositories, never in this checkout or evidence.
|
||||
The slot was used for only the authorized new suite and syntax/diff checks.
|
||||
No claim of the lead's full scripts/test-*.sh landing gate is made here.
|
||||
|
||||
Final inspection after the first green run found a path-decoding concern: lossy
|
||||
UTF-8 decoding can conflate distinct git filenames. Hardened NUL-delimited path
|
||||
decoding to reject invalid UTF-8, and quote newline-containing paths in refusal
|
||||
output to preserve one STOP line. Added scratch cases for both. Requested a new
|
||||
CLEAR (bradford-0005) because the previous slot had already been released.
|
||||
|
||||
Final run (sage-3994 CLEAR) green on final code: PASS push-check 24 checks,
|
||||
all five mutants KILLED again, bash -n both scripts and git diff --check passed.
|
||||
Receipt: test-run-3.txt. The guard rejects non-UTF-8 git paths as environment
|
||||
errors rather than comparing replacement characters; newline paths remain
|
||||
checkable through commit candidates and refusals quote them on one line.
|
||||
|
||||
Acceptance for the builder stage: ordered tokens and stop behavior, explicit
|
||||
base/fetch, exact blob manifest/commit checks, latest-round/veto checks, deletion,
|
||||
log/FF/secrets/attribution refusals, and all required mutants are covered. Queue
|
||||
reads use rows() with production validation; tests use only scratch queues.
|
||||
Candidate-mismatched receipts are impossible through the queue API and are
|
||||
covered by a corrupt scratch queue refusing its read. The lead's full-suite gate,
|
||||
Poe first review, Dewey landing review, and adoption for Sage's next push remain
|
||||
landing stages. No hooks, role policy, credentials or other seats' paths changed.
|
||||
|
||||
## Poe first review R1 (poe-0010) — changes
|
||||
|
||||
cceea407e has a blocking subdirectory bypass: ls-tree without --full-tree can
|
||||
omit an existing binary outside the caller cwd, making it look deleted.
|
||||
Accepted the finding. Git now anchors all invocations at the repository root,
|
||||
ls-tree uses --full-tree, and diff/diff-tree use --no-relative. EXPECTED_LOG still
|
||||
resolves from the original caller cwd. Added outside-cwd and nested-binary cases
|
||||
under diff.relative=true: unreviewed stops, reviewed manifest/commit passes.
|
||||
No old test changed. New run requires Sage CLEAR before execution.
|
||||
|
||||
R1 verification under sage-3999 CLEAR: test-run-4.txt records PASS 29 checks
|
||||
and KILLED all five required mutants. Syntax and diff whitespace checks passed.
|
||||
A second bounded run used the new suite against cceea407e's script copied under
|
||||
TMPDIR: r1-regression-red.txt records the outside-cwd unreviewed binary reaching
|
||||
NO_BINARY/PUSH_READY (actual exit 0, expected 1). Thus the new regression detects
|
||||
the reviewed bug, while the revised guard refuses it. No code changed between
|
||||
the green run and this old-candidate reproduction. All runs used timeout -k 10.
|
||||
|
||||
Holding claimant queue writes per sage-3999 until Boyd finishes row 309's record.
|
||||
Last acknowledged row 327 state is in-progress (bradford-0010, revision 3868).
|
||||
|
||||
## Dewey landing review B1 (dewey-0956) — changes
|
||||
|
||||
Read the invited review body at /mnt/storage/scratch/tmp/dew327vWXloCT/body.md.
|
||||
B1 identifies a brief-level gap: numstat follows candidate/host git attributes,
|
||||
so *.bin diff makes a NUL-containing blob disappear from the binary-only set.
|
||||
The production script indeed filters numstat before reading any blob, consistent
|
||||
with that data flow. Proposed correction: enumerate every changed numstat path,
|
||||
skip absent/non-blob entries at SHA, classify binary when numstat says binary OR
|
||||
committed blob has NUL among first 8000 bytes. Read with cat-file, never worktree.
|
||||
Test committed .gitattributes, info/attributes, and core.attributesFile overrides
|
||||
in scratch; reject with no row, allow exact reviewed bytes; add NUL-scan mutant.
|
||||
Sage owns the brief amendment and next CLEAR/queue sequencing.
|
||||
|
||||
N1 acknowledged: candidate-equality check is defense in depth; invalid receipt
|
||||
cannot pass queue replay, so the corrupt-queue case does not isolate that check.
|
||||
N2/N3 recorded as described in review, without optional secrets/reader changes.
|
||||
N4 planned hardening: refuse unconfirmed durable tail notes from rows() with
|
||||
exit 2, with a real scratch witness-prefix case rather than a test-only reader.
|
||||
No test run or queue write started for B1/N4 before Sage CLEAR.
|
||||
|
||||
Sage sage-4013/4015 authorizes B1 and N4 within the brief; amendment committed on
|
||||
refactor at 9042754c4 (read with git show, no branch change). Fix and tests written
|
||||
before requesting CLEAR as directed. Tests cover all three attribute sources,
|
||||
approved manifest success under each override, exact 7999/8000 offset boundary,
|
||||
and a real prefix-witness rollback to yield an unconfirmed approval tail.
|
||||
Added only the requested sixth mutant, skip-nul-scan; the original five remain.
|
||||
No new production configuration knobs, no secrets-diff change, no queue writes.
|
||||
Will demonstrate red against the prior candidate using the new tests, then green
|
||||
against revised code, under the approved slot. Full landing gate remains Sage's.
|
||||
|
||||
After Dewey's round-2 CHANGES record (comment 31522, rev3876), Sage sage-4016
|
||||
released only the claimant return to in-progress. Acknowledged:
|
||||
`ok bradford-0017 rev 3877 row 327 in-review→in-progress`.
|
||||
No other queue write before the next in-review candidate move.
|
||||
Test slot remains pending Sage CLEAR after Gyro's build.
|
||||
|
||||
## Amendment 1 verification (sage-4017 CLEAR)
|
||||
|
||||
b1-regression-red.txt: new tests against 704910ca3 reproduce attributes-hidden
|
||||
asset.bin reaching NO_BINARY/PUSH_READY with exit0 instead of exit1.
|
||||
n4-regression-red.txt: revised code with only the N4 check removed lets the real
|
||||
scratch unconfirmed approval tail reach BINARY_REVIEWED/PUSH_READY, exit0 rather
|
||||
than exit2. These are expected negative controls; neither changed committed code.
|
||||
|
||||
Final full new-suite run is green: test-run-5.txt records PASS 38 checks and
|
||||
KILLED skip-hash, accept-changes, earlier-round, working-tree, no-row, skip-nul-scan.
|
||||
All runs used timeout -k 10 240 with the same scratch TMPDIR; no old tests were
|
||||
changed or run. bash -n both scripts and git diff --check also passed. No source
|
||||
changes followed this green run. Sage's full landing gate is still separate.
|
||||
|
||||
Scope in the final delta: classify all changed present blobs using numstat OR
|
||||
first-8000-byte NUL; fail closed on queue unconfirmed-tail notes; regression cases
|
||||
and requested sixth mutant; TOOLS entry and this plain-text packet. B1's brief
|
||||
amendment is Sage's 9042754c4; this branch does not edit the brief or queue files.
|
||||
@@ -0,0 +1,27 @@
|
||||
node:internal/modules/run_main:107
|
||||
triggerUncaughtException(
|
||||
^
|
||||
|
||||
AssertionError [ERR_ASSERTION]: LOG_OK
|
||||
FF_OK
|
||||
NO_BINARY
|
||||
SECRETS_CLEAN
|
||||
NO_ATTRIBUTION
|
||||
PUSH_READY ee394f31d1e52751716e2413049f41b2f3072e65..76acc948149e718091fcd3f3807257e752a6616e
|
||||
|
||||
|
||||
|
||||
0 !== 1
|
||||
|
||||
at Object.check (file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:44:12)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:100:7
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
Node.js v26.8.1
|
||||
@@ -0,0 +1,27 @@
|
||||
node:internal/modules/run_main:107
|
||||
triggerUncaughtException(
|
||||
^
|
||||
|
||||
AssertionError [ERR_ASSERTION]: LOG_OK
|
||||
FF_OK
|
||||
BINARY_REVIEWED 1
|
||||
SECRETS_CLEAN
|
||||
NO_ATTRIBUTION
|
||||
PUSH_READY c1a8a1e9b8c3988874a8f7a35afab7c06aea94a5..325cd3671a7bf70b9a3aee60f4446bc93594a12a
|
||||
|
||||
|
||||
|
||||
0 !== 2
|
||||
|
||||
at Object.check (file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:44:12)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:121:7
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 2,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
Node.js v26.8.1
|
||||
@@ -0,0 +1,27 @@
|
||||
node:internal/modules/run_main:107
|
||||
triggerUncaughtException(
|
||||
^
|
||||
|
||||
AssertionError [ERR_ASSERTION]: LOG_OK
|
||||
FF_OK
|
||||
NO_BINARY
|
||||
SECRETS_CLEAN
|
||||
NO_ATTRIBUTION
|
||||
PUSH_READY 896bdf0ef56596c67d2dc427275743c46026e832..ad9f9c86112fadafef4d4854f46bd9994fe2fe5d
|
||||
|
||||
|
||||
|
||||
0 !== 1
|
||||
|
||||
at Object.check (file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:44:12)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:130:7
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
Node.js v26.8.1
|
||||
@@ -0,0 +1,21 @@
|
||||
node:internal/modules/run_main:107
|
||||
triggerUncaughtException(
|
||||
^
|
||||
|
||||
AssertionError [ERR_ASSERTION]: queue: candidate manifest path must be a repo-relative path: "space tab\tasset.bin"
|
||||
|
||||
|
||||
2 !== 1
|
||||
|
||||
at Object.round (file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:61:12)
|
||||
at file:///home/jwoltje/.t3/worktrees/mosaic-stack/bradford-row327/[eval1]:116:7
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 2,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
Node.js v26.8.1
|
||||
@@ -0,0 +1,6 @@
|
||||
PASS push-check 22 checks
|
||||
KILLED skip-hash
|
||||
KILLED accept-changes
|
||||
KILLED earlier-round
|
||||
KILLED working-tree
|
||||
KILLED no-row
|
||||
@@ -0,0 +1,6 @@
|
||||
PASS push-check 24 checks
|
||||
KILLED skip-hash
|
||||
KILLED accept-changes
|
||||
KILLED earlier-round
|
||||
KILLED working-tree
|
||||
KILLED no-row
|
||||
@@ -0,0 +1,6 @@
|
||||
PASS push-check 29 checks
|
||||
KILLED skip-hash
|
||||
KILLED accept-changes
|
||||
KILLED earlier-round
|
||||
KILLED working-tree
|
||||
KILLED no-row
|
||||
@@ -0,0 +1,7 @@
|
||||
PASS push-check 38 checks
|
||||
KILLED skip-hash
|
||||
KILLED accept-changes
|
||||
KILLED earlier-round
|
||||
KILLED working-tree
|
||||
KILLED no-row
|
||||
KILLED skip-nul-scan
|
||||
@@ -0,0 +1 @@
|
||||
/mnt/storage/scratch/tmp/YPrPwR
|
||||
@@ -0,0 +1,140 @@
|
||||
# Size question for Jason: W1 and W2 history cost (Vikunja 618)
|
||||
|
||||
Written 2026-10-24 by Sage for Mos's mos-1572 and mos-1573. Rows 309 (W1, Glomgold) and 310 (W2, Gyro)
|
||||
keep building and reviewing on their seat branches. Neither lands on refactor until Jason answers.
|
||||
|
||||
Status: complete for Mos, 06:20Z 10-24, with the 209-plus-14 measurement added at 06:39Z (mos-1574). Every number below is measured. W2's figures come from Gyro's
|
||||
scratch measurement build (gyro-0013, 06:18Z), recorded in `agents/gyro/work/queue-310/BUILD.md` section
|
||||
"Full-lock measurement results (sage-3992)" on gyro/row310 (pushed, tip 1716f5a22 at 06:32Z).
|
||||
|
||||
## Today's repository
|
||||
|
||||
`git count-objects -vH` in the canonical checkout, 05:55Z 10-24: size-pack 107.30 MiB, 137,187 objects in
|
||||
5 packs, 119.26 MiB loose. Every number below adds to that, permanently. Git history isn't pruned here
|
||||
(invariant 8, no force-push).
|
||||
|
||||
## W1, the WordNet thesaurus data (row 309)
|
||||
|
||||
- Generated data files: 21,916,660 bytes of text (Glomgold, glomgold-0004).
|
||||
- Mos's figure for the row's text is about 58 MB, which includes the source data it is generated from.
|
||||
- Changed text: 58,476,260 bytes across 45 paths (WordNet 3.1 source, generated JSON, generator, tests,
|
||||
evidence). No binary file.
|
||||
- Packed size of the row: 17,480,317 bytes (about 16.7 MiB), from
|
||||
`git rev-list --objects d16958bcf..b10711603 | git pack-objects --stdout | wc -c` on glomgold/row309
|
||||
tip b1071160363d5d0204de96b06a356c9a04845287 (glomgold-0007, 05:56Z). That's about 16% on top of
|
||||
today's 107.30 MiB pack.
|
||||
- Rebuild cost: the data is regenerated only when the WordNet source or the generator changes, so most
|
||||
W1 follow-up rows add little. A regeneration with a changed generator adds a new copy of each changed
|
||||
file, compressed against the old one where git's delta search finds it.
|
||||
|
||||
## W2, the WordCraft source and WASM build (row 310)
|
||||
|
||||
- Imported source, as of gyro-0007 05:47Z: upstream WordCraft 582 files, 21,021,869 bytes (30 icon,
|
||||
screenshot and macOS packaging files left out, no notice or license among them). Cargo vendor tree 209
|
||||
packages, 10,499 files, 130,737,230 bytes. Total 151,759,099 bytes.
|
||||
- Non-text input files: 18 upstream (4,853,036 bytes) and 74 vendor (4,168,676 bytes). Fonts, test blobs,
|
||||
an MP4 and ICC profiles. No .a, .so, .dll, .wasm or .exe in the inputs. List:
|
||||
`agents/gyro/work/queue-310/binary-inputs.json` on gyro/row310.
|
||||
- The packaging is still being settled. The 209-package vendor tree doesn't resolve against WordCraft's
|
||||
538-entry lock without build-copy manifest edits (target sections, dangling feature items, cfg
|
||||
evaluation; rulings sage-3976 to sage-3984g). At 05:58Z those edits hit their stop line (gyro-0011).
|
||||
Cargo refuses `glifo ^0.2.0`, locked at 0.2.0, which an optional normal dependency of the reviewed
|
||||
vello_cpu 0.1.0 requires. Only glifo 0.3.0 is in the 209, and no cfg section controls that edge. The
|
||||
edits so far cover 28 manifests, 121 target-dependency sections and 109 dangling feature items, all in
|
||||
scratch.
|
||||
- The full-lock alternative: WordCraft's lock has 538 third-party records, 534 from crates.io and 4 from
|
||||
git. Louie's existing toolchain cache holds all 534 .crate archives, 85,234,501 bytes compressed
|
||||
(`agents/gyro/work/queue-310/full-lock-cache-size.json`). The 209 reviewed packages are 130,737,230
|
||||
bytes extracted. Extracted in scratch from that cache (gyro-0012, 06:06Z), the full lock is
|
||||
614,049,226 bytes in 25,378 files, including generated checksum files. That's 329 packages beyond the
|
||||
reviewed 209 (328 from crates.io, 1 from git), and about 4.7 times the 209-package tree. Every extra
|
||||
package would also need a source and notice review against S0. My read: the full lock is the wrong
|
||||
trade for a few hundred MB of permanent history, so the choice is between one more kind of build-copy
|
||||
edit for the 209 (optional dependencies no wasm32 feature turns on) and keeping the vendor tree off
|
||||
refactor altogether.
|
||||
- (a) History per rebuild if the WASM stays committed: 16,720,159 bytes (about 15.9 MiB) packed. The
|
||||
release build writes four files, 44,142,800 bytes raw: WASM 43,957,877, JS 164,941, HTML 1,604 and an
|
||||
icon 18,378. Gyro wrote those four blobs into a scratch bare repository (git 2.55.0, `hash-object -w`,
|
||||
then `pack-objects --stdout --threads 4`, no gc) and measured the pack. Receipts:
|
||||
`measurement-output-pack.json` and `full-lock-release-measurement.json` in Gyro's packet. Each later
|
||||
rebuild that changes the WASM adds roughly that much again, because compiled WASM deltas badly
|
||||
against the previous build. Ten rebuilds would be about 160 MiB, more than today's whole pack.
|
||||
- (b) Could the release build make the WASM itself, so only source is committed? Facts so far:
|
||||
- `scripts/stack-copy.sh` builds nothing from source. It `git archive`s COPY_PATHS (:29-41, :72) and
|
||||
runs `npm ci --no-audit --no-fund` in the copy (:74), which needs the npm registry. So a release copy
|
||||
is already not offline today.
|
||||
- The pinned toolchain is Rust/Cargo 1.99.1 with the wasm32-unknown-unknown target, Trunk 0.22.0,
|
||||
wasm-bindgen 0.2.129 and wasm-opt 131 (W2 brief, S0). This host's PATH has Arch's rustc and cargo
|
||||
1.98.0 (not the pin), no rustup, and no trunk, wasm-bindgen or wasm-opt. A release host would need
|
||||
all five pinned and hash-checked. That's an install row of its own, and it touches infra on .53/.54,
|
||||
which is Jason's.
|
||||
- Build time: 172 to 393 s per release build in Louie's and S0's baseline runs. Gyro's offline
|
||||
release build took 397.9 s from a fresh target directory at nice 19, 4 CPUs, 4 Cargo jobs and 4
|
||||
Binaryen cores. A first attempt (388.8 s) started at nice 15, inherited from the harness, so it
|
||||
doesn't count. That's about 6.6 minutes per activation on this host, if the release builds it.
|
||||
- No network: the build ran in an isolated network namespace with no external interface, Cargo and
|
||||
Trunk set offline and update checks off, and exit 0. Nothing was fetched or installed. It used the
|
||||
full-lock vendor set extracted in scratch, not the 209-package tree, so it proves an offline build
|
||||
from vendored source works, not that the 209 alone suffice.
|
||||
- Gyro also saw that the resolver used 14 packages outside the compiled closure (14,523,104 bytes,
|
||||
`measurement-resolution-extra-packages.json`). That suggests a vendor set near 209 plus those 14
|
||||
might resolve without the manifest edits. Nobody has tried it. This timing isn't restricted-adapter
|
||||
or CSP evidence (M1 to M14).
|
||||
- Precedent: row 154's editor bundle commits the built `packages/webui/src/public/editor.js`
|
||||
(758,420 bytes of text) next to its third_party source, and `editor-provenance.test.mjs` rebuilds it
|
||||
with the pinned esbuild on the landing gate (`MOSAIC_REQUIRE_EDITOR_BUILD=1`). W2 committing its WASM
|
||||
and proving a rebuild on the gate follows that pattern. Building in stack-release.sh would be new.
|
||||
|
||||
## The 209-plus-14 option, measured (mos-1574)
|
||||
|
||||
Mos asked for this option to be measured before Jason chooses. Everything ran in scratch and nothing is
|
||||
on refactor. Gyro's packet: gyro/row310 tip 1716f5a22, `agents/gyro/work/queue-310/BUILD.md` section
|
||||
"223-source measurement" (line 498).
|
||||
|
||||
- Size: the 223 packages are 11,201 files, 145,260,334 bytes raw, 26,538,894 bytes packed. The 14 extras
|
||||
alone are 702 files, 14,523,104 bytes raw, 1,720,566 bytes packed. Compare the full lock at 614 MB.
|
||||
- Resolution: the existing build-copy filter script, unchanged, gives 31 changed manifests. With it,
|
||||
cargo resolves offline only after writing its own lock. Against WordCraft's pinned lock (558 entries,
|
||||
sha256 06b745e4...) that lock removes 332 entries and adds or changes none (name, version, source,
|
||||
checksum), so it's a strict subset. The pinned lock itself is untouched.
|
||||
- Build: it builds. Gyro's offline release build with the pruned lock under --frozen/--locked exited 0
|
||||
in 380.7 s (gyro-0018, 06:38Z), with the same settings as the full-lock run (nice 19, 4 jobs, isolated
|
||||
netns, fresh directories, no retry used). S0's root closures matched before compiling: 214 normal and
|
||||
224 normal plus build, including 15 local. Outputs: WASM 43,952,335 bytes, JS 164,941, HTML 1,604,
|
||||
icon 18,378 (`measurement-223-build.json`). The WASM is 5,542 bytes smaller than the full-lock build's
|
||||
and its sha256 differs (0322beb9... against c1c15050...). The two builds had different vendor sets, so
|
||||
this proves nothing either way about reproducibility. A repeat build of one input set would.
|
||||
- Source and notice review of the 14 (Gladstone, Codex, gladstone-0026, 06:37Z, not a verdict): all 14
|
||||
archives match the pinned lock's checksums byte for byte, and the extract matches the archives except
|
||||
three filtered Cargo.toml files. 10 pass. 4 have notice concerns to settle before adoption:
|
||||
- cursor-icon 1.2.0 and winit 0.30.13 apply W3C license terms inside source files (src/lib.rs,
|
||||
src/keyboard.rs), which the filename-based notice collector missed. They need adding to M13.
|
||||
- dpi 0.1.2 is "Apache-2.0 AND MIT", not an expression on S0's list. Both texts must stay.
|
||||
- spirv 0.4.0 declares Apache-2.0 but its archive has no license text, so the standard text has to be
|
||||
supplied under M13.
|
||||
- winit also carries four example PNGs (under 3 KB each) and a Web Worker path. Neither is in the
|
||||
measured build's closure, but the binary inventory must list the PNGs and the Worker source isn't
|
||||
cleared for runtime use.
|
||||
- Notice follow-up (gyro-0019 at 9522d2356, Gladstone's recheck gladstone-0027, 06:44Z): the four gaps
|
||||
are now collected as evidence. The W3C terms for cursor-icon and winit are in the notice bundle with
|
||||
their source hashes, both dpi texts and the libm headers stay, spirv maps WordCraft's pinned
|
||||
LICENSE-APACHE text explicitly, and winit's four PNGs are inventoried. Gladstone found no further
|
||||
gaps. What stays open is policy: the W3C form isn't among S0's accepted licenses, and the PNGs and
|
||||
the Worker source need explicit classification if adopted.
|
||||
- What adopting it would mean: commit a cargo-pruned lock in place of WordCraft's (a packaging change
|
||||
for S0's reviewer), the 14 packages' source, the collected notices, and an owner decision on the W3C
|
||||
license form.
|
||||
|
||||
## What Jason is asked
|
||||
|
||||
1. Whether the roughly 260 MB of raw text and source (W1 plus W2's inputs) goes into permanent history,
|
||||
given the packed numbers above. For W2 that now includes which vendor tree: the 209 reviewed
|
||||
packages with build-copy manifest edits (blocked at glifo, would need a further kind of edit), the
|
||||
209 plus the 14 the resolver needed (measured above: 145 MB raw, 26.5 MB packed, a pruned lock and
|
||||
four notice fixes), or the full 538-record lock (614 MB extracted, 329 more packages to review). I
|
||||
recommend 209 plus 14.
|
||||
2. For W2's output, one of: commit the WASM and rebuild-check it on the landing gate (the row 154
|
||||
pattern, adding (a) per rebuild), or build it during release (needs the toolchain on every release
|
||||
host, adds the build time per activation, and stack-copy.sh changes).
|
||||
3. Option C from sage-3978 (Git LFS or an off-tree artifact store) stays open if neither cost is
|
||||
acceptable.
|
||||
@@ -1625,3 +1625,4 @@ are never rewritten or removed; corrections are new entries.
|
||||
2026-10-24T04:29Z | Duckworth (Opus 5.5) | row 208 (193d) second review, guards only, on 4a4bef6c1 (sage-3934) | APPROVE (static), no bus change, every hidden control has a broker refusal, grant hint per-grant and self-only, N1 5 s poll (duckworth-0302) |
|
||||
2026-10-24T04:27Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row265 / #1770 C2-09b rooms | Goldie owns build, Boyd reviews under sage-3915. New goldie/row265 worktree at ec86f2c84, claim rev3814; room verbs/parser/new tests prepared, no old tests changed. Sage-3932 slot: new parser14/14 and rooms18/18 passed; whole bus in progress before refusal mutants, stop rule active.
|
||||
2026-10-24T04:35Z | Goldie (a13dd6d5-5e73-4b7d-8318-02abeff24940) | Row265 / #1770 verified | First slot stopped at project-runtime import pin. mos-1567 narrowly authorizes pin and pure-source mount correction. sage-3942 rerun: sandbox10/10, parser15/15, rooms18/18, bus957/957, twelve refusal mutants killed/restored SHA71b32ee4, parser15/15 + rooms18/18 again. Slot returned goldie-0034; no further test runs. Boyd reviews RUNNER_FILES alongside rooms; own-branch push/claimant in-review next.
|
||||
2026-10-24T07:18:53Z | Sage (1ef1e4f8) | Row 327 / #1825 push guard landed (mos-1572 option A, mos-1575) | Gate went red 06:57Z on an empty canonical node_modules (Glomgold's webui npm ci through a link to canonical, glomgold-0011); rerun with its own npm ci GREEN 07:15:49Z on 1bd214e38; pushed 68c08b4e7 07:18:03Z; #1825 closed c31530; canonical restored (128 packages); brief template rule 4db23a51f | landed
|
||||
@@ -1365,3 +1365,28 @@ public sign-in smoke and a forged-loopback 401 check; rollback drops flags
|
||||
before middleware. Same-uid callers can still read the file; separate service
|
||||
uid is Jason's question on #385. The Authentik allowed-uid file remains an
|
||||
identity input and is never reused for proof.
|
||||
|
||||
## `scripts/push-check.sh` — verify a prospective push
|
||||
|
||||
`scripts/push-check.sh SHA EXPECTED_LOG [--row N]... [--base-ref REF] [--fetch]`
|
||||
checks the repository at the caller's cwd; it never pushes or writes the queue.
|
||||
Git checks cover the full repository even from a subdirectory; EXPECTED_LOG is
|
||||
resolved relative to the original caller. Non-UTF-8 git paths refuse with exit 2.
|
||||
EXPECTED_LOG must contain exactly `git log --oneline BASE..SHA` (including its
|
||||
newlines). BASE defaults to `refs/remotes/origin/refactor`; `--base-ref` overrides
|
||||
it. Only `--fetch` fetches origin, before resolving BASE.
|
||||
|
||||
Success prints `LOG_OK`, `FF_OK`, `NO_BINARY` or `BINARY_REVIEWED n`,
|
||||
`SECRETS_CLEAN`, `NO_ATTRIBUTION`, then `PUSH_READY BASE..SHA` as the last line.
|
||||
A changed present blob counts as binary when numstat marks it binary or its
|
||||
first 8000 committed bytes contain NUL, regardless of git attributes. Each
|
||||
present binary needs its committed blob sha256 and exact path in an approved
|
||||
latest-round candidate from a supplied row. A `changes` receipt blocks that round.
|
||||
Commit candidates authorize only changed paths with matching bytes and mode.
|
||||
Deleted binaries need no review. Queue reads retain canonical-checkout checks
|
||||
and refuse unconfirmed durable tails with exit 2.
|
||||
|
||||
Exit 0 means ready; 1 means refusal (`STOP reason`, always the last line); 2 means
|
||||
usage or environment error. A refusal never prints later success tokens.
|
||||
`timeout -k 10 240 bash scripts/test-push-check.sh` exercises isolated scratch
|
||||
repositories and the six required mutants; set TMPDIR to a scratch filesystem.
|
||||
@@ -101,3 +101,13 @@ if git log --format=%B "$BASE..$SHA" | grep -iqE 'co-authored-by|generated with'
|
||||
echo NO_ATTRIBUTION
|
||||
echo "PUSH_READY $BASE..$SHA"
|
||||
```
|
||||
|
||||
### Amendment 1 (Dewey's landing review, dewey-0955)
|
||||
|
||||
`git diff --numstat` follows git attributes, so a `diff` attribute (from the tree's `.gitattributes`,
|
||||
`.git/info/attributes` or `core.attributesFile`) can make a binary blob count as text. Item 2 changes.
|
||||
A path is binary when numstat marks it binary, or when it exists at SHA and its blob (`git cat-file
|
||||
blob SHA:path`) has a NUL byte in its first 8000 bytes, git's own rule. Attributes can add binaries
|
||||
but never remove them. Tests add scratch cases for each of the three attribute sources, each with no
|
||||
`--row` and each expecting `STOP binary`. A sixth mutant drops the NUL scan. Also, when the queue read
|
||||
returns code 0 with an unconfirmed-tail note, the check stops (fail closed), with a test.
|
||||
@@ -66,6 +66,14 @@ that prove the claim. Or keep it off refactor, in the seat's scratch, and
|
||||
cite its path and sha256 in BUILD.md (mos-1570, after row 265's 22 gzip
|
||||
logs).
|
||||
|
||||
Builders never run npm or remove files in the canonical checkout
|
||||
(`/mnt/storage/src/mosaic-stack`), only in their own worktree. Every gate,
|
||||
the builder's and the lead's landing gate, runs its own
|
||||
`npm ci --ignore-scripts --no-audit --no-fund` in its worktree root and in
|
||||
`packages/webui`, and never links to canonical node_modules (mos-1575,
|
||||
after canonical node_modules was found empty at 05:45:15Z 10-24 and row
|
||||
327's gate went red on it).
|
||||
|
||||
### Handoffs
|
||||
|
||||
Every seat hands off directly (DECISIONS Q210, mos-1221). When the owner
|
||||
|
||||
+4
-3
@@ -33,7 +33,7 @@ Gaps found while working go to `docs/plans/DEFERRED.md`, not here.
|
||||
|
||||
<!-- mosaic-queue:begin -->
|
||||
|
||||
Generated from `docs/plans/queue.json` revision 3844 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
|
||||
Generated from `docs/plans/queue.json` revision 3885 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
|
||||
|
||||
| # | Piece | Owner | Issues | State | After | Gate | Brief | Note |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
@@ -345,8 +345,8 @@ Generated from `docs/plans/queue.json` revision 3844 by `queue render`. Do not e
|
||||
| 306 | A revoked run's late bus call stops it instead of exiting 22 (295 N1) | morgana; reviewers dewey | #1813 | done | 272 done | Dewey approves the design, then the manifest; bus, harness and cli suites and every scripts/test-*.sh green on Sage's gate (sage) | `docs/plans/2026-10-23_gate-env-rows.md` § Brief: a revoked run's late bus call stops it instead of exiting 22 (295 N1) | Landed 2d0318a44 on 4874d1e83: 33d3844a0..683833205 replayed, 4 patch-ids equal (a1960f36 55fadbc3 af1a3d88 35cf81a3), range-diff all =. Dewey APPROVE c31206 rev 3596 on 683833205. access-revoke.test numstat 7 7 (mos-1463). Sage gate 16:33Z: every package suite 0 fail 0 skipped (bus 894, cli 942, plugins 119, webui 392, business 231, harness 151), every scripts/test-*.sh rc 0, M1-M3 each killed on every named file, restores equal. N1/N2 to DEFERRED next. |
|
||||
| 307 | Row 270's pi compaction reading moves behind the harness kind (REQ-PLUG-2, Vikunja 598) | gizmoduck; reviewers duckworth | #1814 | done | — | Duckworth approves the design, then the manifest; bus, cli, plugins (0 failures), webui and business suites, every scripts/test-*.sh green and the mutants killed on Sage's gate (sage) | `docs/plans/2026-10-23_harness-kind-compactions.md` § Brief: row 270's pi compaction reading moves behind the harness kind (Vikunja 598) | LANDED d243f6b25: 6 row commits replayed onto 8dc132bec, every non-log patch-id equal, candidate paths = 6fdfe3790 (manifest 17c9f8d9). Duckworth APPROVE rev 3586 c31189 round 1, C2 met. Sage gate: bus, cli 0 skipped, plugins fully green incl harness.test.mjs:192, webui, business, seat, every scripts/test-*.sh; M1-M4 killed. P19 598 commented. DEFERRED: 307 N1 store-unreadable not sticky. N3 doc-only BUILD.md red list one short, next BUILD.md change. |
|
||||
| 308 | PRD 1.17 Case 2 story files are DOCX (Q235 to Q237, Q239) | gosalyn; reviewers webby | #1815 | done | 291 done | Webby checks the PRD diff against DRAFT-d9.md at 4607e15b8 one instruction at a time and that nothing else changed. No new Jason question (Q239 approved the text). tasks whole, bus whole, cli stack-copy.test and every scripts/test-*.sh green on Sage's gate. Mos gets the landing commit (mos-1470). (sage) | `docs/plans/2026-10-23_prd-1-17-brief.md` § PRD 1.17 Case 2 story files are DOCX (Q235 to Q237, Q239) | Landed 30e6bb680: Gosalyn 45fe7b3ce replayed onto 7defb58e9, patch-id 4658c831 equal, range-diff =, four blobs equal. Webby r1 APPROVE c31224 rev 3606, d9 at 4607e15b8 applied word for word. Sage gate 17:19Z: every package suite 0 fail 0 skipped (tasks 100, bus 894, cli 942, plugins 119, webui 392), every scripts/test-*.sh rc 0. Mos gets the sha (mos-1470). 570/571 build rows next. |
|
||||
| 309 | 571 W1: reviewed local thesaurus data (post-10-27) | unassigned | — | queued | — | Independent Opus 5.5 reviewer, not the author, approves the manifest; the brief section's Gate and G1 pass on Sage's slot: touched test files whole, affected package suites green, named mutants killed. Post-10-27; MVP rows keep slot priority (sage) | `docs/plans/2026-10-21_q195-writing-editor.md` § W1 reviewed local thesaurus data | — |
|
||||
| 310 | 571 W2: WordCraft source import and reproducible restricted web build (post-10-27) | unassigned | — | queued | — | Independent Opus 5.5 reviewer, not the author, approves the manifest; the brief section's Gate and G1 pass on Sage's slot: touched test files whole, affected package suites green, named mutants killed. Post-10-27; MVP rows keep slot priority (sage) | `docs/plans/2026-10-21_q195-writing-editor.md` § W2 WordCraft source import and reproducible restricted web build | — |
|
||||
| 309 | 571 W1: reviewed local thesaurus data (post-10-27) | glomgold; reviewers boyd, duckworth | #1823 | in-review, round 1, request posted | — | Independent Opus 5.5 reviewer, not the author, approves the manifest; the brief section's Gate and G1 pass on Sage's slot: touched test files whole, affected package suites green, named mutants killed. Post-10-27; MVP rows keep slot priority (sage) | `docs/plans/2026-10-21_q195-writing-editor.md` § W1 reviewed local thesaurus data | — |
|
||||
| 310 | 571 W2: WordCraft source import and reproducible restricted web build (post-10-27) | gyro; reviewers filbert, gladstone | #1824 | in-progress | — | Independent Opus 5.5 reviewer, not the author, approves the manifest; the brief section's Gate and G1 pass on Sage's slot: touched test files whole, affected package suites green, named mutants killed. Post-10-27; MVP rows keep slot priority (sage) | `docs/plans/2026-10-21_q195-writing-editor.md` § W2 WordCraft source import and reproducible restricted web build | — |
|
||||
| 311 | 571 W3a: bus pins and human-only editor settings (post-10-27) | unassigned | — | queued | — | Independent Opus 5.5 reviewer, not the author, approves the manifest; the brief section's Gate and G1 pass on Sage's slot: touched test files whole, affected package suites green, named mutants killed. Post-10-27; MVP rows keep slot priority (sage) | `docs/plans/2026-10-21_q195-writing-editor.md` § W3a bus pins and human-only editor settings | — |
|
||||
| 312 | 571 W3b: human DOCX store and instant commits (post-10-27) | unassigned | — | queued | 311 done | Independent Opus 5.5 reviewer, not the author, approves the manifest; the brief section's Gate and G1 pass on Sage's slot: touched test files whole, affected package suites green, named mutants killed. Post-10-27; MVP rows keep slot priority (sage) | `docs/plans/2026-10-21_q195-writing-editor.md` § W3b human DOCX store and instant commits | — |
|
||||
| 313 | 571 W6: owned browser bridge and editor-only route (post-10-27) | unassigned | — | queued | 309 done, 310 done, 312 done | Independent Opus 5.5 reviewer, not the author, approves the manifest; the brief section's Gate and G1 pass on Sage's slot: touched test files whole, affected package suites green, named mutants killed. Post-10-27; MVP rows keep slot priority (sage) | `docs/plans/2026-10-21_q195-writing-editor.md` § W6 owned browser bridge and editor-only route | — |
|
||||
@@ -363,6 +363,7 @@ Generated from `docs/plans/queue.json` revision 3844 by `queue render`. Do not e
|
||||
| 324 | Tracker optional in the business file (Q247) | goldie; reviewers duckworth | #1820 | done | — | New tests only. Absent tracker: loader and validate pass, the bus boots with no credential-file error and no @sync. Present tracker unchanged. A mutant that skips the presence check is killed. Then whole touched test files, then the full landing gate. (sage) | `docs/plans/2026-10-23_optional-tracker-brief.md` § Brief: tracker becomes optional in the business file | LANDED 7e59ba8b3 + evidence dbdfcfd52 (ff from 3569bf54f), pushed with tip dd6cb5872 00:06:58Z 10-24. Duckworth APPROVE c31470 rev 3770 (static, N1 doc only). Goldie: new 5/5 and 4/4, business.test 21/21, presence mutant killed. Sage full gate GREEN 00:05:49Z on 64ea54083: every package suite 0 fail 0 skip (business 254, cli 986, bus 924, plugins 119, webui 411), every scripts/test-*.sh rc 0. Live unchanged 45dca69f6. Run B N1 for ops-01 P2/P3 (mos-1558). |
|
||||
| 325 | Case 1 business file: re-key project to stack to match .mosaic/project.json, plus a project-file test (#414 stage 2, ops-0288, mos-1563) | goldie; reviewers gladstone | #1821 | done | — | New test file and packages/business whole on CLEAR; one key mutant killed; Gladstone review; Sage full landing gate (mos-1455) (sage) | `docs/plans/2026-10-24_case1-project-key-brief.md` § Brief: re-key the Case 1 project to stack | LANDED c5f96836d (ff from 99930362e), tip 3d2569ce2. Gladstone approve c31484 rev 3788 on c5f96836d. Sage gate green 01:31Z on c5f96836d: all package suites 0 fail (business 264, cli 986), every scripts/test-*.sh rc 0. Project key is now stack, matching .mosaic/project.json; new case1-project-key test checks the real project file. Old-test line :70 under mos-1564. |
|
||||
| 326 | Host run sheet: five operator gaps (B2-2 proof format, 323 N2, pre-install key check, failed-first-install retry, backup SHA256SUMS form) | goldie; reviewers gladstone | #1822 | done | — | Gladstone approves the manifest; every new claim cites its line; no test reads the sheet; Sage's full landing gate green (sage) | `docs/plans/2026-10-24_run-sheet-doc-brief.md` § Brief: five run-sheet gaps | LANDED 33f49f7d5 (cherry-pick of a1b13c60e, patch-id match, sheet e4c14516) in f58049de0 with row 208 on one full gate 04:36-04:51Z, all suites green. Gladstone c31489. #1822 closed c31507. B2-2 closes in DEFERRED. Bundle 9fc7754a. |
|
||||
| 327 | Push check in the repository: scripts/push-check.sh passes a binary only when an approved review candidate lists its path and sha256, plus scripts/test-push-check.sh (#1825, mos-1572 A) | bradford; reviewers dewey, poe | #1825 | done | — | Poe gives the first Codex review, Dewey reviews the landing candidate; scripts/test-push-check.sh and every other scripts/test-*.sh green on Sage's gate; the mutants killed; Sage's next push runs through scripts/push-check.sh (sage) | `docs/plans/2026-10-24_push-guard-binary-manifest.md` § Brief: push-check.sh passes a binary only when the reviewed candidate lists its path and sha256 | LANDED 68c08b4e7 (pushed 07:18:03Z 10-24): 3 commits on 6a5e12511, blobs equal round 3 d858c9f8b; Poe c31525 rev 3882, Dewey c31526 rev 3883; gate GREEN 07:15:49Z with its own npm ci (first run red on empty canonical node_modules, sage-4023); 6 push-check mutants KILLED; next push 42ba0f5bc ran through scripts/push-check.sh --fetch, PUSH_READY; #1825 closed c31530. |
|
||||
|
||||
<!-- mosaic-queue:end -->
|
||||
|
||||
|
||||
+1340
-22
File diff suppressed because it is too large.
Load diff
Executable
+127
@@ -0,0 +1,127 @@
|
||||
#!/bin/bash
|
||||
# Check an explicit committed push range. Fetch is opt-in; never pushes.
|
||||
command -v node >/dev/null 2>&1 || { echo 'STOP node unavailable'; exit 2; }
|
||||
exec node --input-type=module - "$@" <<'JS'
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { join } from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
const stop = (reason, code = 1) => { console.log(`STOP ${reason}`); process.exit(code); };
|
||||
let gitRoot = process.cwd();
|
||||
const git = (...args) => {
|
||||
const r = spawnSync('git', args, { cwd: gitRoot, maxBuffer: 256 * 1024 * 1024 });
|
||||
if (r.error || r.status !== 0) throw new Error('git');
|
||||
return r.stdout;
|
||||
};
|
||||
// Git paths must round-trip as UTF-8 before comparing them with queue paths.
|
||||
const pathsText = bytes => new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes);
|
||||
const shownPath = path => /[\r\n]/.test(path) ? JSON.stringify(path) : path;
|
||||
const hash = bytes => createHash('sha256').update(bytes).digest('hex');
|
||||
const text = (...args) => git(...args).toString('utf8').trimEnd();
|
||||
const resolve = ref => text('rev-parse', '--verify', '--end-of-options', `${ref}^{commit}`);
|
||||
const argv = process.argv.slice(2);
|
||||
if (argv.length < 2) stop('usage: push-check.sh SHA EXPECTED_LOG [--row N]... [--base-ref REF] [--fetch]', 2);
|
||||
const [ref, expectedFile] = argv.splice(0, 2);
|
||||
let baseRef = 'refs/remotes/origin/refactor', fetch = false;
|
||||
const rowIds = new Set();
|
||||
while (argv.length) {
|
||||
const flag = argv.shift();
|
||||
if (flag === '--fetch' && !fetch) fetch = true;
|
||||
else if (flag === '--row' && /^[1-9][0-9]*$/.test(argv[0] ?? '') && Number.isSafeInteger(Number(argv[0]))) rowIds.add(Number(argv.shift()));
|
||||
else if (flag === '--base-ref' && argv[0] && !argv[0].startsWith('-')) baseRef = argv.shift();
|
||||
else stop('usage', 2);
|
||||
}
|
||||
let sha, base, expected;
|
||||
try {
|
||||
// Resolve the log against the original caller, then anchor every Git command
|
||||
// at the repository root. Queue reads retain their production cwd checks.
|
||||
expected = readFileSync(expectedFile);
|
||||
gitRoot = text('rev-parse', '--show-toplevel');
|
||||
sha = resolve(ref);
|
||||
}
|
||||
catch { stop('sha or expected log unavailable', 2); }
|
||||
if (fetch) { try { git('fetch', '-q', 'origin'); } catch { stop('fetch', 2); } }
|
||||
try { base = resolve(baseRef); } catch { stop('base ref unavailable', 2); }
|
||||
try {
|
||||
if (!git('log', '--oneline', `${base}..${sha}`).equals(expected)) stop('log');
|
||||
console.log('LOG_OK');
|
||||
try { git('merge-base', '--is-ancestor', base, sha); } catch { stop('ff'); }
|
||||
console.log('FF_OK');
|
||||
|
||||
// --no-renames keeps -z numstat to exactly one path per record. Never parse
|
||||
// quoted path output or consult working-tree bytes.
|
||||
const changedPaths = pathsText(git('diff', '--no-relative', '--no-color', '--no-ext-diff', '--no-textconv', '--no-renames', '--numstat', '-z', base, sha))
|
||||
.split('\0').filter(Boolean).map(record => {
|
||||
const first = record.indexOf('\t'), second = record.indexOf('\t', first + 1);
|
||||
return { path: record.slice(second + 1), binary: record.startsWith('-\t-\t') };
|
||||
});
|
||||
const tree = commit => {
|
||||
const entries = new Map();
|
||||
for (const entry of pathsText(git('ls-tree', '--full-tree', '-r', '-z', commit)).split('\0').filter(Boolean)) {
|
||||
const tab = entry.indexOf('\t');
|
||||
const [mode, type, oid] = entry.slice(0, tab).split(' ');
|
||||
entries.set(entry.slice(tab + 1), { mode, type, oid });
|
||||
}
|
||||
return entries;
|
||||
};
|
||||
const have = tree(sha);
|
||||
// Attributes can force a binary to appear as text in numstat. Inspect raw
|
||||
// committed bytes as well, following Git's first-8000-byte NUL rule.
|
||||
const present = changedPaths.filter(({ path, binary }) => {
|
||||
const entry = have.get(path);
|
||||
if (!entry || entry.type !== 'blob') return false;
|
||||
return binary || git('cat-file', 'blob', entry.oid).subarray(0, 8000).includes(0);
|
||||
}).map(({ path }) => path);
|
||||
let approved = [];
|
||||
if (present.length && rowIds.size) {
|
||||
let rows;
|
||||
try {
|
||||
const root = text('rev-parse', '--show-toplevel');
|
||||
const { rows: readRows } = await import(pathToFileURL(join(root, 'packages/queue/src/store.mjs')).href);
|
||||
const result = readRows({ cwd: process.cwd(), env: process.env });
|
||||
if (result.code !== 0) throw new Error('queue');
|
||||
if (result.err.some(note => /unconfirmed tail|not confirmed durable/.test(note))) stop('queue unconfirmed tail', 2);
|
||||
rows = result.rows;
|
||||
for (const id of rowIds) if (!rows.some(r => r.id === id)) throw new Error('row');
|
||||
} catch { stop('queue unavailable or invalid', 2); }
|
||||
for (const row of rows.filter(r => rowIds.has(r.id))) {
|
||||
const round = row.review?.rounds.at(-1);
|
||||
if (!round || round.receipts?.some(r => r.verdict === 'changes')) continue;
|
||||
if (!round.receipts?.some(r => r.verdict === 'approve' && r.candidate === round.candidate.digest)) continue;
|
||||
approved.push(round.candidate);
|
||||
}
|
||||
}
|
||||
const candidates = new Map();
|
||||
for (const c of approved) {
|
||||
if (c.kind !== 'commit') continue;
|
||||
const candidateTree = tree(c.digest);
|
||||
let parent;
|
||||
try { parent = text('rev-parse', '--verify', `${c.digest}^1`); }
|
||||
catch { parent = text('hash-object', '-t', 'tree', '/dev/null'); }
|
||||
const changed = new Set(pathsText(git('diff-tree', '--no-relative', '-r', '--no-renames', '--name-only', '-z', parent, c.digest)).split('\0').filter(Boolean));
|
||||
candidates.set(c.digest, { candidateTree, changed });
|
||||
}
|
||||
for (const path of present) {
|
||||
const h = have.get(path);
|
||||
const digest = hash(git('cat-file', 'blob', h.oid));
|
||||
const matches = approved.some(c => {
|
||||
if (c.kind === 'manifest') return c.text.split('\n').includes(`${digest} ${path}`);
|
||||
const { candidateTree, changed } = candidates.get(c.digest);
|
||||
const entry = candidateTree.get(path);
|
||||
return changed.has(path) && entry?.type === 'blob' && entry.mode === h.mode && hash(git('cat-file', 'blob', entry.oid)) === digest;
|
||||
});
|
||||
if (!matches) stop(`binary ${shownPath(path)} ${digest} not in a reviewed candidate`);
|
||||
}
|
||||
console.log(present.length ? `BINARY_REVIEWED ${present.length}` : 'NO_BINARY');
|
||||
|
||||
const diff = git('diff', '--no-relative', '--no-color', '--no-ext-diff', '--no-textconv', base, sha).toString('utf8');
|
||||
const secret = /BEGIN [A-Z ]*PRIVATE KEY|ghp_[A-Za-z0-9]{20}|sk-ant-[A-Za-z0-9-]{10}|xox[bp]-|AKIA[0-9A-Z]{16}|(token|secret|password)["' ]*[:=] *["'][A-Za-z0-9_\-]{24,}/i;
|
||||
if (diff.split('\n').some(line => line.startsWith('+') && secret.test(line))) stop('secrets');
|
||||
console.log('SECRETS_CLEAN');
|
||||
if (/co-authored-by|generated with/i.test(text('log', '--format=%B', `${base}..${sha}`))) stop('attribution');
|
||||
console.log('NO_ATTRIBUTION');
|
||||
console.log(`PUSH_READY ${base}..${sha}`);
|
||||
} catch { stop('git or environment error', 2); }
|
||||
JS
|
||||
Executable
+256
@@ -0,0 +1,256 @@
|
||||
#!/bin/bash
|
||||
# All repositories, queue writes, remotes and mutants are disposable scratch.
|
||||
set -euo pipefail
|
||||
ROOT=$(cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
exec node --input-type=module - "$ROOT" "$@" <<'JS'
|
||||
import assert from 'node:assert/strict';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { readFileSync, writeFileSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { createHash } from 'node:crypto';
|
||||
const root = process.argv[2];
|
||||
const { scratchRepo, genesisCommitted, cli } = await import(pathToFileURL(join(root, 'packages/queue/tests/helpers.mjs')));
|
||||
const target = process.argv[3] ?? join(root, 'scripts/push-check.sh');
|
||||
const sha256 = bytes => createHash('sha256').update(bytes).digest('hex');
|
||||
const binary = Buffer.from([0, 1, 2, 3]);
|
||||
const digest = sha256(binary);
|
||||
const cleanups = [];
|
||||
let count = 0;
|
||||
function fixture() {
|
||||
const repo = scratchRepo({ after: fn => cleanups.push(fn) });
|
||||
genesisCommitted(repo);
|
||||
const run = (args, by = 'darkwing', allow = false) => {
|
||||
const r = cli(repo, args, { by });
|
||||
assert.ok(r.code === 0 || allow, r.err);
|
||||
return r;
|
||||
};
|
||||
run(['move', '6', 'blocked', '--reason', 'paused', '--op', 'block-six-0001']);
|
||||
run(['move', '9', 'in-progress', '--op', 'start-nine-0001']);
|
||||
run(['set', '9', 'reviewers', 'dewey,filbert', '--op', 'set-reviewers-0001'], 'sage');
|
||||
// A real local bare remote and clone, with no network or origin access.
|
||||
const bare = join(repo.base, 'remote.git');
|
||||
repo.g('init', '--bare', '-q', bare);
|
||||
repo.g('clone', '-q', bare, join(repo.base, 'clone'));
|
||||
repo.g('remote', 'add', 'origin', bare);
|
||||
const base = repo.g('rev-parse', 'HEAD').trim();
|
||||
repo.g('update-ref', 'refs/remotes/origin/refactor', base);
|
||||
const commit = (path, bytes, message = 'content') => {
|
||||
if (bytes === null) repo.g('rm', '--', path);
|
||||
else { writeFileSync(join(repo.root, path), bytes); repo.g('add', '--', path); }
|
||||
repo.g('commit', '-q', '-m', message);
|
||||
return repo.g('rev-parse', 'HEAD').trim();
|
||||
};
|
||||
const check = (sha, extra = [], expectedCode = 0, reason = null, script = target, cwd = repo.root) => {
|
||||
const log = join(repo.base, 'expected.log');
|
||||
writeFileSync(log, repo.g('log', '--oneline', `${base}..${sha}`));
|
||||
const r = spawnSync('bash', [script, sha, log, ...extra], { cwd, env: repo.env, encoding: 'utf8' });
|
||||
assert.equal(r.status, expectedCode, `${r.stdout}\n${r.stderr}`);
|
||||
const lines = r.stdout.trim().split('\n');
|
||||
if (expectedCode === 0) assert.match(lines.at(-1), /^PUSH_READY /);
|
||||
else {
|
||||
assert.equal(lines.filter(s => s.startsWith('STOP ')).length, 1, r.stdout);
|
||||
assert.match(lines.at(-1), reason ?? /^STOP /);
|
||||
assert.ok(!r.stdout.includes('PUSH_READY'), r.stdout);
|
||||
}
|
||||
count++;
|
||||
return r.stdout;
|
||||
};
|
||||
let n = 0;
|
||||
const round = (candidate, verdict = 'approve') => {
|
||||
n++;
|
||||
const r = run(['move', '9', 'in-review', '--candidate', candidate, '--op', `review-nine-000${n}`], 'darkwing', true);
|
||||
// Missing dummy transport credentials fails the request, after opening a
|
||||
// valid round. Recording a receipt is cooperative and needs no credential.
|
||||
assert.equal(r.code, 1, r.out + r.err);
|
||||
const row = JSON.parse(run(['show', '9']).out);
|
||||
const c = row.review.rounds.at(-1).candidate.digest;
|
||||
run(['review', 'record', '9', '--verdict', verdict, '--candidate', c, '--comment', String(n), '--op', `verdict-nine-00${n}`], 'filbert');
|
||||
};
|
||||
const manifest = (d = digest, path = 'asset.bin') => {
|
||||
const file = join(repo.base, 'manifest.sha256');
|
||||
writeFileSync(file, `${d} ${path}\n`);
|
||||
return file;
|
||||
};
|
||||
return { repo, run, base, commit, check, round, manifest };
|
||||
}
|
||||
try {
|
||||
{
|
||||
const f = fixture();
|
||||
const sha = f.commit('text.txt', 'hello\n');
|
||||
assert.match(f.check(sha), /NO_BINARY\nSECRETS_CLEAN\nNO_ATTRIBUTION\nPUSH_READY /);
|
||||
f.check(sha, ['--base-ref', f.base]);
|
||||
f.check(sha, ['--bad-option'], 2, /^STOP usage/);
|
||||
// Explicit fetch goes only to the scratch remote.
|
||||
f.check(sha, ['--fetch']);
|
||||
const wrong = join(f.repo.base, 'wrong.log'); writeFileSync(wrong, 'wrong\n');
|
||||
const r = spawnSync('bash', [target, sha, wrong], { cwd: f.repo.root, env: f.repo.env, encoding: 'utf8' });
|
||||
assert.equal(r.status, 1); assert.equal(r.stdout, 'STOP log\n'); count++;
|
||||
f.repo.g('update-ref', 'refs/remotes/origin/refactor', sha);
|
||||
const log = join(f.repo.base, 'empty.log'); writeFileSync(log, '');
|
||||
const ff = spawnSync('bash', [target, f.base, log], { cwd: f.repo.root, env: f.repo.env, encoding: 'utf8' });
|
||||
assert.equal(ff.status, 1); assert.equal(ff.stdout, 'LOG_OK\nSTOP ff\n'); count++;
|
||||
}
|
||||
// B1: attributes may add binary classifications, never hide NUL blobs.
|
||||
for (const source of ['tree', 'info', 'config']) {
|
||||
const f = fixture();
|
||||
if (source === 'tree') f.commit('.gitattributes', '*.bin diff\n');
|
||||
if (source === 'info') writeFileSync(join(f.repo.gitDir, 'info/attributes'), '*.bin diff\n');
|
||||
if (source === 'config') {
|
||||
const attributes = join(f.repo.base, 'attributes'); writeFileSync(attributes, '*.bin diff\n');
|
||||
f.repo.g('config', 'core.attributesFile', attributes);
|
||||
}
|
||||
const sha = f.commit('asset.bin', binary);
|
||||
f.check(sha, [], 1, /^STOP binary asset.bin /);
|
||||
f.round(f.manifest());
|
||||
assert.match(f.check(sha, ['--row', '9']), /BINARY_REVIEWED 1\n/);
|
||||
}
|
||||
// The content rule includes byte 7999, and excludes byte 8000.
|
||||
for (const offset of [7999, 8000]) {
|
||||
const f = fixture(), bytes = Buffer.alloc(8001, 65); bytes[offset] = 0;
|
||||
f.commit('.gitattributes', '*.bin diff\n');
|
||||
const sha = f.commit('asset.bin', bytes);
|
||||
if (offset === 7999) f.check(sha, [], 1, /^STOP binary asset.bin /);
|
||||
else assert.match(f.check(sha), /NO_BINARY\n/);
|
||||
}
|
||||
// N4: an approval beyond the durable witness is visible, not authority.
|
||||
{
|
||||
const f = fixture(), sha = f.commit('asset.bin', binary);
|
||||
const witness = join(f.repo.gitDir, 'mosaic-queue.head');
|
||||
const durable = readFileSync(witness);
|
||||
f.round(f.manifest());
|
||||
writeFileSync(witness, durable);
|
||||
const visible = f.run(['show', '9']);
|
||||
assert.match(visible.err, /not confirmed durable/);
|
||||
f.check(sha, ['--row', '9'], 2, /^STOP queue unconfirmed tail$/);
|
||||
}
|
||||
{
|
||||
const f = fixture(), sha = f.commit('asset.bin', binary);
|
||||
f.check(sha, [], 1, /^STOP binary asset.bin /);
|
||||
f.round(f.manifest('0'.repeat(64)));
|
||||
f.check(sha, ['--row', '9'], 1, /^STOP binary asset.bin /);
|
||||
}
|
||||
{
|
||||
const f = fixture(), sha = f.commit('asset.bin', binary);
|
||||
f.round(f.manifest());
|
||||
writeFileSync(join(f.repo.root, 'asset.bin'), 'working tree differs');
|
||||
assert.match(f.check(sha, ['--row', '9']), /BINARY_REVIEWED 1\n/);
|
||||
f.check(sha, [], 1, /^STOP binary asset.bin /);
|
||||
const other = f.commit('asset.bin', Buffer.from([0, 8, 9]));
|
||||
// The working tree is the approved version; the pushed blob isn't.
|
||||
writeFileSync(join(f.repo.root, 'asset.bin'), binary);
|
||||
f.check(other, ['--row', '9'], 1, /^STOP binary asset.bin /);
|
||||
}
|
||||
{
|
||||
const f = fixture(), sha = f.commit('asset.bin', binary);
|
||||
f.round(sha);
|
||||
assert.match(f.check(sha, ['--row', '9']), /BINARY_REVIEWED 1\n/);
|
||||
const other = f.commit('asset.bin', Buffer.from([0, 7]));
|
||||
f.check(other, ['--row', '9'], 1, /^STOP binary asset.bin /);
|
||||
}
|
||||
{
|
||||
const f = fixture(), sha = f.commit('space tab\tasset.bin', binary);
|
||||
f.round(sha);
|
||||
assert.match(f.check(sha, ['--row', '9']), /BINARY_REVIEWED 1\n/);
|
||||
}
|
||||
{
|
||||
const f = fixture(), sha = f.commit('line\nbreak.bin', binary);
|
||||
f.check(sha, [], 1, /^STOP binary "line\\nbreak.bin" /);
|
||||
writeFileSync(Buffer.concat([Buffer.from(f.repo.root + '/'), Buffer.from([255]), Buffer.from('.bin')]), binary);
|
||||
f.repo.g('add', '-A'); f.repo.g('commit', '-q', '-m', 'invalid UTF-8 path');
|
||||
f.check(f.repo.g('rev-parse', 'HEAD').trim(), [], 2, /^STOP git or environment error$/);
|
||||
}
|
||||
{
|
||||
const f = fixture(), sha = f.commit('asset.bin', binary);
|
||||
const cwd = join(f.repo.root, 'nested'); mkdirSync(cwd);
|
||||
f.repo.g('config', 'diff.relative', 'true');
|
||||
f.check(sha, [], 1, /^STOP binary asset.bin /, target, cwd);
|
||||
f.round(f.manifest());
|
||||
assert.match(f.check(sha, ['--row', '9'], 0, null, target, cwd), /BINARY_REVIEWED 1\n/);
|
||||
const relativeLog = spawnSync('bash', [target, sha, '../../expected.log', '--row', '9'], { cwd, env: f.repo.env, encoding: 'utf8' });
|
||||
assert.equal(relativeLog.status, 0, relativeLog.stdout + relativeLog.stderr);
|
||||
assert.match(relativeLog.stdout.trim().split('\n').at(-1), /^PUSH_READY /); count++;
|
||||
}
|
||||
{
|
||||
const f = fixture(), cwd = join(f.repo.root, 'nested'); mkdirSync(cwd);
|
||||
const sha = f.commit('nested/asset.bin', binary);
|
||||
f.repo.g('config', 'diff.relative', 'true');
|
||||
f.check(sha, [], 1, /^STOP binary nested\/asset.bin /, target, cwd);
|
||||
f.round(sha);
|
||||
assert.match(f.check(sha, ['--row', '9'], 0, null, target, cwd), /BINARY_REVIEWED 1\n/);
|
||||
}
|
||||
{
|
||||
const f = fixture();
|
||||
f.commit('asset.bin', binary);
|
||||
const unrelated = f.commit('text.txt', 'unrelated\n');
|
||||
f.round(unrelated);
|
||||
f.check(unrelated, ['--row', '9'], 1, /^STOP binary asset.bin /);
|
||||
}
|
||||
{
|
||||
const f = fixture(), sha = f.commit('asset.bin', binary);
|
||||
f.round(f.manifest(), 'changes');
|
||||
const row = JSON.parse(f.run(['show', '9']).out);
|
||||
f.run(['review', 'record', '9', '--verdict', 'approve', '--candidate', row.review.rounds.at(-1).candidate.digest, '--comment', '2', '--op', 'approve-dewey-0001'], 'dewey');
|
||||
f.check(sha, ['--row', '9'], 1, /^STOP binary asset.bin /);
|
||||
}
|
||||
{
|
||||
const f = fixture(), sha = f.commit('asset.bin', binary);
|
||||
f.round(f.manifest());
|
||||
f.run(['move', '9', 'in-progress', '--op', 'revise-nine-0001']);
|
||||
f.round(f.manifest('0'.repeat(64)));
|
||||
f.check(sha, ['--row', '9'], 1, /^STOP binary asset.bin /);
|
||||
}
|
||||
{
|
||||
const f = fixture(), sha = f.commit('asset.bin', binary);
|
||||
f.round(f.manifest());
|
||||
// This shape cannot be recorded by the queue. Its read API must reject
|
||||
// tampered receipts before the guard can treat them as authority.
|
||||
const path = f.repo.queuePath;
|
||||
const doc = JSON.parse(readFileSync(path));
|
||||
doc.rows.find(r => r.id === 9).review.rounds.at(-1).receipts[0].candidate = '0'.repeat(64);
|
||||
writeFileSync(path, JSON.stringify(doc));
|
||||
f.check(sha, ['--row', '9'], 2, /^STOP queue unavailable or invalid$/);
|
||||
}
|
||||
{
|
||||
const f = fixture(), added = f.commit('asset.bin', binary);
|
||||
f.repo.g('update-ref', 'refs/remotes/origin/refactor', added);
|
||||
const deleted = f.commit('asset.bin', null);
|
||||
// Explicit base retains the check helper's original expected log range.
|
||||
f.repo.g('update-ref', 'refs/remotes/origin/refactor', f.base);
|
||||
assert.match(f.check(deleted, ['--base-ref', added], 1, /^STOP log$/), /STOP log/);
|
||||
const log = join(f.repo.base, 'delete.log'); writeFileSync(log, f.repo.g('log', '--oneline', `${added}..${deleted}`));
|
||||
const r = spawnSync('bash', [target, deleted, log, '--base-ref', added], { cwd: f.repo.root, env: f.repo.env, encoding: 'utf8' });
|
||||
assert.equal(r.status, 0, r.stdout); assert.match(r.stdout, /NO_BINARY\n/); assert.match(r.stdout.trim().split('\n').at(-1), /^PUSH_READY /); count++;
|
||||
}
|
||||
for (const [message, content, reason, prefix] of [
|
||||
['content', 'password="' + 'a'.repeat(30) + '"\n', 'secrets', 'LOG_OK\nFF_OK\nNO_BINARY\n'],
|
||||
['Generated with example', 'hello\n', 'attribution', 'LOG_OK\nFF_OK\nNO_BINARY\nSECRETS_CLEAN\n'],
|
||||
]) {
|
||||
const f = fixture(), sha = f.commit('text.txt', content, message);
|
||||
assert.equal(f.check(sha, [], 1), `${prefix}STOP ${reason}\n`);
|
||||
}
|
||||
console.log(`PASS push-check ${count} checks`);
|
||||
if (!process.argv[3]) {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'push-check-mutants-'));
|
||||
cleanups.push(() => rmSync(dir, { recursive: true, force: true }));
|
||||
const source = readFileSync(target, 'utf8');
|
||||
const mutants = [
|
||||
['skip-hash', "c.text.split('\\n').includes(`${digest} ${path}`)", "c.text.split('\\n').some(line => line.endsWith(` ${path}`))"],
|
||||
['accept-changes', "round.receipts?.some(r => r.verdict === 'changes')", 'false'],
|
||||
['earlier-round', 'row.review?.rounds.at(-1)', 'row.review?.rounds.at(0)'],
|
||||
['working-tree', "hash(git('cat-file', 'blob', h.oid))", 'hash(readFileSync(path))'],
|
||||
['no-row', 'if (!matches) stop(', 'if (!matches && rowIds.size) stop('],
|
||||
['skip-nul-scan', " || git('cat-file', 'blob', entry.oid).subarray(0, 8000).includes(0)", ''],
|
||||
];
|
||||
for (const [name, before, after] of mutants) {
|
||||
assert.ok(source.includes(before), name);
|
||||
const file = join(dir, `${name}.sh`); writeFileSync(file, source.replace(before, after));
|
||||
const r = spawnSync('timeout', ['-k', '10', '120', 'bash', join(root, 'scripts/test-push-check.sh'), file], { encoding: 'utf8', maxBuffer: 4 << 20 });
|
||||
assert.equal(r.status, 1, `${name} survived or infrastructure failed: ${r.status}\n${r.stdout}\n${r.stderr}`);
|
||||
assert.match(r.stderr, /AssertionError/, r.stderr);
|
||||
console.log(`KILLED ${name}`);
|
||||
}
|
||||
}
|
||||
} finally { for (const fn of cleanups.reverse()) fn(); }
|
||||
JS
|
||||
Reference in new issue
Block a user