Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions src/codex/prompt-layers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -714,22 +714,25 @@ function readToggle(configBytes: string | null, id: ToggleId): ToggleState {

function readModelInstructionsFile(configBytes: string | null): string | null {
if (configBytes === null) return null;
const parsed = rootValue(configBytes, "model_instructions_file");
if (typeof parsed === "string") return parsed;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid Bun's nonconforming escape decoding

For externally authored basic strings containing valid \t or \f escapes, this returns Bun's decoded value even though this same module documents that supported Bun versions transpose those escapes relative to Codex's Rust TOML parser. The dashboard therefore reports the wrong path, and computePromptProbeStateFingerprint reads and hashes that wrong file, so editing the real external prompt may leave a cached probe result valid; decode these path literals with Codex-compatible TOML escape semantics rather than trusting Bun's parsed string.

AGENTS.md reference: src/AGENTS.md:L10-L10

Useful? React with 👍 / 👎.

if (parsed === undefined) return null;
for (const line of rootLines(configBytes)) {
// Capture the whole literal INCLUDING its quotes and decode it, rather than
// returning the raw inner text. `setRootString` writes this key through
// `encodeBasicString`, which escapes backslashes, so on Windows the stored
// literal is "C:\\Users\\..." while the path is "C:\Users\...". Reading the
// inner text verbatim returned the doubled form: the round trip did not
// survive, `baseSelection` compared a doubled path against the real variant
// path and reported `external` for a variant this code had just selected.
// literal is "C:\\Users\\..." while the path is "C:\Users\...".
//
// `[^"]*` cannot span an escaped quote either. That is not a new limit -- it
// is the same one the writer's restricted escape set is built around, and
// `decodeBasicString` refuses anything outside it rather than guessing.
// Prefer Bun's TOML decoder above so externally-authored standard escapes
// remain present and external. This restricted scan is only a fallback for
// documents Bun cannot parse. If its narrow decoder refuses a literal,
// preserve that literal rather than treating the setting as absent.
const m = /^\s*model_instructions_file\s*=\s*("[^"]*")\s*(?:#.*)?$/.exec(line);
if (m) return decodeBasicString(m[1]!);
if (m) return decodeBasicString(m[1]!) ?? m[1]!;
}
return null;
// A present non-string value or unrecognised spelling fails closed. Only
// `undefined` above proves that the setting is absent.
return "<unreadable model_instructions_file>";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Distinguish parse failure from a missing setting

When rootValue returns PARSE_FAILED for a Codex-valid document that Bun cannot parse—such as the out-of-safe-range i64 case already documented in this module—and the document has no model_instructions_file, the fallback scan finds nothing and this unconditional sentinel classifies the absent key as external. Consequently, selectBaseVariant rejects every attempt to select a base variant with developer_instructions_not_owned; use scanHasRootKey on parse failure so only an actually present but unreadable assignment fails closed.

AGENTS.md reference: src/AGENTS.md:L10-L10

Useful? React with 👍 / 👎.

}

/** Variant ids are ours to generate, so they stay in one narrow shape. */
Expand Down
13 changes: 13 additions & 0 deletions tests/codex-prompt-base-variants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ describe("base variant selection", () => {
});
});

test("a hand-set key with standard TOML escapes stays external and cannot be overwritten", () => {
const config = 'model_instructions_file = "\\u002Fetc\\u002Fsomebody-elses.md"\n';
const paths = fixture(config);
expect(readPromptLayers(paths).baseSelection).toEqual({
kind: "external",
path: "/etc/somebody-elses.md",
});

const result = selectBaseVariant({ kind: "default" }, rev(paths), paths);
expect(result).toMatchObject({ ok: false, error: "developer_instructions_not_owned" });
expect(read(paths.configPath)).toBe(config);
});

test("selecting a variant writes an absolute path, and the default removes the key", () => {
const paths = fixture("model = \"x\"\n");
const created = writeBaseVariant({ id: null, title: "Terse", body: "Be brief." }, rev(paths), paths);
Expand Down
Loading