portfolio-optimiser/examples/ingest-golden-sql/build_fixture.py
Kjell Tore Guttormsen 392f8493da chore(repo): planning artifacts become local-only; fixture builders become code
Operator ruling 2026-08-05, which settles decision (g): planning documents are
generally never public, and what OUR OWN sessions generate does not go out on
the forge at all. The example itself stays public so others can run the
process.

`.claude/projects/` is the Voyage session workbench -- 25 briefs/plans/reviews
this project's own sessions produced. Untracked and gitignored, exactly as
STATE.md already is, and for the same stated reason: this repo has a public
mirror, so that class of material is local-only rather than tracked.

The line is drawn at who wrote the document, and it is drawn deliberately:
`docs/plan/`, `docs/research/` and `docs/rapport/` stay tracked. Those are
curated, dated documents written for the repo's readers, three of them linked
from the README as the decision record. Move that line if it was meant wider.

Two files were NOT process artifacts and are not deleted. Both
`build_fixture.py` scripts are cited by tracked tests
(`test_ingest_golden_sql.py`, `test_ingest_golden_http.py`) as the documented
rebuild path for byte-exact goldens -- reproduction code that had landed in the
wrong directory. Moved next to the goldens they build; both docstrings updated,
so no tracked file is left pointing into an untracked tree (verified: the only
remaining `.claude/projects` string in a tracked file is the .gitignore rule
itself). One prose reference in the dated Foundry auth recipe was dropped for
the same reason.

652 tests still pass.

Does NOT address the 27 of these already readable on open/ since the S12
release -- untracking stops future publication only. That retraction is a
separate operator decision and is deliberately not taken here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWsexbQjPo9rsV3aUE54ZS
2026-08-05 10:08:17 +02:00

53 lines
1.9 KiB
Python

"""Build the I4 sql golden fixture: examples/ingest-golden-sql/fixture/portefolje.sqlite
+ ingested-at.txt.
Regenerable; the committed db BYTES need not be reproduced bit-for-bit — the golden test
compares the derived bundle (from db CONTENT via the manifest queries), not the db file.
Parameterized inserts keep the backslash/pipe escape-probe values exact (no SQL string-literal
surprises: 'c:\\temp' would be a tab if written as a Python literal). The data is chosen to
discriminate a typed §5 implementation from naive ones — SQL NULL, an integral REAL (4200.0),
non-integral REALs, and pipe/backslash text.
"""
from __future__ import annotations
import sqlite3
from pathlib import Path
GOLDEN = Path(__file__).resolve().parents[3] / "examples" / "ingest-golden-sql"
def main() -> None:
fixture = GOLDEN / "fixture"
fixture.mkdir(parents=True, exist_ok=True)
db = fixture / "portefolje.sqlite"
if db.exists():
db.unlink()
conn = sqlite3.connect(db)
try:
conn.execute("CREATE TABLE costs (id INTEGER, item TEXT, amount REAL, note TEXT)")
conn.executemany(
"INSERT INTO costs VALUES (?, ?, ?, ?)",
[
(1, "LED-armatur", 1200.5, "godkjent"),
(2, "sensor", 89.9, None), # NULL → empty cell (discriminator)
(3, "kabel", 4200.0, "rest"), # integral REAL → "4200.0", not "4200"
],
)
conn.execute("CREATE TABLE meta (k TEXT, v TEXT)")
conn.executemany(
"INSERT INTO meta VALUES (?, ?)",
[
("owner", "anlegg | drift"), # pipe → escaped \|
("path", "c:\\temp"), # backslash → escaped \\
],
)
conn.commit()
finally:
conn.close()
(GOLDEN / "ingested-at.txt").write_bytes(b"2026-07-04T12:00:00Z\n")
print(f"built {db} ({db.stat().st_size} bytes) + ingested-at.txt")
if __name__ == "__main__":
main()