mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 22:48:57 +02:00
fix(config): reject malformed cross_project_learnings at set
A typo was stored with exit 0, so the feature stayed off and the first-run prompt never returned. Reject like codex_reviews; do not coerce. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
Garry Tan
co-authored by
Cursor
parent
6b9f10a8b7
commit
3599a3d4df
@@ -403,6 +403,14 @@ case "${1:-}" in
|
||||
echo "Error: codex_reviews '$VALUE' not recognized. Valid values: enabled, disabled. Existing value left unchanged." >&2
|
||||
exit 1
|
||||
fi
|
||||
# cross_project_learnings: empty get is the first-run prompt sentinel.
|
||||
# Skills enable only on the literal "true". A typo must not persist — that
|
||||
# keeps the feature off and suppresses the prompt. Reject, like
|
||||
# codex_reviews; do not coerce (a stored default still kills the sentinel).
|
||||
if [ "$KEY" = "cross_project_learnings" ] && [ "$VALUE" != "true" ] && [ "$VALUE" != "false" ]; then
|
||||
echo "Error: cross_project_learnings '$VALUE' not recognized. Valid values: true, false. Existing value left unchanged." >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$STATE_DIR"
|
||||
# Write annotated header on first creation
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* #2673: gstack-config set must reject malformed cross_project_learnings.
|
||||
*
|
||||
* Empty get is the first-run prompt sentinel (pinned in
|
||||
* gstack-config-defaults.test.ts). Skills only enable on the literal "true".
|
||||
* A typo used to store verbatim and exit 0, so the feature stayed off and
|
||||
* the prompt never returned. Unlike pair_agent / redact_prepush_hook, do
|
||||
* not coerce to a default — that would still persist a value and still
|
||||
* kill the sentinel. Follow codex_reviews: reject, leave existing.
|
||||
*/
|
||||
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
||||
import { spawnSync } from "child_process";
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
|
||||
const CONFIG = path.resolve(import.meta.dir, "..", "bin", "gstack-config");
|
||||
let stateRoot: string;
|
||||
|
||||
function cfg(args: string[]): { code: number; out: string; err: string } {
|
||||
const r = spawnSync(CONFIG, args, {
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, GSTACK_STATE_ROOT: stateRoot },
|
||||
});
|
||||
return { code: r.status ?? 0, out: r.stdout ?? "", err: r.stderr ?? "" };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
stateRoot = fs.mkdtempSync(path.join(os.tmpdir(), "gstack-config-xproj-"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(stateRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe("cross_project_learnings set domain (#2673)", () => {
|
||||
test("empty get is still the first-run sentinel", () => {
|
||||
const r = cfg(["get", "cross_project_learnings"]);
|
||||
expect(r.code).toBe(0);
|
||||
expect(r.out).toBe("");
|
||||
});
|
||||
|
||||
test("true and false round-trip", () => {
|
||||
expect(cfg(["set", "cross_project_learnings", "true"]).code).toBe(0);
|
||||
expect(cfg(["get", "cross_project_learnings"]).out).toBe("true");
|
||||
expect(cfg(["set", "cross_project_learnings", "false"]).code).toBe(0);
|
||||
expect(cfg(["get", "cross_project_learnings"]).out).toBe("false");
|
||||
});
|
||||
|
||||
test("typo is rejected and does not write", () => {
|
||||
const r = cfg(["set", "cross_project_learnings", "ture"]);
|
||||
expect(r.code).toBe(1);
|
||||
expect(r.err).toContain("not recognized");
|
||||
expect(r.err).toContain("cross_project_learnings");
|
||||
const got = cfg(["get", "cross_project_learnings"]);
|
||||
expect(got.code).toBe(0);
|
||||
expect(got.out).toBe("");
|
||||
});
|
||||
|
||||
test("typo leaves an existing valid value unchanged", () => {
|
||||
expect(cfg(["set", "cross_project_learnings", "true"]).code).toBe(0);
|
||||
const r = cfg(["set", "cross_project_learnings", "yes"]);
|
||||
expect(r.code).toBe(1);
|
||||
expect(r.err).toContain("Existing value left unchanged");
|
||||
expect(cfg(["get", "cross_project_learnings"]).out).toBe("true");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user