From 2a02647fd48c6becf4f7e22bd6acaccd026acf80 Mon Sep 17 00:00:00 2001 From: Chao Chen <209239467+surlymochan@users.noreply.github.com> Date: Sat, 17 Oct 2026 16:31:39 +0800 Subject: [PATCH] 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 --- context-restore/SKILL.md | 10 ++++++++++ context-restore/SKILL.md.tmpl | 10 ++++++++++ context-save/SKILL.md | 15 +++++++++++++++ context-save/SKILL.md.tmpl | 15 +++++++++++++++ test/context-remaining-work-provenance.test.ts | 4 ++++ 5 files changed, 54 insertions(+) diff --git a/context-restore/SKILL.md b/context-restore/SKILL.md index f69ce4a4a..ea94985f4 100644 --- a/context-restore/SKILL.md +++ b/context-restore/SKILL.md @@ -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). diff --git a/context-restore/SKILL.md.tmpl b/context-restore/SKILL.md.tmpl index 066594e6e..dbebf92e5 100644 --- a/context-restore/SKILL.md.tmpl +++ b/context-restore/SKILL.md.tmpl @@ -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). diff --git a/context-save/SKILL.md b/context-save/SKILL.md index 9998eb56b..d68858b9c 100644 --- a/context-save/SKILL.md +++ b/context-save/SKILL.md @@ -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. diff --git a/context-save/SKILL.md.tmpl b/context-save/SKILL.md.tmpl index 87dc9b428..91fce9edf 100644 --- a/context-save/SKILL.md.tmpl +++ b/context-save/SKILL.md.tmpl @@ -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. diff --git a/test/context-remaining-work-provenance.test.ts b/test/context-remaining-work-provenance.test.ts index 0fc423fe2..c9ac1e705 100644 --- a/test/context-remaining-work-provenance.test.ts +++ b/test/context-remaining-work-provenance.test.ts @@ -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(