Skip to content
307 changes: 212 additions & 95 deletions plugins/avatasia/mmc-trajectory/miniapp/client/index.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions plugins/avatasia/mmc-trajectory/miniapp/node/server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ const GENERATION_PROBE_BYTES = 64 * 1024;
* costs accuracy, not correctness — 256 KB was reading four times what the label needed, on every
* cache miss.
*
* The last clause used to end "for counts nobody treats as exact", and that is what D45 found
* false: the page printed the message count in the session picker (`(3 分钟前 · 10 条)`), so it
* The last clause used to end "for counts nobody treats as exact", and that is what turned out
* to be false: the page printed the message count in the session picker (`(3 分钟前 · 10 条)`), so it
* was treated as exact. On a session whose active file is 1 MB, the first 64 KB held 10 messages
* out of 23,642 — a number that was wrong by three orders of magnitude and carried no hint of it.
* So the prefix stays bounded and the counters it buys are only REPORTED when the prefix was the
Expand Down
4 changes: 2 additions & 2 deletions plugins/avatasia/mmc-trajectory/tests/mutate-cross-layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
("the window bar is presented outside the ledger panel",
' <div class="window-bar" id="window-bar" aria-label="列表视图控制"></div>\n'
' <div class="ledger-body" id="ledger"></div>\n'
' <!-- D44: the two ends of the ledger, and the one reading that says which end the reader is\n'
' <!-- The two ends of the ledger, and the one reading that says which end the reader is\n'
' at. See `.ledger-end` and `.ledger-jump` for why the sentinel is outside #ledger. -->\n'
' <div class="ledger-end" id="ledger-end" aria-hidden="true"></div>\n'
' <!-- No text and no glyph in the markup: buildLedgerJump() fills the arrow in, and the\n'
Expand All @@ -43,7 +43,7 @@
' <button type="button" class="ledger-jump" id="ledger-jump" hidden aria-label="跳到轨迹的最低"></button>\n'
' </section>',
' <div class="ledger-body" id="ledger"></div>\n'
' <!-- D44: the two ends of the ledger, and the one reading that says which end the reader is\n'
' <!-- The two ends of the ledger, and the one reading that says which end the reader is\n'
' at. See `.ledger-end` and `.ledger-jump` for why the sentinel is outside #ledger. -->\n'
' <div class="ledger-end" id="ledger-end" aria-hidden="true"></div>\n'
' <!-- No text and no glyph in the markup: buildLedgerJump() fills the arrow in, and the\n'
Expand Down
282 changes: 223 additions & 59 deletions plugins/avatasia/mmc-trajectory/tests/mutate-ledger-jump.py

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions plugins/avatasia/mmc-trajectory/tests/mutate-review16.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,21 @@
"const key = ids.join('" + chr(0) + "');",
"no shipped source file carries a NUL byte"),

# The other half of the same defect: the numbering on its own, with no file name for the first
# check to match against. These were labels on comments that explain themselves one line later,
# so no reader goes looking -- which is the reason they need a check rather than an argument.
# The layer numbers (L0-L6) are the same leak from the same missing document, so one pattern
# covers both and the assertion names them together.
("a private defect number comes back into a shipped comment",
"/* ---------------- the ledger's two ends ---------------- */",
"/* ---------------- D44: the ledger's two ends ---------------- */",
"no shipped file carries a private defect or layer number"),

("a private layer number comes back into a shipped comment",
" * read rather than re-measured here: the band is filters, the overview is the strip, and",
" * read rather than re-measured here: band is L2 and overview is L3, and",
"no shipped file carries a private defect or layer number"),

# ---- 5. the cache stops being released ----
# Anchored without any line ending on purpose. The first version of these two used \r\n because
# server.mjs is CRLF in the working tree — and that is exactly the trap: it only matched because
Expand Down Expand Up @@ -217,6 +232,44 @@
'"version": "1.1.0",',
'"version": "1.2.0",',
"the English README carries the shipped version"),

# ---- the snapshot the last run left, and the run that never looked at it ----
# All five break the cross-start check in tests/run.mjs, which existed on paper and did nothing:
# the snapshot was written only when it already existed, recorded the run's start rather than
# its end, and was never read. A hard kill mid-mutation therefore left the source mutated and
# the next run said nothing about it -- it reported whatever guard happened to read the mutated
# file, which reads as that guard being broken.

("the harness never reads the snapshot it writes",
"readFileSync(SNAPSHOT, 'utf8')",
"'{}'",
"the harness reads the snapshot the previous run wrote"),

("the startup check stops comparing per file",
"previous[rel] !== undefined && previous[rel] !== digest(rel)",
"false",
"the startup comparison is per file, against the digest that was recorded"),

# The old form: record the state the run STARTED with. Identical to the end state whenever
# nothing drifted, and a record of the mutation whenever something did.
("the snapshot records the state the run started with",
"writeFileSync(SNAPSHOT, JSON.stringify(Object.fromEntries(SHIPPED.map((rel) => [rel, digest(rel)]))));",
"writeFileSync(SNAPSHOT, JSON.stringify(snapshotBefore));",
"the snapshot records the state the run ended with, and only a clean end"),

