Date: 2026-10-05, about 03:25 → 06:30 AEDT (written 11:57 AEDT) Session: v10.0 Chapter 1 — atomic store writes for the four in-place writers, manual update Participants: Andrew.human & Claude.ai Status: OPEN. Three of four writers delivered and tested in isolation; MiscIndexer.py must be redone against its current file. Nothing here has been deployed or run against the real DB.
The MiscIndexer.py inside ISE_Atomic_Writers_v1.0.0.zip is built on a stale base. Do not deploy it.
--single flag.--single ID_ATTACH.--single and anything else added in 7.5.0, and its version number would go backwards.gzip_Live generation taken before the deploy, and check MiscIndexer.py --single N still works.Root cause on my side: I patched an uploaded file without first comparing its @version against the version the manual records for it. That check is now a proposed standing step (section 6).
Carry-over from the v9.9 closing checkpoint, item 10: convert the four scripts that still write their stores in place (IndexBuilder.py, MediaIndexer.py, MiscIndexer.py, orphan_check.py) to temp-file-then-rename. Reason (OVERSEER checkpoint 5.7): until that is done, a mirror, rsync or gzip during an index run can catch a half-written store, and the gated mirror can't be relied on.
Scope decision (Andrew): mirrorISE_Clone, mirrorISE_Dev and json_push_media_Clone_to_Live (which replaced json_push_audio_Clone_to_Live) are Andrew's to look after. Claude does not touch them. This removed v9.9 items 8 and 9 (gated mirror, ls diff) from this session.
IndexBuilder.py 7.4.3, MediaIndexer.py ISE v9.9.2, orphan_check.py 7.4.3Problem & Resolution. Each script opened its store with open(path, "w") and dumped into it, so the real file is truncated and half-written for the duration of the dump. Fix: a helper, write_json_files_atomic(items, **dump_kwargs), copied inline into each script (no shared module, to avoid a new dependency across unrelated scripts). It writes every object to <path>.tmp, flushes and fsyncs, then renames all of them over their final names back to back with os.replace(). On any failure the .tmp files are removed and the live files are untouched. The old file's permissions are copied to the new one, and its owner too where permitted.
Risk Analysis.
.tmp sits in the same directory, so that holds.IndexBuilder: word_index.json + post_store.json) the pair can still disagree for the microseconds between the two renames. This cannot be closed without a different design and was judged not worth it..tmp (crash mid-write) is harmless, but an rsync pull could copy it before cleanup; the next --delete mirror removes it. Whether the mirror scripts should exclude *.tmp is Andrew's call.Code Change. The write block in each script replaced by one helper call. orphan_check.py writes orphan_state.json with indent=2; MediaIndexer.py writes media_store.json with indent=4; IndexBuilder.py writes both stores with default json.dump settings. Python 3.6 compatible (Live runs 3.6 and 3.8).
IndexBuilder.py clean-ups (approved by Andrew: "fix all the anomalies")DB_PASS removed from the DEBUG: print. It was going to the terminal and possibly to cron mail._is_dev() removed, with its now-unused subprocess import. It referred to an undefined _DEV_IP; the NameError was swallowed by its own except, so it always returned False. Risk: if any other module imports IndexBuilder._is_dev, that import now fails. None is known.VERSION = "7.4.1", so the script printed the wrong version. Both are now 7.4.3.ISE_Python_Scripts_Manual_v6.md (from v5)IndexBuilder.py clean-ups. It states that MiscIndexer.py is not covered yet.orphan_check.py section and "Atomic store write (ISE v9.9.2)" note in the MediaIndexer section.Done:
py_compile)..tmp left); a planted failure on the second object (both originals untouched, no .tmp left); a brand-new file with indent=2.MediaIndexer.py run in a sandbox with a stubbed Collabware.core_utils in --single mode: the new entry merged, existing entries survived, mode preserved, no .tmp left.Not done:
ise_settings.py or real stores. The first real run on Live is the end-to-end test.MegaIndexer.sh was not run.MediaIndexer.py can wipe media_store.json. If the DB query fails, or pymysql is missing, fetch_media_from_db() prints the error and returns {}; a plain run then writes {} over the store. Reproduced in the sandbox (0 items after the run). The atomic write does not prevent it. In --single mode a corrupt existing store is treated as {}, so the rewrite would leave only that one attachment. A proposed fix (abort with exit code 1 and leave the store untouched) was not approved and is not in any delivered file.patch_media2.py and a MediaIndexer.py marked ISE v9.9.3 implementing exactly that safeguard. Neither was created in this conversation, and I don't know their origin. I restored the approved v9.9.2 (verified byte-identical to v1.0.0) and left the v9.9.3 code out of every delivered zip. If it came from you or another session, say so; either way the safeguard stays unshipped until you approve it.MegaIndexer.sh passes --monitor to IndexBuilder.py, which has no argument parsing, so the flag is silently ignored. Harmless; untouched. MegaIndexer.sh runs Posts, PDF (--fast) and Text, but not MediaIndexer.py.PDFIndexer.py writes pdf_store.json but was not on the four-writer list and not examined. Unknown whether it is already atomic.IndexBuilder.py (despite the opening saying it covers every script); MediaProcessor.py still quoted as v9.18, unconfirmed.Confirmed in use:
Proposed (not yet agreed): before patching any uploaded file, compare its @version with the version the manual (or checkpoint version map) records for it, and stop if they differ. This would have caught the stale MiscIndexer.py before it was patched. It costs one grep per file.
| File | State |
|---|---|
ISE_Atomic_Writers_v1.0.0.zip |
IndexBuilder.py, MiscIndexer.py, MediaIndexer.py, orphan_check.py. Do not deploy its MiscIndexer.py. Other three fine |
ISE_Atomic_Writers_v1.0.1.zip |
IndexBuilder.py 7.4.3, MediaIndexer.py ISE v9.9.2, orphan_check.py 7.4.3, ISE_Python_Scripts_Manual_v6.md. No MiscIndexer.py |
DM2S-NX_v10_0_Checkpoint_2026-10-05.md |
This file |
Module versions at close:
| Module | Version | Note |
|---|---|---|
IndexBuilder.py |
7.4.3 | delivered, not deployed |
MediaIndexer.py |
ISE v9.9.2 | delivered, not deployed |
orphan_check.py |
7.4.3 | delivered, not deployed |
MiscIndexer.py |
7.5.0 (production, per manual) | not converted; v1.0.0 copy is stale |
ISE_Python_Scripts_Manual |
v6 (Master v6) | delivered |
Version map entries from the v9.9 close (search.py 7.6.0, PDFsearch.py 7.7.0, MiscSearch.py 7.6.0, MediaSearch.py 1.8.0, SortEngine.py 8.1, COI_Chain.py 1.0.1) are unchanged by this session.
Blocked on something from Andrew
MiscIndexer.py from Live (should say 7.5.0). Then convert it, add its manual entry, ship as the next dotted zip.ISE_Atomic_Writers_v1.0.0.zip was deployed anywhere. If so, restore MiscIndexer.py there.MediaIndexer.py store-wipe safeguard (finding 1), and say where patch_media2.py / the v9.9.3 file came from.IndexBuilder.py section, and confirm the current MediaProcessor.py version.Still open from the v9.9 list (unchanged unless noted)
MediaProcessor.py --attachid=6882 on Clone; deploy ISE_AND_OR_v1.0.0.zip on Live and Clone; rebuild collabware_baseline.md5 after deploying new code (this session's files add to that deploy).MiscIndexer.py outstanding.--snapshot, --check on all three machines, planted faults on Clone), 11–13 (COI in daily cron, OVERSEER RPM, .htaccess): unchanged. Item 11 should wait until the four writers are converted and deployed, otherwise the daily check can flag a half-written store.ls diff): Andrew's, in his own mirror scripts.[listatt] disk check, bare /sort newest, ISEmega "Show all", 6982 re-measure, orphan entry 2419), 19–20 (QAT tools, clap_diag.py), 21–24 (audio quality), 25–27 (OVERSEER v2): unchanged.New
PDFIndexer.py writes pdf_store.json atomically..tmp files left behind and for permissions/owner on the new stores.*.tmp (Andrew's call).Not observed this session. No commands were run on Live, Clone or Dev; all work was on uploaded copies in the sandbox. The v9.9 close state (Live under swap pressure with media_store.json at 2723 items; Clone with free memory and no swap, mirror needed before MediaProcessor.py) is the last known state.
DM2S/NX v10.0 Chapter 1 checkpoint — 2026-10-05 11:57 AEDT