Skip to content

Commit 2c28ff5

Browse files
Prithvi-Webclaude
andcommitted
test(truth): the way-back sweep examines every "puts ... back" an undo is said to do, and lets one through only when it says until when or names a condition - README, AGENTS and the Autopilot notes bounded where it found none (review F3.4 of QA third pass D06)
The F2.5 rule fired only when "puts it/them/everything back" was followed by punctuation, the end of the line or until/while, so "Undo puts it back, whenever you ask.", "...from the Time Capsule.", "...where it was.", "the undo puts back what was trashed." and "puts back every file it trashed." were never examined; its floor (3) was met by exactly the three sentences already fixed. Inverted: every "puts <up to three words> back", and every "puts back <object>", in the docs, the agent-facing server text and the three services' sources is examined, and passes only by saying until when (until / while / as long as, before its clause ends) or by being one of three shapes that offer no way back, each named in the test: a condition on the item's place ("only if", "once", "there"), the order of a restore's steps ("before it puts any back"), and a "puts back" with no object ("what an undo puts back", "copies an undo puts back now"). Keyboard focus is no deleted item. The rule is also run on the review's five phrasings (each must read as a way back with no end) and on four bounded ones (each let through), and the floor is the 19 offers it now examines. What it found: README's "Undo puts files back byte for byte" now says "while the Time Capsule holds their copies"; AGENTS' "one undo puts both back" says "while the Time Capsule holds that copy"; autopilot.ts' header says Undo puts a run back "while it holds the run's copies"; and timeCapsule.ts' field note, which is about timestamps, reads "so what a restore puts back is the file that was taken". Ten hand mutants, all RED: each of the four sentences put back as it was, each of the review's five phrasings added to README, SECURITY or metaRoutes, and one bounded offer reworded away (the floor). Each file restored by SHA-256. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent da77507 commit 2c28ff5

5 files changed

Lines changed: 48 additions & 15 deletions

File tree

