* fix(#1581): config-set no longer silently coerces Infinity/project_code The value parser used !isNaN(Number(val)), which admits Infinity/-Infinity; JSON.stringify then renders those as null on disk while the CLI echoed the non-finite value (output ≠ disk). Leading-zero strings like project_code '007' were also silently number-coerced to 7. - config.cts parser: Number.isFinite instead of !isNaN, so Infinity falls through to the JSON branch (rejected) and stays a string. - project_code: always persisted as a string (identifier; leading zeros matter), bypassing number coercion. - context_window: new per-key validator — must be a finite positive integer (rejects Infinity/0/negatives/non-integers with a non-zero exit). - tests/config.test.cjs: #1581 regression (Infinity rejected, 0 rejected, 200000 accepted finite, project_code '007' string-preserved, granularity numeric coercion unchanged). Closes #1581 * docs(#1581): backfill changeset pr 2023
This commit is contained in:
@@ -586,11 +586,22 @@ function cmdConfigSet(cwd: string, keyPath: string | undefined, value: string |
|
||||
let parsedValue: unknown = val;
|
||||
if (val === 'true') parsedValue = true;
|
||||
else if (val === 'false') parsedValue = false;
|
||||
else if (!isNaN(Number(val)) && val !== '') parsedValue = Number(val);
|
||||
// #1581: Number.isFinite (not !isNaN) so 'Infinity'/'-Infinity' are NOT
|
||||
// coerced to non-finite numbers that JSON.stringify later renders as `null`
|
||||
// (disk=null while the CLI echoed 'Infinity'). They fall through to the
|
||||
// JSON branch (which rejects them) and stay strings, then per-key validators
|
||||
// reject them with a non-zero exit.
|
||||
else if (Number.isFinite(Number(val)) && val !== '') parsedValue = Number(val);
|
||||
else if (typeof val === 'string' && (val.startsWith('[') || val.startsWith('{'))) {
|
||||
try { parsedValue = JSON.parse(val); } catch { /* keep as string */ }
|
||||
}
|
||||
|
||||
// #1581: project_code is an identifier string — never number-coerce it. A
|
||||
// leading-zero code like '007' must persist verbatim (not collapse to 7).
|
||||
if (kp === 'project_code') {
|
||||
parsedValue = val;
|
||||
}
|
||||
|
||||
const VALID_CONTEXT_VALUES = ['dev', 'research', 'review'];
|
||||
if (kp === 'context') assertEnumValue(parsedValue, val, VALID_CONTEXT_VALUES, 'context value');
|
||||
|
||||
@@ -603,6 +614,15 @@ function cmdConfigSet(cwd: string, keyPath: string | undefined, value: string |
|
||||
}
|
||||
}
|
||||
|
||||
// #1581: context_window must be a finite positive integer. 'Infinity' is no
|
||||
// longer number-coerced (see the parse block above) so it reaches here as a
|
||||
// string and is rejected; '0', negatives, and non-integers are also rejected.
|
||||
if (kp === 'context_window') {
|
||||
if (typeof parsedValue !== 'number' || !Number.isFinite(parsedValue) || !Number.isInteger(parsedValue) || parsedValue < 1) {
|
||||
error(`Invalid context_window '${val}'. Must be a positive integer (token count).`, ERROR_REASON.USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
// Post-planning gap checker (#2493)
|
||||
if (kp === 'workflow.post_planning_gaps') {
|
||||
if (typeof parsedValue !== 'boolean') {
|
||||
|
||||
Reference in New Issue
Block a user