Converts CSV, xlsx/xls/ods, directories, ZIPs and globs into one INSERT
file per table. Producer-specific layouts (header/data rows found by a
marker cell) and sheet naming live in a gitignored dataconvert.json, with
dataconvert-example.json as the template. --max-rows samples each table
and SCHEMA.md records columns, types, row counts and full sizes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The IR supersedes both intermediate designs (requirements D6, D7):
station/tools/docgen and the graph model that briefly lived in graphgen.
Shipping them beside atlas2/docgen would mean two graph models, which is the
thing the architecture argues against.
Carried across rather than lost:
- style/extract.py and tokens.py, the offline theme harvester (R31). Rewritten
to emit a *theme* — a slot-to-hex binding — rather than a whole style file,
since what harvesting recovers is which colour a slot should be, not what a
kind should look like.
- graphgen/README.md, rewritten for what graphgen actually is now: the
schema explorer. It fixes the blank station-index entry at run.py:304.
Two bugs found while doing it:
- Canvas and ink are the two lightness extremes, not the two most common
values. In a Graphviz SVG every label carries a fill, so the ink outnumbers
the canvas 87 to 43 and the old rule produced a theme whose text was
invisible against its own background.
- A name defined in both branches of an if/else produced a duplicate id, which
failed validation on docgen's own source. Disambiguated by line.
gen/<room>/ is the docker build context and soleprint/Dockerfile is `COPY . .`,
so anything reaching gen/ reaches an image layer — and registry.mcrn.ar is
public-read. station/tools/tester/.env has been gitignored since the last
incident, but .gitignore does not bind shutil: copy_path() called
shutil.copytree() with no ignore=, so the key was copied into every built room.
Verified extractable from soleprint_localtest-soleprint:latest (built 8 days
ago) at /app/station/tools/tester/.env.
ctrl/deploy.sh's --exclude='.env' is why this looked handled; it only covers the
rsync path, not the build-and-push path.
Two layers now:
- copy_path()/merge_into() filter .env, __pycache__, *.pyc, .git, node_modules
and virtualenvs out of bulk directory copies. Single-file copies named by a
caller are untouched, so cfg/<room>/.env.example still ships.
- soleprint/.dockerignore repeats the rule at the docker boundary and is
copied into the context beside the Dockerfile. Follows the convention
soleprint/atlas/.dockerignore already set (.env, .env.*, !.env.example).
Runtime is unaffected: no Dockerfile COPYs a .env, and the room compose files
supply it with `env_file: - .env`, read from the host at run time.
The key itself still needs rotating — it remains in git history.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`make build ctrl` ran the build and then printed "make: 'ctrl' is up to date."
The empty rule from $(eval $(ARGS):;@:) is not enough when the word names a real
directory — and cfg, ctrl, docs, gen and init all exist at this level. Only
.PHONY stops make consulting the filesystem.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
spr had no .gitattributes at all, despite shipping ctrl/*.sh and generating
gen/<room>/ctrl/*.sh. A checkout on Windows/WSL rewrites those to CRLF, and a
shell script with CRLF fails as `bad interpreter: /usr/bin/env bash^M` — which
reads as a broken installer rather than a line-ending problem.
Copied verbatim from rig/.gitattributes and deliberately duplicated rather than
shared: rig/ has to carry its own so it survives being handed over alone.
No tracked file in either repo currently has CRLF, so `git add --renormalize .`
rewrote nothing. Landing it now, while that is true, keeps it off the diff of
whatever lands next.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A rig is a copy of rig/ renamed after the environment it models, so its k8s
files spell out a real architecture — the one thing that must not be committed
here. rig/.gitignore already refuses them, but only within rig/: a copy is a
SIBLING of rig/, where that file has no reach. spr had no rule at all, so the
first `git add -A` after the fold would have committed one.
Anchored at the root, and the negation names the full path because `*-rig/` is
unanchored and would otherwise match rig/sample-rig too.
Verified both ways: a file under client-rig/ is ignored, one under
rig/sample-rig/ is not.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>