{"specs":[{"id":"s1","title":"slug-utility","contract":"makeSlug(input)","file":"specs/s1-slug-utility.md","md":"# SPEC S1 · slug-utility\n\nSynthetic spec, written for this demo. It is the kind of instruction document the harness runs on.\n\n## Goal\n\nA pure function that turns a human title into a URL slug. No dependencies, no I/O.\n\n## Contract\n\n`makeSlug(input: string) -> string`\n\n## Functional requirements\n\n- FR-1: lowercase the input.\n- FR-2: replace each run of whitespace (spaces, tabs, newlines) with a single hyphen.\n- FR-3: remove every character that is not a-z, 0-9 or hyphen.\n- FR-4: collapse repeated hyphens and trim hyphens from both ends.\n\n## Acceptance tests\n\nThe harness runs these vectors against the current implementation on every iteration.\n\n```json\n[\n  { \"name\": \"AT-1 basic words\", \"fr\": \"FR-1\", \"input\": \"Hello World\", \"expected\": \"hello-world\" },\n  { \"name\": \"AT-2 repeated spaces\", \"fr\": \"FR-2\", \"input\": \"  Multiple   Spaces  \", \"expected\": \"multiple-spaces\" },\n  { \"name\": \"AT-3 tabs and newlines\", \"fr\": \"FR-2\", \"input\": \"Tabs\\tand\\nNewlines\", \"expected\": \"tabs-and-newlines\" },\n  { \"name\": \"AT-4 accents and symbols\", \"fr\": \"FR-3\", \"input\": \"Café & Bar!\", \"expected\": \"caf-bar\" },\n  { \"name\": \"AT-5 dots and parens\", \"fr\": \"FR-3\", \"input\": \"Release v2.0 (final)\", \"expected\": \"release-v20-final\" },\n  { \"name\": \"AT-6 hyphen runs\", \"fr\": \"FR-4\", \"input\": \"--Already--Slugged--\", \"expected\": \"already-slugged\" },\n  { \"name\": \"AT-7 empty input\", \"fr\": \"FR-1\", \"input\": \"\", \"expected\": \"\" }\n]\n```\n\n## Notes for the implementer\n\nInputs come from a CMS title field. Do not assume the field is clean.\n","requirementCount":4,"testCount":7},{"id":"s2","title":"invoice-totals","contract":"computeInvoice(order)","file":"specs/s2-invoice-totals.md","md":"# SPEC S2 · invoice-totals\n\nSynthetic spec, written for this demo. Money math with the two classic traps: float drift and bad input.\n\n## Goal\n\nCompute invoice totals from order lines, with money-safe rounding and defensive input handling.\n\n## Contract\n\n`computeInvoice(order: { lines: [{ qty, unitPrice }], discountPercent }) -> { subtotal, discount, total }`\n\n## Functional requirements\n\n- FR-1: the raw total of a line is qty times unitPrice.\n- FR-2: a negative qty counts as zero. Returns are handled by a different system, per finance.\n- FR-3: round every money value to 2 decimals, half away from zero. Never expose raw float drift.\n- FR-4: subtotal is the sum of the rounded line totals, rounded to 2 decimals.\n- FR-5: discount is discountPercent of the subtotal, rounded to 2 decimals. Total is subtotal minus discount, rounded to 2 decimals.\n\n## Acceptance tests\n\n```json\n[\n  { \"name\": \"AT-1 integer lines\", \"fr\": \"FR-1\",\n    \"input\": { \"lines\": [ { \"qty\": 2, \"unitPrice\": 10 }, { \"qty\": 1, \"unitPrice\": 5 } ], \"discountPercent\": 0 },\n    \"expected\": { \"subtotal\": 25, \"discount\": 0, \"total\": 25 } },\n  { \"name\": \"AT-2 float drift\", \"fr\": \"FR-3\",\n    \"input\": { \"lines\": [ { \"qty\": 3, \"unitPrice\": 0.1 } ], \"discountPercent\": 0 },\n    \"expected\": { \"subtotal\": 0.3, \"discount\": 0, \"total\": 0.3 } },\n  { \"name\": \"AT-3 half cent rounds up\", \"fr\": \"FR-3\",\n    \"input\": { \"lines\": [ { \"qty\": 1, \"unitPrice\": 10.005 } ], \"discountPercent\": 0 },\n    \"expected\": { \"subtotal\": 10.01, \"discount\": 0, \"total\": 10.01 } },\n  { \"name\": \"AT-4 negative qty clamped\", \"fr\": \"FR-2\",\n    \"input\": { \"lines\": [ { \"qty\": -2, \"unitPrice\": 9.5 }, { \"qty\": 3, \"unitPrice\": 4 } ], \"discountPercent\": 0 },\n    \"expected\": { \"subtotal\": 12, \"discount\": 0, \"total\": 12 } },\n  { \"name\": \"AT-5 clean discount\", \"fr\": \"FR-5\",\n    \"input\": { \"lines\": [ { \"qty\": 4, \"unitPrice\": 25 } ], \"discountPercent\": 10 },\n    \"expected\": { \"subtotal\": 100, \"discount\": 10, \"total\": 90 } },\n  { \"name\": \"AT-6 discount needs rounding\", \"fr\": \"FR-5\",\n    \"input\": { \"lines\": [ { \"qty\": 3, \"unitPrice\": 3.33 } ], \"discountPercent\": 15 },\n    \"expected\": { \"subtotal\": 9.99, \"discount\": 1.5, \"total\": 8.49 } },\n  { \"name\": \"AT-7 mixed traps\", \"fr\": \"FR-2\",\n    \"input\": { \"lines\": [ { \"qty\": -1, \"unitPrice\": 50 }, { \"qty\": 2, \"unitPrice\": 7.25 }, { \"qty\": 1, \"unitPrice\": 0.005 } ], \"discountPercent\": 20 },\n    \"expected\": { \"subtotal\": 14.51, \"discount\": 2.9, \"total\": 11.61 } },\n  { \"name\": \"AT-8 empty order\", \"fr\": \"FR-4\",\n    \"input\": { \"lines\": [], \"discountPercent\": 0 },\n    \"expected\": { \"subtotal\": 0, \"discount\": 0, \"total\": 0 } }\n]\n```\n\n## Notes for the implementer\n\nOne failing test can hide another. AT-7 fails for the negative qty first; only after that fix does its rounding failure surface. The loop is expected to discover this across iterations, not guess it.\n","requirementCount":5,"testCount":8},{"id":"s3","title":"csv-dedupe-audit","contract":"processCsv(text)","file":"specs/s3-csv-dedupe-audit.md","md":"# SPEC S3 · csv-dedupe-audit\n\nSynthetic spec, written for this demo. It contains a contradiction ON PURPOSE. A serious harness must refuse to burn budget on an unsatisfiable spec, and must say which requirements collide.\n\n## Goal\n\nParse a semicolon CSV of transactions and prepare it for reporting.\n\n## Contract\n\n`processCsv(text: string) -> { rows: [{ id, amount }], rowCount }`\n\n## Functional requirements\n\n- FR-1: parse each non-empty line as `id;amount`, amount as a number.\n- FR-2: drop rows with a duplicate id, keep the first occurrence. Reporting must not double count.\n- FR-3: rowCount is the number of rows in the output.\n- FR-4: every input line must appear in the output. The audit team requires a complete record, so rowCount must equal the number of parsed input lines.\n\n## Acceptance tests\n\n```json\n[\n  { \"name\": \"AT-1 basic parse\", \"fr\": \"FR-1\", \"input\": \"X;1\\nY;2\",\n    \"expected\": { \"rows\": [ { \"id\": \"X\", \"amount\": 1 }, { \"id\": \"Y\", \"amount\": 2 } ], \"rowCount\": 2 } },\n  { \"name\": \"AT-2 duplicates dropped\", \"fr\": \"FR-2\", \"input\": \"A;10\\nB;5\\nA;7\\nC;2\\nB;1\",\n    \"expected\": { \"rows\": [ { \"id\": \"A\", \"amount\": 10 }, { \"id\": \"B\", \"amount\": 5 }, { \"id\": \"C\", \"amount\": 2 } ], \"rowCount\": 3 } },\n  { \"name\": \"AT-3 decimal amounts\", \"fr\": \"FR-1\", \"input\": \"Z;3.5\",\n    \"expected\": { \"rows\": [ { \"id\": \"Z\", \"amount\": 3.5 } ], \"rowCount\": 1 } },\n  { \"name\": \"AT-4 audit completeness\", \"fr\": \"FR-4\", \"input\": \"A;10\\nB;5\\nA;7\\nC;2\\nB;1\",\n    \"expected\": { \"rows\": [ { \"id\": \"A\", \"amount\": 10 }, { \"id\": \"B\", \"amount\": 5 }, { \"id\": \"A\", \"amount\": 7 }, { \"id\": \"C\", \"amount\": 2 }, { \"id\": \"B\", \"amount\": 1 } ], \"rowCount\": 5 } }\n]\n```\n\n## Notes for the implementer\n\nFR-2 and FR-4 cannot both hold on inputs that contain duplicate ids. The correct behavior for the harness is to detect the collision, stop, and hand the decision to a human with both requirements named.\n","requirementCount":4,"testCount":4}],"skills":[{"id":"defensive-io","file":"skills/defensive-io.md","md":"# SKILL · defensive-io\n\nA skill is a file. The harness loads it into the working context, and its directives change how the loop implements from iteration 1.\n\n## Directive\n\nWhen a spec touches user-supplied or numeric input, include the input-validation rules from the start instead of waiting for a test to fail. Bad input is the most common production failure class; do not treat it as an edge case to discover later.\n\n## Machine section\n\nThe loop reads this block. Rules tagged `defensive` in the active spec's rule library are preloaded when this skill is on.\n\n```json\n{ \"skill\": \"defensive-io\", \"preload_tags\": [\"defensive\"] }\n```\n\n## Expected effect in this demo\n\n- S1: `strip-specials` is active from iteration 1, so S1 goes green one iteration earlier.\n- S2: `clamp-negative` is active from iteration 1, so the loop only has the rounding class left to discover.\n"},{"id":"naming-discipline","file":"skills/naming-discipline.md","md":"# SKILL · naming-discipline\n\nA style skill. It does not change behavior of the generated function; it changes the emitted code itself, which proves skills reach the code generator, not just the prose around it.\n\n## Directive\n\nWrite internal identifiers in snake_case. Team convention for shared review with the Python side of the codebase.\n\n## Machine section\n\n```json\n{ \"skill\": \"naming-discipline\", \"identifier_style\": \"snake_case\" }\n```\n\n## Expected effect in this demo\n\nToggle it and re-run any spec: the generated source switches its local variable names between camelCase and snake_case. The acceptance tests still pass, because style rules must never change semantics.\n"}],"knowledgeSeed":"# KNOWLEDGE · harness notes\n\nThis file starts empty on purpose. The loop earns entries; nothing is pre-written.\n\n## Append rule\n\nAn entry is appended only when a run ends green AND the entry names the acceptance tests that proved it. A model believing it learned something is not an entry. A test flipping from red to green is.\n\n## Entry format\n\n```\n- rule: <rule id> · spec: <spec id> · proved by: <test names> · added at iteration <n>\n```\n\n## Entries\n\n(none yet)\n","routerConfig":{"plan":["hosted:anthropic-claude","local:deterministic-v1"],"codegen":["hosted:anthropic-claude","hosted:openai-gpt","local:deterministic-v1"],"analysis":["hosted:openai-gpt","local:deterministic-v1"],"report":["local:deterministic-v1"]},"providers":{"hosted:anthropic-claude":{"kind":"hosted","configured":false,"note":"route slot, no credentials in this public demo"},"hosted:openai-gpt":{"kind":"hosted","configured":false,"note":"route slot, no credentials in this public demo"},"local:deterministic-v1":{"kind":"local","configured":true,"note":"rule engine, runs everywhere, no key"}},"costs":{"plan":2,"codegen":3,"test-run":1,"analysis":2,"report":1},"defaultCaps":{"iterations":6,"units":40},"capLimits":{"iterations":[1,12],"units":[4,500]},"content":{"source":"fs","filesLoaded":6,"filesExpected":6,"missing":[]}}