mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-09 02:08:19 +01:00
Merge remote-tracking branch 'origin/main' into capy/gsta-95-bun-config-windows
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> # Conflicts: # CHANGELOG.md # bin/gstack-question-log
This commit is contained in:
commit
d77948c6cb
20 files changed
+537
-35
No files matched your search
@@ -29,7 +29,7 @@
|
||||
"test/helpers/eval-budgets.ts": {"blob":"667d0e09b20fe5d7366f57b77b5fb9b04baa37ef","since":6},
|
||||
"test/helpers/eval-store.ts": {"blob":"a50ea2c28f508ad9f35ea9353dd2f502ee0dcfc2","since":6,"nonBehavioral":"credential rejections are classed infra instead of assertion: no case's pass or fail changes; only failed trials whose provider rejected the run's key move class, so no rerun spends on a rejected key"},
|
||||
"test/helpers/hermetic-env.ts": {"blob":"cc072895461dc52ea317d1beff72121342420d30","since":6},
|
||||
"test/helpers/llm-judge.ts": {"blob":"d0bc6bbbe9f478603d11a1ece95e66c812ad34e6","since":6},
|
||||
"test/helpers/llm-judge.ts": {"blob":"a3177665b662932ce485ccc976c2b36a1531a0bb","since":6,"nonBehavioral":"callJudge rewrites only the message of a JSON parse failure (names the judge output, stop reason, length and the retained llm-judge-response-parse-error event); it still throws a SyntaxError before any score is parsed, the request is unchanged, and no case's pass or fail changes"},
|
||||
"test/helpers/paid-test-set.ts": {"blob":"ff9bfe43c43ec2bdd11114f7b8ec7c5736116f75","since":6},
|
||||
"test/helpers/pty-screen.ts": {"blob":"4f77569f9ce11f9cee9f347216e468cd7c956820","since":6},
|
||||
"test/helpers/pty/auq.ts": {"blob":"16e5ea2bb5b9f28f44808a1a09024dbd3e6f28c4","since":6},
|
||||
|
||||
@@ -223,6 +223,89 @@ export const SIGNAL_MAP: Record<string, Record<string, DimensionDelta[]>> = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Label cues: option key → leading words of UI labels that mean that key.
|
||||
*
|
||||
* The AskUserQuestion hook only sees the label the user clicked ("A) Fix it
|
||||
* now (recommended)"), never a stable key, while SIGNAL_MAP is keyed by the
|
||||
* registry's option keys (#2623). resolveOptionKey() maps a label back to a
|
||||
* key when bin/gstack-question-log records a hook event. Every key also
|
||||
* matches its own words ("fix-now" → "fix now"), so only phrasings beyond
|
||||
* that need an entry here. Cues are
|
||||
* lowercase words separated by single spaces and match at the start of the
|
||||
* normalized label; candidates are limited to the question's own registered
|
||||
* options, and the longest cue wins.
|
||||
*/
|
||||
const OPTION_LABEL_CUES: Record<string, readonly string[]> = {
|
||||
accept: ['yes', 'approve', 'agree', 'add', 'include', 'apply', 'adopt', 'enable', 'go ahead', 'do it', 'proceed', 'run', 'create', 'merge', 'roll back', 'migrate', 'open', 'promote', 'reorganize', 'polish'],
|
||||
reject: ['no', 'disagree', 'decline', 'cancel', 'abort', 'dont', 'do not', 'keep current', 'keep as is', 'keep it as is'],
|
||||
skip: ['no', 'no thanks', 'not now', 'dont', 'do not', 'pass', 'drop', 'ignore', 'leave it', 'ship as is', 'ship it as is'],
|
||||
defer: ['later', 'fix later', 'add to todos', 'add to todo', 'add a todo', 'todo', 'postpone', 'backlog', 'park', 'track it'],
|
||||
'fix-now': ['fix', 'fix it', 'fix them', 'fix before shipping'],
|
||||
'fix-anyway': ['fix', 'fix it'],
|
||||
investigate: ['dig in', 'look into', 'debug', 'diagnose', 'root cause'],
|
||||
'ack-and-ship': ['ack', 'acknowledge', 'ship', 'ship anyway', 'ship it'],
|
||||
'accept-risk': ['accept', 'accept the risk', 'live with it'],
|
||||
'false-positive': ['not a bug', 'mark false positive', 'mark as false positive', 'mark it false positive'],
|
||||
'add-test': ['add', 'add tests', 'add the test', 'write', 'write the test', 'test it'],
|
||||
expand: ['scope expansion'],
|
||||
selective: ['cherry pick', 'selective expansion'],
|
||||
hold: ['keep', 'keep scope', 'hold scope'],
|
||||
reduce: ['cut', 'trim', 'shrink', 'scope down', 'scope reduction'],
|
||||
revise: ['edit', 'change', 'rework'],
|
||||
restart: ['start over', 'redo'],
|
||||
'build-now': ['build', 'build it now', 'include in this pr'],
|
||||
'keep-local': ['keep'],
|
||||
'keep-vendored': ['keep'],
|
||||
'yes-upgrade': ['yes', 'upgrade'],
|
||||
'always-auto': ['always'],
|
||||
'not-now': ['later', 'snooze'],
|
||||
'never-ask': ['never', 'disable'],
|
||||
};
|
||||
|
||||
function normalizeOptionLabel(label: string): string {
|
||||
return label
|
||||
.toLowerCase()
|
||||
.replace(/\s*\(recommended\)\s*$/, '')
|
||||
.replace(/['\u2019]/g, '')
|
||||
.replace(/^\s*(?:option\s+)?[a-z0-9]\s*(?:[).:]|-(?=\s))\s+/, '')
|
||||
.replace(/[^a-z0-9]+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a chosen UI label to the registered option key of its question.
|
||||
*
|
||||
* Returns the key when exactly one of the question's registered options
|
||||
* matches with the longest cue; undefined for unregistered ids, questions
|
||||
* without options, unmatched labels and ties, so an unclear answer stays
|
||||
* unmapped instead of nudging the wrong dimension.
|
||||
*/
|
||||
export function resolveOptionKey(question_id: string, choice: string): string | undefined {
|
||||
const options = (QUESTIONS as Record<string, QuestionDef>)[question_id]?.options as string[] | undefined;
|
||||
if (!options?.length) return undefined;
|
||||
if (options.includes(choice)) return choice;
|
||||
const label = normalizeOptionLabel(choice);
|
||||
if (!label) return undefined;
|
||||
let best: string | undefined;
|
||||
let bestWords = 0;
|
||||
let tie = false;
|
||||
for (const key of options) {
|
||||
for (const cue of [key.replace(/-/g, ' '), ...(OPTION_LABEL_CUES[key] ?? [])]) {
|
||||
if (label !== cue && !label.startsWith(cue + ' ')) continue;
|
||||
const words = cue.split(' ').length;
|
||||
if (words > bestWords) {
|
||||
best = key;
|
||||
bestWords = words;
|
||||
tie = false;
|
||||
} else if (words === bestWords && best !== key) {
|
||||
tie = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tie ? undefined : best;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a user choice for a question to the running dimension totals.
|
||||
*
|
||||
|
||||
Reference in new issue
Block a user