# Recording a drifted end is the one that hides the thing twice: the next run compares against
# the mutation and agrees with it.
("the snapshot is written even when this run dirtied the source",
"if (drift.length === 0) {",
"if (drift.length >= 0) {",
"the snapshot records the state the run ended with, and only a clean end"),

# The two findings collapse into one line. Reads fine, costs the reader the only thing that
# distinguishes "this run broke it" from "it was already broken".
("last run's residue is named exactly like this run's",
"'shipped files differ from the last run'",
"'shipped files restored'",
"this run's own residue and last run's residue are named differently"),
]


Expand Down
55 changes: 52 additions & 3 deletions plugins/avatasia/mmc-trajectory/tests/run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ const filter = args.find((a) => !a.startsWith('--')) ?? '';
// rewrite are hashed before the first control runs and checked after the last one. A mismatch is
// reported as a failed guard of its own, with the restore command, instead of being inferred from
// whatever the next person sees.
//
// That catches a control that failed to restore DURING a run. A run that was killed outright writes
// nothing, so its residue can only be seen by comparing against the state the run BEFORE it ended
// with — which means a snapshot on disk, read at startup, and a report that is deliberately not a
// verdict. The two are separate findings: the drift below says "this run dirtied the source", the
// warning at startup says "the source was already different when this run began".
// The shipped files a mutate-* control rewrites in place — that is the whole membership rule, and
// it is a rule about files rather than about importance. docs/LAYERS.md sat in this list until it
// moved out of the package to the workspace root: it then hashed to 'absent' on every run, which
Expand All @@ -55,10 +61,52 @@ const digest = (rel) => {

const snapshotBefore = Object.fromEntries(SHIPPED.map((rel) => [rel, digest(rel)]));

// What the previous run left behind. The snapshot this reads was written by that run and meant the
// bytes it ended with, so a file that differs now is one of two things — a mutate-* control killed
// mid-run, or somebody edited the plugin after that run — and this suite cannot tell them apart.
// Both were once silent: nothing read the snapshot at all, so the residue surfaced only as unrelated
// red in whatever guard happened to read the mutated file, which reads as that guard being broken
// rather than as the checkout being dirty.
//
// Reported, not failed, and that is a decision rather than a dodge. The suite runs on machines with
// uncommitted work; failing on "these files are not what the last run left" would report a
// contributor writing code as a broken checkout, and the fix for that — trusting git — is exactly
// what is unavailable in an unpackaged copy. So the two states are named separately and only one
// of them is a verdict: this is a line above the results, the drift below is a failed guard.
const readSnapshot = () => {
try {
return existsSync(SNAPSHOT) ? JSON.parse(readFileSync(SNAPSHOT, 'utf8')) : null;
} catch {
// A missing or unreadable baseline is not a reason to stop. Without one there is nothing to
// compare against, which is the state a fresh checkout is in, and that is a normal first run.
return null;
}
};

const previous = readSnapshot();
const leftover = previous
? SHIPPED.filter((rel) => previous[rel] !== undefined && previous[rel] !== digest(rel))
: [];

if (leftover.length) {
console.log('');
console.log(`WARN ${'shipped files differ from the last run'.padEnd(30)} ${leftover.join(', ')}`);
console.log(' either a mutate-* control was killed mid-run and left the source mutated,');
console.log(' or these files were edited after that run. Not a failure: this suite runs on');
console.log(' machines with uncommitted work, and a contributor writing code is not a broken');
console.log(' checkout. The results below are only meaningful if you know which one it was.');
console.log(' if you did not just edit them: git checkout -- <file>');
}

function checkShipped() {
const drift = SHIPPED.filter((rel) => digest(rel) !== snapshotBefore[rel]);
try { existsSync(SNAPSHOT) && writeFileSync(SNAPSHOT, JSON.stringify(snapshotBefore)); }
catch { /* the snapshot is a convenience, never a reason to fail the suite */ }
// Only a clean end becomes the baseline for the next run. Recording a drifted end would make the
// next comparison agree with the mutation, which is the one thing this file exists to prevent.
if (drift.length === 0) {
try {
writeFileSync(SNAPSHOT, JSON.stringify(Object.fromEntries(SHIPPED.map((rel) => [rel, digest(rel)]))));
} catch { /* the snapshot is bookkeeping, never a reason to fail the suite */ }
}
return drift;
}

Expand Down Expand Up @@ -131,7 +179,8 @@ const drift = checkShipped();
if (drift.length) {
console.log('');
console.log(`FAIL ${'shipped files restored'.padEnd(30)} ${drift.join(', ')}`);
console.log(' a mutate-* control was killed mid-run and left the source mutated.');
console.log(' a mutate-* control ran to completion and left the source mutated. This run\'s own');
console.log(' residue, not the one the warning above reported.');
console.log(' restore with: git checkout -- <file>');
failed.push({ name: 'shipped files restored byte for byte', ok: false });
}
Expand Down
Loading
Loading