mirror of
https://github.com/garrytan/gstack.git
synced 2026-06-17 23:30:09 +02:00
feat(question-pref): runtime AUTO_DECIDE carve-out for *-split-* ids
Split chains (per-option AskUserQuestion calls emitted by the new "Handling 5+ options" rule) must never be silently auto-approved via /plan-tune preferences. The user's option set is sacred. Layer 1 (mechanism): unique <skill>-split-<option-slug> ids prevent cross-option preference leakage. Layer 2 (this commit): the runtime checker `gstack-question-preference --check` detects any id matching *-split-* and forces ASK_NORMALLY even when never-ask or ask-only-for-one-way preferences exist for that exact id. An explanatory note tells the user their preference was bypassed and why. 7 tests pin the carve-out: no-pref baseline, never-ask override, explanatory note text, ask-only-for-one-way override, always-ask (no note), non-split id containing "split" word (negative case for regex specificity), multi-skill split id formats. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -103,6 +103,65 @@ describe('--check with preferences set', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Split-chain carve-out: question_ids matching <skill>-split-<option-slug>
|
||||
// must always ASK_NORMALLY regardless of stored preferences.
|
||||
// See scripts/resolvers/preamble/generate-ask-user-format.ts
|
||||
// "Handling 5+ options — split, never drop" for the surrounding mechanism.
|
||||
describe('--check split-chain carve-out (*-split-* always ASK_NORMALLY)', () => {
|
||||
function setPref(id: string, pref: string) {
|
||||
return run('--write', JSON.stringify({ question_id: id, preference: pref, source: 'plan-tune' }));
|
||||
}
|
||||
|
||||
test('split-id without preference → ASK_NORMALLY', () => {
|
||||
const r = run('--check', 'plan-ceo-review-split-e4-detect-mappings');
|
||||
expect(r.stdout.trim()).toContain('ASK_NORMALLY');
|
||||
});
|
||||
|
||||
test('split-id + never-ask → ASK_NORMALLY (carve-out overrides preference)', () => {
|
||||
setPref('plan-ceo-review-split-e4-detect-mappings', 'never-ask');
|
||||
const r = run('--check', 'plan-ceo-review-split-e4-detect-mappings');
|
||||
expect(r.stdout).toContain('ASK_NORMALLY');
|
||||
expect(r.stdout).not.toContain('AUTO_DECIDE');
|
||||
});
|
||||
|
||||
test('split-id + never-ask → emits explanatory note', () => {
|
||||
setPref('plan-ceo-review-split-e4-detect-mappings', 'never-ask');
|
||||
const r = run('--check', 'plan-ceo-review-split-e4-detect-mappings');
|
||||
expect(r.stdout).toContain('split-chain per-option calls always ASK_NORMALLY');
|
||||
expect(r.stdout).toContain('never-ask');
|
||||
});
|
||||
|
||||
test('split-id + ask-only-for-one-way → ASK_NORMALLY (carve-out overrides preference)', () => {
|
||||
setPref('ship-split-version-bump', 'ask-only-for-one-way');
|
||||
const r = run('--check', 'ship-split-version-bump');
|
||||
expect(r.stdout).toContain('ASK_NORMALLY');
|
||||
expect(r.stdout).not.toContain('AUTO_DECIDE');
|
||||
});
|
||||
|
||||
test('split-id + always-ask → ASK_NORMALLY (no note since preference agrees)', () => {
|
||||
setPref('plan-eng-review-split-add-test', 'always-ask');
|
||||
const r = run('--check', 'plan-eng-review-split-add-test');
|
||||
expect(r.stdout.trim()).toContain('ASK_NORMALLY');
|
||||
expect(r.stdout).not.toContain('does not apply');
|
||||
});
|
||||
|
||||
test('non-split id that just happens to contain "split" word is NOT carved out', () => {
|
||||
// The carve-out matches `-split-` (kebab-cased), not the substring "split".
|
||||
// A question id like `qa-splitscreen-test` (hypothetical) would not match.
|
||||
// Verify by using a never-ask pref that should fire AUTO_DECIDE.
|
||||
setPref('qa-splitscreen-test', 'never-ask');
|
||||
const r = run('--check', 'qa-splitscreen-test');
|
||||
expect(r.stdout.trim()).toContain('AUTO_DECIDE');
|
||||
});
|
||||
|
||||
test('multiple split-id formats: skill-split-anything matches', () => {
|
||||
setPref('autoplan-split-ceo-finding-7', 'never-ask');
|
||||
const r = run('--check', 'autoplan-split-ceo-finding-7');
|
||||
expect(r.stdout).toContain('ASK_NORMALLY');
|
||||
expect(r.stdout).not.toContain('AUTO_DECIDE');
|
||||
});
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// --write
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user