fix: README.md leaked into the teacher's system prompt

composeInstructions loaded EVERY .md in src/agent/prompts — including
README.md, the directory's human documentation, which rode into the
system prompt as its own section (visible in OpenRouter logs, old POC
paths and all).

The loader now enforces the documented convention: only numeric-prefix
fragments (\d+-*.md) compose. README.md stays in the directory as docs
and can never reach the model again. README's rule text updated to
match; composeInstructions exported for tests; regression test asserts
the README never leaks and every numeric fragment composes verbatim.
This commit is contained in:
amosblomqvist
2026-09-02 03:19:03 +03:00
parent 8f8659c8b4
commit 28ccc76594
3 changed files with 29 additions and 4 deletions
+10 -2
View File
@@ -20,16 +20,24 @@ import { questionTools } from "./tools.ts";
* src/agent/prompts/*.md fragment in sorted filename order (workers add
* fragments — nobody edits shared sections of the base prompt). Prefix
* fragments with a numeric order key: 10-plan-tree.md, 20-jobs.md, …
*
* ONLY numeric-prefix files load — the loader enforces the convention, so
* non-fragment documentation (README.md, …) can live in the directory
* without ever leaking into the model's prompt.
*/
const PROMPTS_DIR = path.join(PATHS.root, "src", "agent", "prompts");
function composeInstructions(): string {
/** A prompt fragment: numeric order key + .md (e.g. 20-jobs.md). Nothing else loads. */
const FRAGMENT_RE = /^\d+-[\w.-]+\.md$/;
/** Exported for tests: assert what composes into the model's prompt. */
export function composeInstructions(): string {
const base = fs.readFileSync(PATHS.teacherPrompt, "utf-8");
if (!fs.existsSync(PROMPTS_DIR)) return base;
const fragments = fs
.readdirSync(PROMPTS_DIR)
.filter((f) => f.endsWith(".md"))
.filter((f) => FRAGMENT_RE.test(f))
.sort()
.map((f) => fs.readFileSync(path.join(PROMPTS_DIR, f), "utf-8").trim())
.filter(Boolean);