Debounce the artifact auto re-export so a re-export doesn't run on every incremental index.
Design accepted on 2026-08-20 on PR #870 by @moofone. Re-filed as an issue because the hook point that PR targeted no longer exists on main — export moved out of dump_and_persist into export_after_publish (src/pipeline/pipeline.c:2529-2544), whose signature (cbm_pipeline_t *p, const char *final_path) has no access to changed_total, file_count or stamps[] — all of which the debounce needs, and all local to cbm_pipeline_run_incremental. Reaching them needs new cross-module plumbing, so this is a design task rather than a rebase, and it deserves its own issue instead of blocking on a stale branch.
Accepted shape
Skip the per-run re-export unless one of:
changed_total exceeds a threshold, or
- cumulative drift since the last export exceeds a threshold (@moofone added this trigger mid-review), or
- the artifact is older than a max age.
Changes requested against the original, to carry forward
- MAX_AGE ~1h, not 24h.
- Deletions must count toward drift, with a test.
- A run-summary line naming the blocking threshold, so a skipped export is explainable rather than mysterious.
- README documentation — the original added two env knobs and changed a user-visible default cadence with no docs.
Defect to fix in any implementation
parse_iso8601_utc (src/pipeline/artifact.c:244) range-checks month/day/hour/min/sec but never the year. In pipeline_incremental.c:800 the result feeds (exported_epoch + 1) * CBM_NS_PER_SEC; since INT64_MAX / 1e9 ≈ year 2262, a hostile or corrupt "indexed_at":"9999-01-01T00:00:00Z" yields ≈2.53e20 and overflows signed int64 — reachable on the first index after a clone. Release builds wrap; the Makefile.cbm diag UBSan lane traps. sscanf("%d") on an out-of-range year is itself UB. Range-check the year.
Credit to @moofone for the design and for adding the cumulative-drift trigger during review.
Debounce the artifact auto re-export so a re-export doesn't run on every incremental index.
Design accepted on 2026-08-20 on PR #870 by @moofone. Re-filed as an issue because the hook point that PR targeted no longer exists on main — export moved out of
dump_and_persistintoexport_after_publish(src/pipeline/pipeline.c:2529-2544), whose signature(cbm_pipeline_t *p, const char *final_path)has no access tochanged_total,file_countorstamps[]— all of which the debounce needs, and all local tocbm_pipeline_run_incremental. Reaching them needs new cross-module plumbing, so this is a design task rather than a rebase, and it deserves its own issue instead of blocking on a stale branch.Accepted shape
Skip the per-run re-export unless one of:
changed_totalexceeds a threshold, orChanges requested against the original, to carry forward
Defect to fix in any implementation
parse_iso8601_utc(src/pipeline/artifact.c:244) range-checks month/day/hour/min/sec but never the year. Inpipeline_incremental.c:800the result feeds(exported_epoch + 1) * CBM_NS_PER_SEC; sinceINT64_MAX / 1e9≈ year 2262, a hostile or corrupt"indexed_at":"9999-01-01T00:00:00Z"yields ≈2.53e20 and overflows signed int64 — reachable on the first index after a clone. Release builds wrap; theMakefile.cbm diagUBSan lane traps.sscanf("%d")on an out-of-range year is itself UB. Range-check the year.Credit to @moofone for the design and for adding the cumulative-drift trigger during review.