config_io handled dest_description but never the module's own
description field: it was absent from export_config, from both
repo.create_module and repo.update_module calls in apply_config, and
from the change-detection watch tuple. Descriptions set via the web
form were therefore invisible to the config tree — `pipekit apply`
silently ignored a description in a module's JSON, and `pipekit export`
would have dropped all 127 existing ones on a round-trip.
Adding "description" to the watch tuple means a module file without the
key now reads as a change to NULL, so backfill the key into all 131
existing module files from the DB. Without this, the next apply would
have wiped every description it was meant to preserve.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Five dest_names in the column map were junk from a crude source-name
sanitizer (ext._price, margin__, _hd_grower, ext._cost,
ext._margin_dollar) and didn't match the physical gp table's clean names
(ext_price, margin_pct, hd_grower, ext_cost, ext_margin_dollar). The load
is positional so this was harmless for years — until the new dest-table
preflight's name check started (correctly) flagging the drift and failing
the daily run. Align the map to the table.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add `pipekit export` / `pipekit apply` (pipekit/config_io.py) to serialise the
DB's config — drivers, connections, modules (+ columns, watermarks, hooks),
groups, schedules — to a git-trackable text tree under config/, and rehydrate
it. SQLite stays runtime state; definitions become diffable/reviewable/
revertible.
- config only: run_log/group_run/settings and per-run state columns excluded
- name-keyed refs (portable across databases); source_query in .sql sidecars;
columns as real JSON arrays for line-by-line diffs
- newline-normalised so CRLF-vs-LF is never a spurious change
- apply is create/update by name; child collections fully synced; top-level
deletes gated behind --prune; --dry-run prints the plan
- round-trip is identity (export -> apply --dry-run == nothing to do)
Commits the current config/ as the first baseline, capturing the freshly
populated columns_json for rm00101/rm00301/iv00101. Documented in SPEC.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>