‎AGENTS.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,8 +580,9 @@ OpenAPI 3 spec).
580580
A path inside another path of the same commit gets no copy of its own:
581581
the item holding it is copied and trashed whole, the inner path is listed
582582
in `trashed` with it — once the disk no longer shows it — and counted
583-
once, and one undo puts both back (or, when the item holding it is not
584-
confirmed moved, it is in `unverified` with it; when that item is not
583+
once, and one undo puts both back while the Time Capsule holds that copy
584+
(or, when the item holding it is not confirmed moved, it is in
585+
`unverified` with it; when that item is not
585586
deleted, it is in `skipped`, saying so; when the Trash reports taking that
586587
item but the disk still shows the inner path in its place, the inner path
587588
is in `skipped` with code `NOT_DELETED`, and when its place cannot be

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ The weights live in **Settings → Reclaim Score** and are yours to change, with
218218

219219
**Preview after** re-lays the treemap out with the staged items taken out and hatches what would come back, under a banner that says nothing has been deleted. The freed space stays on the map rather than the survivors growing into it: area means bytes, and a rectangle that silently became worth more bytes than it was a second ago would make every size comparison against the live map wrong. Exiting restores the map exactly as it was. It is pure arithmetic in the browser — no scan, no server call, nothing on disk.
220220

221-
Committing runs a **dry run first, always**: the exact manifest, every path with its size, and anything that will be **left in place** with the reason. Then the whole batch goes through the **Time Capsule** as one run — copied, verified, and only then trashed — so **Undo this run** puts every file back at its original path, even after you have emptied the Trash, until the Time Capsule removes its copies (after 30 days by default, or sooner to stay under its size cap). Anything too large for the capsule to protect is **left undeleted rather than deleted unprotected**, said in the manifest before you click rather than in a summary afterwards. Undo puts files back byte for byte, **with their permissions and the dates they were last modified and opened** — including a folder's own, restored after its contents so that writing the children back does not re-stamp it. (Not the date each was created: no system lets a program set that back.)
221+
Committing runs a **dry run first, always**: the exact manifest, every path with its size, and anything that will be **left in place** with the reason. Then the whole batch goes through the **Time Capsule** as one run — copied, verified, and only then trashed — so **Undo this run** puts every file back at its original path, even after you have emptied the Trash, until the Time Capsule removes its copies (after 30 days by default, or sooner to stay under its size cap). Anything too large for the capsule to protect is **left undeleted rather than deleted unprotected**, said in the manifest before you click rather than in a summary afterwards. Undo puts files back byte for byte while the Time Capsule holds their copies, **with their permissions and the dates they were last modified and opened** — including a folder's own, restored after its contents so that writing the children back does not re-stamp it. (Not the date each was created: no system lets a program set that back.)
222222

223223
An optional **cleanup target** in Settings — "free 50 GB" — gives the cart a meter filling toward it. A meter, and nothing else: no streaks, no badges, no confetti. A progress meter is information; a reward loop pointed at deleting your own files is manipulation.
224224

‎src/services/autopilot.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ import { AsyncMutex } from '../utils/asyncMutex';
6464
* policy pointed at the wrong folder cannot empty it overnight.
6565
* - **Cooldown** keeps a policy from running again immediately, and doubles as
6666
* the schedule — one knob instead of two that can disagree.
67-
* - **Undo** puts a whole run back from the Time Capsule, which is why every
68-
* run stamps its own id onto the capsule entries it creates.
67+
* - **Undo** puts a whole run back from the Time Capsule, while it holds the
68+
* run's copies — which is why every run stamps its own id onto the capsule
69+
* entries it creates.
6970
*
7071
* Every one of these refuses *loudly*: a run that did not delete still writes a
7172
* run record saying what it would have done and why it stopped.

‎src/services/timeCapsule.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ interface ManifestMember {
128128
/** Symlinks only: whether what it pointed at was a folder, when that could be seen (FG3f #9). */
129129
targetIsDir?: boolean;
130130
/**
131-
* Modification and access time, so a restore puts back the file that was
132-
* taken rather than a copy of it made today.
131+
* Modification and access time, so what a restore puts back is the file
132+
* that was taken rather than a copy of it made today.
133133
*
134134
* Optional because manifests written before this existed do not carry it,
135135
* and a restore of one of those must still work — it simply leaves the

‎tests/cleanUpModalHonesty.test.ts‎

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,7 @@ test('what the server and the docs tell an agent about the Trash says when the w
938938
assert.ok(seen >= 6, `the sweep found the server's and the docs' own "recoverable" (${seen})`);
939939
});
940940

941-
test('no sentence in the docs, in what an agent is told or in a result the server writes offers a way back with no end — "any time", "can be put back", "puts it back", "even after the Trash is emptied" each say until when (review F1.2, F1.8, F2.2, F2.5 of QA third pass D06)', () => {
941+
test('no sentence in the docs, in what an agent is told or in a result the server writes offers a way back with no end — "any time", "can be put back", "puts it back", "even after the Trash is emptied" each say until when (review F1.2, F1.8, F2.2, F2.5, F3.4 of QA third pass D06)', () => {
942942
// The sweep above reads only the word "recoverable", so README's "undo
943943
// from Finder/Explorer any time", SECURITY's "can be put back" and "a run
944944
// can be undone even after the Trash is emptied", AGENTS' cart undo "even
@@ -958,6 +958,41 @@ test('no sentence in the docs, in what an agent is told or in a result the serve
958958
const WAY_BACK = /\brecoverabl|\brestor(?:e|ed)\b|\bundo(?:ne)?\b|\bput\b[^.;]*\bback\b|\bcan be recovered\b/i;
959959
/** Whether, after the phrase at `m` and before its clause ends, the sentence says when that way back ends. */
960960
const endsLater = (s: string, m: RegExpExecArray) => /^[^;.]*?\b(?:until|while|as long as)\b/i.test(s.slice(m.index + m[0].length));
961+
/**
962+
* Every "puts … back" in `s` that offers a way back — "puts it back", "puts every file back",
963+
* "puts back what was trashed" — and whether it is bounded. Every one is examined (review F3.4:
964+
* the rule read four trailing shapes and passed every other), and passes only by saying until
965+
* when, or by being one of three shapes that offer no way back, each named here: a condition on
966+
* the item's place ("only if its place is empty by then", "once", "there") says how an undo
967+
* works, not whether one still can; "before it puts any back" says the order of a restore's
968+
* steps; and a "puts back" with no object ("what an undo puts back", "copies an undo puts back
969+
* now") names the copies rather than promising them. Keyboard focus is no deleted item.
970+
*/
971+
const putsBackIn = (s: string): Array<{ phrase: string; bounded: boolean }> => {
972+
const offers: Array<{ phrase: string; bounded: boolean }> = [];
973+
for (const m of s.matchAll(/\bputs((?: (?!back\b)[\w’'`-]+){0,3}) back\b/gi)) {
974+
const what = m[1].trim();
975+
const before = s.slice(0, m.index);
976+
const after = s.slice(m.index! + m[0].length);
977+
if (/^focus$/i.test(what)) continue;
978+
if (!what && /^\s*(?:$|[,.;:)\]]|(?:now|is|are)\b)/i.test(after)) continue;
979+
const bounded = /^[^;.]*?\b(?:until|while|as long as)\b/i.test(after)
980+
|| /^[^;.]*?\b(?:only if|once|there)\b/i.test(after)
981+
|| /\bbefore (?:it|an undo|a restore|the undo|the restore) $/i.test(before);
982+
offers.push({ phrase: m[0], bounded });
983+
}
984+
return offers;
985+
};
986+
// The rule itself, on the shapes the review found it blind to: each is a way back with no end.
987+
for (const s of ['Undo puts it back, whenever you ask.', 'Undo puts it back from the Time Capsule.', 'Undo puts it back where it was.',
988+
'The undo puts back what was trashed.', 'A run is undone with a click and puts back every file it trashed.', 'Undo puts every file back.']) {
989+
assert.deepEqual(putsBackIn(s).map((o) => o.bounded), [false], `the rule reads "${s}" as a way back with no end`);
990+
}
991+
// And the shapes that say until when, or how, are let through.
992+
for (const s of ['Undo puts it back until the Time Capsule removes its copies.', 'An undo puts it back only if its place is empty by then.',
993+
'An undo puts each back once its place is empty.', 'An undo puts it back, and looks for it, there.']) {
994+
assert.deepEqual(putsBackIn(s).map((o) => o.bounded), [true], `the rule lets "${s}" through`);
995+
}
961996
const seen = { wayBack: 0, pastEmptied: 0, canBe: 0, putsBack: 0 };
962997
for (const [file, text] of agentTexts(RESULT_SOURCES)) {
963998
for (const s of sentencesOf(file, text)) {
@@ -976,16 +1011,12 @@ test('no sentence in the docs, in what an agent is told or in a result the serve
9761011
seen.canBe++;
9771012
assert.ok(endsLater(s, can), `${file}: "${can[0]}" with no end — "${s}"`);
9781013
}
979-
// An undo that "puts it back" with nothing after it is a promise that it still can — or one
980-
// that says until when. One qualified by a condition ("only if its place is empty by then",
981-
// "once", "there") says how an undo works, not whether one still can, and is not this rule's.
982-
const puts = /\bputs (?:it|them|everything) back(?=\s*(?:[.;'"`)]|$|\b(?:until|while|as long as)\b))|\bputs back (?:exactly what|everything)\b/i.exec(s);
983-
if (puts) {
1014+
for (const offer of putsBackIn(s)) {
9841015
seen.putsBack++;
985-
assert.ok(endsLater(s, puts), `${file}: "${puts[0]}" with no end — "${s}"`);
1016+
assert.ok(offer.bounded, `${file}: "${offer.phrase}" with no end — "${s}"`);
9861017
}
9871018
}
9881019
}
9891020
// Each rule read the sentences it is about: a split that merged or lost them would pass by seeing nothing.
990-
assert.ok(seen.wayBack >= 100 && seen.pastEmptied >= 5 && seen.canBe >= 8 && seen.putsBack >= 3, `the sweep read the docs' ways back: ${JSON.stringify(seen)}`);
1021+
assert.ok(seen.wayBack >= 100 && seen.pastEmptied >= 5 && seen.canBe >= 8 && seen.putsBack >= 19, `the sweep read the docs' ways back: ${JSON.stringify(seen)}`);
9911022
});

0 commit comments

Comments
 (0)