mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 06:24:41 +01:00
* fix(plan-eng-review): register the structure question id so never-ask can fire The complexity gate's Original/Smaller arrangement question had no registered question_id, so each run invented a per-plan slug (one install logged ten different ids for it). --check matches ids exactly, so a stored never-ask could never fire and the question was asked every time. - Register plan-eng-review-structure as a two-way routing question. - The template names that exact id and says AUTO_DECIDE picks the recommended arrangement; the preamble's check-first rule does the rest. Wording is byte-neutral against the parity budget and the per-host pins. - --check falls back to $GSTACK_HOME/global-question-preferences.json, the file the question-preference hook already reads, when the project file has no entry for the id, so one setting covers every repo and both layers agree. A project entry still wins, the one-way and split-chain guards run first, a malformed or non-object global file is ignored with a NOTE, and --write stays per-project. - Tests for the fallback and the template contract; two wording pins updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commitfb0334eb5c) * 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 (cherry picked from commit2a02647fd4) * docs: add a neutral how-to for bringing up a first deploy (#3150) /setup-deploy stops at "Nothing is deployed yet" and writes nothing, because it has no production URL or deploy trigger to record. The docs said nothing about the step in between. The maintainers chose to keep that stop vendor-neutral and asked for a neutral docs page instead (#3150, option 2). docs/howto-bring-up-a-first-deploy.md walks through the first deploy: where the app runs and how deploys start, the services it needs, env and secrets, DNS and TLS, checking that it works, and running /setup-deploy again. It names no hosting provider or deploy tool. The README Docs table and the /setup-deploy section of docs/skills.md link to it. The stop's own text is unchanged. Fixes #3150 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit5c5d8ec96f) * docs(changelog): contributor fixes from #3249, #3244, #3264 under [Unreleased] Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: Trevor Bezdek <bezdekt@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Chao Chen <209239467+surlymochan@users.noreply.github.com> Co-authored-by: y$un_ <forrest.sun527@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
68 lines
3.7 KiB
TypeScript
68 lines
3.7 KiB
TypeScript
/**
|
|
* Remaining Work provenance (#3004): prompt-byte contract for the shipped
|
|
* context-save and context-restore SKILL.md files.
|
|
*
|
|
* The markers are a wire format between two skills: /context-save writes them
|
|
* into a checkpoint and a later /context-restore sorts on them. Only that
|
|
* contract is pinned (marker tokens, group headings, banner, option-A order),
|
|
* not the surrounding prose. Behavior is covered by the periodic E2E case
|
|
* context-restore-provenance-order.
|
|
*
|
|
* Value: protects=save writes the five provenance markers and restore sorts them into Next steps and Verify first; fails_when=a marker, a group or the saved-order option A is dropped; why_new=no test reads Remaining Work; seam=none
|
|
*/
|
|
import { describe, test, expect } from 'bun:test';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
const ROOT = path.resolve(import.meta.dir, '..');
|
|
const MARKERS = ['(path run)', '(path read)', '(path assumed)', '(code read)', '(target state checked)'];
|
|
|
|
function read(rel: string): string {
|
|
return fs.readFileSync(path.join(ROOT, rel), 'utf-8');
|
|
}
|
|
|
|
function between(text: string, start: string, end: string): string {
|
|
const from = text.indexOf(start);
|
|
expect(from, `missing ${start}`).toBeGreaterThanOrEqual(0);
|
|
const to = text.indexOf(end, from + start.length);
|
|
expect(to, `missing ${end} after ${start}`).toBeGreaterThan(from);
|
|
return text.slice(from, to);
|
|
}
|
|
|
|
describe('Remaining Work provenance contract', () => {
|
|
test('context-save marks every item Open. and defines all five markers, writing steps first', () => {
|
|
const save = read('context-save/SKILL.md');
|
|
const table = between(save, '**Remaining Work provenance.**', 'If the user provided a title');
|
|
for (const marker of MARKERS) expect(table).toContain(`\`${marker}\``);
|
|
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.`');
|
|
});
|
|
|
|
test('context-restore sorts into Next steps and Verify first and option A walks saved order', () => {
|
|
const restore = read('context-restore/SKILL.md');
|
|
expect(restore).not.toContain('{remaining work items}');
|
|
const rules = between(restore, '**Sort Remaining Work by provenance.**', 'Read the chosen file');
|
|
const verifyFirst = between(rules, 'Put an item under **Verify first**', 'Every other item');
|
|
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(
|
|
'`This checkpoint predates provenance markers; items naming commands, paths or writes are listed under Verify first.`',
|
|
);
|
|
const summary = between(restore, '### Remaining Work', '### Notes');
|
|
expect(summary.indexOf('Next steps')).toBeLessThan(summary.indexOf('Verify first'));
|
|
const optionA = between(restore, 'If A,', '## If no saved contexts exist');
|
|
expect(optionA).toContain('first Remaining Work item in saved order');
|
|
expect(optionA).toContain('under Verify first, suggest verifying it');
|
|
});
|
|
});
|