Conversation
…sort fix (5 dupes removed) Heartbeat row + applied sort-tick-history-canonical.py (cherry-picked from PR #541 since #541 hasn't merged to main yet — eating dogfood per Otto-346: when the tool exists, USE the tool). 121 → 116 unique rows (5 exact duplicates removed). Strict check passes cleanly. Future heartbeat ticks will batch on this branch until 3-5 rows accumulate, then PR. Per Otto-341 substance-not-throughput (avoiding per-tick-PR spam pattern). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: df12bd42a9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if not line.strip(): | ||
| continue | ||
| ts = get_timestamp(line) | ||
| if ts: | ||
| data_rows.append((ts, original_index, line)) |
There was a problem hiding this comment.
Preserve non-timestamp lines when sorting tick history
The sorter currently skips every non-empty line that does not match a timestamp (if ts: data_rows.append(...)) and then rewrites the file from only those collected rows, so any footer text, malformed row, or future metadata line after the table separator is silently deleted when the tool runs. This makes a formatting typo or added annotation become irreversible data loss in the history file, which is especially risky for a script intended as a safe canonicalization step; it should either preserve unmatched lines or fail loudly instead of dropping them.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Adds a repo-native hygiene tool to canonicalize the loop tick-history table (sort + dedupe), and updates the tick-history log with a batched set of heartbeat rows plus a one-time canonical-order correction.
Changes:
- Add
tools/hygiene/sort-tick-history-canonical.pyto sort and dedupedocs/hygiene-history/loop-tick-history.md. - Apply the canonical sort/dedupe once and append 6 new heartbeat rows in the tick-history log.
- Normalize ordering in the tick-history around affected rows (including moving an out-of-order entry into correct chronological position).
Reviewed changes
Copilot reviewed 1 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| tools/hygiene/sort-tick-history-canonical.py | New Python tool to stably sort tick-history rows by timestamp and remove exact duplicates. |
| docs/hygiene-history/loop-tick-history.md | Applies canonical ordering/deduplication and appends the batched heartbeat rows for 2026-04-26. |
| Why this exists (Aaron 2026-04-26): | ||
| "maybe this should be substraite built in instead of dynamic python" | ||
|
|
||
| I had been doing the canonical-order sort via inline `python3 << PYEOF` | ||
| heredocs each time the rebase / one-case-Otto-229-override needed | ||
| applying. That's the wrong shape — the sort logic is structural, not | ||
| ad-hoc. It belongs as a tool checked into substrate per Otto-341 | ||
| (mechanism over vigilance). | ||
|
|
There was a problem hiding this comment.
P1 (maintainability): The module docstring embeds a specific individual’s name/date as the rationale ("Why this exists ..."). That makes the tool less reusable and tends to go stale; prefer describing the motivation impersonally (e.g., “This tool exists to replace recurring inline python heredocs for canonical sort”) and, if needed, move attribution/history to a history surface.
| Why this exists (Aaron 2026-04-26): | |
| "maybe this should be substraite built in instead of dynamic python" | |
| I had been doing the canonical-order sort via inline `python3 << PYEOF` | |
| heredocs each time the rebase / one-case-Otto-229-override needed | |
| applying. That's the wrong shape — the sort logic is structural, not | |
| ad-hoc. It belongs as a tool checked into substrate per Otto-341 | |
| (mechanism over vigilance). | |
| Why this exists: | |
| This tool replaces recurring inline `python3 << PYEOF` | |
| canonical-sort heredocs with a checked-in structural tool. | |
| The sort logic is not ad-hoc; it belongs in | |
| `tools/hygiene/` per Otto-341 (mechanism over vigilance). |
| original = p.read_text() | ||
| try: | ||
| new_text, stats = sort_canonical(original) | ||
| except ValueError as exc: | ||
| print(f"ERROR: {exc}", file=sys.stderr) | ||
| return 1 | ||
|
|
||
| print(f"rows_in: {stats['rows_in']}") | ||
| print(f"rows_out: {stats['rows_out']}") | ||
| print(f"duplicates_removed: {stats['duplicates_removed']}") | ||
| print(f"reordered: {stats['reordered']}") | ||
|
|
||
| if new_text == original: | ||
| print("OK: file already in canonical order; no changes") | ||
| return 0 | ||
|
|
||
| if args.dry_run: | ||
| print("DRY RUN: would write changes; --dry-run prevented") | ||
| return 0 | ||
|
|
||
| p.write_text(new_text) | ||
| print(f"WROTE {p} ({len(original)} -> {len(new_text)} bytes)") |
There was a problem hiding this comment.
P1 (best_practices): Path.read_text() / write_text() are called without an explicit encoding. For deterministic behavior across platforms/CI locales, read and write with encoding='utf-8' (and optionally newline='\n' if you want to normalize line endings intentionally).
| print(f"rows_in: {stats['rows_in']}") | ||
| print(f"rows_out: {stats['rows_out']}") | ||
| print(f"duplicates_removed: {stats['duplicates_removed']}") | ||
| print(f"reordered: {stats['reordered']}") |
There was a problem hiding this comment.
P2 (maintainability): The reordered stat is reported as “whether row order changed”, but it also becomes true when duplicates are removed (rows_out != rows_in). Consider splitting this into separate booleans (e.g., reordered vs deduped) or renaming the field to reflect “changed” rather than only reordering.
| print(f"reordered: {stats['reordered']}") | |
| print(f"changed: {stats['reordered']}") |
Closing as superseded by downstream main statePer Otto-232 hot-file cascade discipline + Otto-342 heartbeat-as-existence-marker: Three-signal confirmation for bulk-close:
Otto-342 satisfaction: heartbeat row IS existence-marker — that function is satisfied by commits existing on this branch (timestamped, attributed). Branch persistence (recoverable via Otto-238 retractability: branch + commits remain in git history. Cherry-pick or re-open is always possible if a future reader needs these heartbeat rows on main. Cost-benefit: ~1hr cherry-pick rebase × 3 sibling PRs vs ~15min bulk-close × 3 with preservation comments. Heartbeat content is summarized in subsequent rows. Closing decision authority: Aaron 2026-04-26 "you have authority to delegate or my[ake] these decisions and we will bulk align later". UI-bulk-align later is the retraction surface. Substrate cluster Otto-339→347 + agent-wallet research + B-0028..B-0033 backlog rows captured in subsequent PRs (#547/#548/#553/#558/#561/#562/#563/#564/#565/#566). Branch retained: |
Pull request was closed
Summary
Bundle PR for 6 heartbeat tick rows accumulated 03:51:37Z..03:58:00Z, with a one-time canonical-sort fix applied via
tools/hygiene/sort-tick-history-canonical.py(cherry-picked from PR #541 since #541 hasn't merged to main yet — eating the dogfood per Otto-346).Why bundling
Per Otto-341 substance-not-throughput discipline at the PR-cadence layer:
This is a bridge until task #276 (tick-history direct-to-main with low gate) ships. Once that lands, the heartbeat-batch-branch becomes unnecessary.
What's in the bundle
6 tick rows + 1 sort-tool application (5 duplicates removed):
Composes with
Self-tests
tools/hygiene/check-tick-history-order.sh: passes cleanly (93 rows, non-decreasing, no advisory)tools/hygiene/sort-tick-history-canonical.pywas applied once at row 1 to fix pre-existing historical violationscat >> file << EOFheredoc patternTest plan
docs/hygiene-history/loop-tick-history.mdandtools/hygiene/sort-tick-history-canonical.py(tool added on this branch)🤖 Generated with Claude Code