fix(context): pin English provenance markers and ask when branch cannot discriminate

Addresses three gaps from #3217:

1. context-save: markers must stay the five English literals; translated
   tokens break restore sorting. Decisions Made claims about file contents
   need evidence or an assumed Remaining Work item.
2. context-restore: unknown/translated parenthesised markers sort under
   Verify first as (path assumed).
3. When CURRENT_BRANCH cannot discriminate and recent candidates have
   different titles, ask instead of silently loading newest.

Pins the new contract in the provenance prompt-byte test.

Fixes #3217
This commit is contained in:
Chao Chen committed 2026-10-17 16:31:39 +08:00
1 parent d258bcffb0
commit 2a02647fd4
5 files changed
+54

No files matched your search

+10
View File
@@ -509,6 +509,12 @@ has written to). Prefixed lines are never candidates for "latest":
- Otherwise: load the **first plain path line returned by Step 1 above**, which
is the newest `YYYYMMDD-HHMMSS` checkpoint of this project for the current
branch, or, if the current branch has none, the newest across all branches.
- **No useful branch discriminator.** When `CURRENT_BRANCH` is empty, `HEAD`, or
otherwise cannot discriminate (cwd is not a git repo, or branch is unknown), do
not silently load newest if the recent candidates (e.g. last 24 hours, or the
three newest) carry **different titles**. Show those titles with timestamps via
AskUserQuestion, mark newest as the recommended option, and wait. Only auto-load
newest when every recent candidate shares one title (same topic).
- **Do not silently take an older checkpoint.** If Step 1 printed `NEWER_TASK`,
or a `BELOW_CWD`/`POINTER` file is newer than the first plain path line (or
there is none), show both (title, branch, saved time, and `project_root` for a
@@ -534,6 +540,10 @@ checkpoint(s) from another project; only this project's are listed." If it print
order, and drop nothing. Put an item under **Verify first** when it:
- ends in `(path assumed)` or `(code read)`;
- ends in `(path run)` but its text reports a failure;
- ends in a parenthesised trailing marker that is **not** one of the five English
literals `(path run)`, `(path read)`, `(path assumed)`, `(target state checked)`,
`(code read)` — treat a translated or unknown marker as `(path assumed)` (→ Verify
first), never as unmarked Next steps;
- has no marker and is a writing step (migration, sync, insert, import, a dialog that
writes) or names a concrete path (a runnable command, CLI flag or switch, config key
or value, or file or directory path).
+10
View File
@@ -176,6 +176,12 @@ has written to). Prefixed lines are never candidates for "latest":
- Otherwise: load the **first plain path line returned by Step 1 above**, which
is the newest `YYYYMMDD-HHMMSS` checkpoint of this project for the current
branch, or, if the current branch has none, the newest across all branches.
- **No useful branch discriminator.** When `CURRENT_BRANCH` is empty, `HEAD`, or
otherwise cannot discriminate (cwd is not a git repo, or branch is unknown), do
not silently load newest if the recent candidates (e.g. last 24 hours, or the
three newest) carry **different titles**. Show those titles with timestamps via
AskUserQuestion, mark newest as the recommended option, and wait. Only auto-load
newest when every recent candidate shares one title (same topic).
- **Do not silently take an older checkpoint.** If Step 1 printed `NEWER_TASK`,
or a `BELOW_CWD`/`POINTER` file is newer than the first plain path line (or
there is none), show both (title, branch, saved time, and `project_root` for a
@@ -201,6 +207,10 @@ checkpoint(s) from another project; only this project's are listed." If it print
order, and drop nothing. Put an item under **Verify first** when it:
- ends in `(path assumed)` or `(code read)`;
- ends in `(path run)` but its text reports a failure;
- ends in a parenthesised trailing marker that is **not** one of the five English
literals `(path run)`, `(path read)`, `(path assumed)`, `(target state checked)`,
`(code read)` — treat a translated or unknown marker as `(path assumed)` (→ Verify
first), never as unmarked Next steps;
- has no marker and is a writing step (migration, sync, insert, import, a dialog that
writes) or names a concrete path (a runnable command, CLI flag or switch, config key
or value, or file or directory path).
+15
View File
@@ -435,6 +435,21 @@ depends on the target's state, not on the code that writes. Examples:
`Open. Run the suite with CONFIG=staging. (path run) exit 0` and
`Open. Switch B reads config Y. (path assumed)`.
**Markers stay English literals.** `/context-restore` sorts on the exact strings
`(path run)`, `(path read)`, `(path assumed)`, `(target state checked)`, and
`(code read)`. Never translate them, even when the rest of the checkpoint is in
another language. After drafting Remaining Work, scan each item that should have
a marker: if it ends in a parenthesised token that is not one of those five, or
has no marker when the table requires one, rewrite that item before writing the
file (preferred), or add a Remaining Work item that names the bad line so restore
puts it under Verify first.
**Decisions Made and file claims.** Provenance above covers Remaining Work only.
Any statement in Decisions Made about the *contents* of a concrete file (item
counts, line counts, section names) must be backed by a `grep`/`wc`/`Read` in this
turn, or rewritten with an honest `(path assumed)` note in Remaining Work that
restore will verify first. Do not record an unchecked file claim as fact.
If the user provided a title, use it. Otherwise, infer a concise title (3-6 words)
from the work being done.
+15
View File
@@ -103,6 +103,21 @@ depends on the target's state, not on the code that writes. Examples:
`Open. Run the suite with CONFIG=staging. (path run) exit 0` and
`Open. Switch B reads config Y. (path assumed)`.
**Markers stay English literals.** `/context-restore` sorts on the exact strings
`(path run)`, `(path read)`, `(path assumed)`, `(target state checked)`, and
`(code read)`. Never translate them, even when the rest of the checkpoint is in
another language. After drafting Remaining Work, scan each item that should have
a marker: if it ends in a parenthesised token that is not one of those five, or
has no marker when the table requires one, rewrite that item before writing the
file (preferred), or add a Remaining Work item that names the bad line so restore
puts it under Verify first.
**Decisions Made and file claims.** Provenance above covers Remaining Work only.
Any statement in Decisions Made about the *contents* of a concrete file (item
counts, line counts, section names) must be backed by a `grep`/`wc`/`Read` in this
turn, or rewritten with an honest `(path assumed)` note in Remaining Work that
restore will verify first. Do not record an unchecked file claim as fact.
If the user provided a title, use it. Otherwise, infer a concise title (3-6 words)
from the work being done.
@@ -37,6 +37,8 @@ describe('Remaining Work provenance contract', () => {
expect(table).toContain('`Open.`');
expect(table).toContain('`[done]`');
expect(table.indexOf('a writing step')).toBeLessThan(table.indexOf('names a concrete path'));
expect(table).toContain('**Markers stay English literals.**');
expect(table).toContain('**Decisions Made and file claims.**');
const format = between(save, '### Remaining Work', '### Notes');
expect(format).toContain('`Open.`');
});
@@ -49,6 +51,8 @@ describe('Remaining Work provenance contract', () => {
for (const marker of ['(path assumed)', '(code read)']) expect(verifyFirst).toContain(`\`${marker}\``);
expect(verifyFirst).toContain('reports a failure');
expect(verifyFirst).toContain('has no marker');
expect(verifyFirst).toContain('translated or unknown marker');
expect(restore).toContain('**No useful branch discriminator.**');
const nextSteps = between(rules, 'Every other item goes under **Next steps**', 'Verifying means');
for (const marker of ['(path run)', '(path read)', '(target state checked)']) expect(nextSteps).toContain(`\`${marker}\``);
expect(rules).toContain(