Commit Graph

30 Commits

Author SHA1 Message Date
Timmy 8421104645 fix(914): thread-local ALL_OPS/VECTOR_CLOCK in cfg(test) so compaction tests don't race
Root cause was not the persist channel (the test-mode channel is unbounded
and its receiver is leaked, so sends never fail). It was that `ALL_OPS` and
`VECTOR_CLOCK` were process-wide `OnceLock` globals while `CRDT_STATE` was
already thread-local — so one test thread's `apply_compaction` would prune
another test thread's freshly-written ops out of the shared journal, and
the subsequent `all_ops_json()` read in `compaction_reduces_ops` would
return fewer than the 5 it had just written.

Mirror the pattern already used for `CRDT_STATE` and `SnapshotState`: in
`cfg(test)` use thread-local `OnceLock<Mutex<...>>`s for the op journal and
vector clock, accessed via new `all_ops_lock()` / `vector_clock_lock()`
helpers. Production code path is unchanged (still the global statics set
during `init()`).

Touches ops/read/snapshot call sites to go through the helpers. Note in
passing that this overlaps backlog story 518; that story is about the
production-side persist path, this is the cfg(test)-only journal-isolation
slice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 16:09:38 +01:00
dave 2c5326f339 huskies: merge 890 2026-05-12 14:48:52 +00:00
Timmy 98d496b1ad fix(901): unblock_story works on CRDT-only stories post-865
Bug 901: `unblock_story` (and the chat `unblock` command) routed through
`parse_front_matter` and errored with "Missing front matter" on any
post-865 story (story content is now CRDT-only with no YAML on disk).

In `chat/commands/unblock.rs::unblock_by_story_id`:
  - Drop the early `parse_front_matter` gate.
  - Read story name and blocked state from the CRDT register API instead
    of parsed YAML (`crdt_state::read_item`, `pipeline_state::read_typed`).
  - Keep the legacy fallback cleanup, but gate it on the content actually
    starting with a `---` YAML block, so CRDT-only stories don't hit a
    parse error there either.
  - Remove the now-unused `parse_front_matter` import.

Surfaced a second sub-bug: even when the state-machine transition
fired (`Blocked + Unblock → Coding`), the CRDT `blocked` register was
never explicitly cleared. Pre-865 the YAML-strip content_transform
cleared it as a side effect; post-865 there is no YAML to strip.

  - Add `crdt_state::set_blocked(story_id, bool)` parallel to
    `set_retry_count`. Wired through `crdt_state::write` and the
    crate-level re-export.
  - `agents::lifecycle::transition_to_unblocked` now calls
    `set_blocked(story_id, false)` alongside `set_retry_count(0)` so
    the legacy register stays in sync with the typed stage.

Test: `unblock_command_works_on_crdt_only_story_no_yaml` seeds a CRDT
entry with no YAML on disk, runs unblock, asserts success + cleared
blocked + retry_count=0. All 10 existing unblock tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:13:01 +01:00
dave f8a295eaec huskies: merge 889 2026-05-01 15:02:40 +00:00
dave c84786364a huskies: merge 874 2026-04-29 17:00:28 +00:00
dave deffcdc326 huskies: merge 844 2026-04-29 16:29:52 +00:00
dave 8a7e1aa036 huskies: merge 873 2026-04-29 16:11:34 +00:00
dave 9bd3c10a09 huskies: merge 872 2026-04-29 15:59:37 +00:00
dave 7f8467b068 huskies: merge 871 2026-04-29 15:45:54 +00:00
dave 2655288412 huskies: merge 870 2026-04-29 15:26:57 +00:00
dave f3e4d5d072 huskies: merge 869 2026-04-29 14:58:11 +00:00
dave 4324fa7511 huskies: merge 838 2026-04-29 13:58:05 +00:00
dave b4854cf693 huskies: merge 862 2026-04-29 13:28:37 +00:00
dave 186cb38eeb huskies: merge 836 2026-04-29 11:50:04 +00:00
dave 11d111360d huskies: merge 858 2026-04-29 10:47:18 +00:00
dave cf470f5048 huskies: merge 776 2026-04-28 14:01:18 +00:00
dave b7db6d6aae huskies: merge 775 2026-04-28 12:25:59 +00:00
dave fb5a21cfbb huskies: merge 778 2026-04-28 10:01:10 +00:00
dave d2d5ef8afa huskies: merge 764 2026-04-28 09:54:47 +00:00
dave 9b24c2e281 huskies: merge 743 2026-04-27 23:48:53 +00:00
dave 88f9e5dd54 huskies: merge 731_refactor_migrate_existing_stories_from_slug_based_ids_to_numeric_only 2026-04-27 20:42:21 +00:00
dave aa7b26a24a huskies: merge 728_story_cryptographic_peer_handshake_with_trusted_keys_gating 2026-04-27 19:22:55 +00:00
dave 26f9f3f7fc huskies: merge 729_story_store_story_name_as_a_crdt_field_separate_from_the_story_id 2026-04-27 19:09:56 +00:00
dave 4a0f57478c huskies: merge 671_refactor_migrate_pipeline_state_consumers_from_string_comparisons_to_typed_pipelinestage_enum 2026-04-27 16:39:39 +00:00
dave ea872fa01c huskies: merge 676_bug_apply_and_persist_silently_drops_ops_when_persist_channel_send_fails 2026-04-27 14:38:11 +00:00
dave 9040d18f50 huskies: merge 664_story_crdt_lamport_clock_inner_seq_must_resume_from_max_own_author_seq_1_instead_of_resetting_to_1_on_restart_phase_c 2026-04-27 12:30:44 +00:00
dave b340aa97b0 fix: clean up clippy warnings + cargo fmt across post-refactor surface
The 13-file refactor pass (commits db00a5d4 through eca15b4e) introduced
~89 clippy errors and 38 cargo fmt issues — every agent in every worktree
hit them on script/test, burning their turn budget on cleanup before doing
real story work. This is the silent kill behind 644, 652, 655, 664, 667
all hitting watchdog limits this round.

Changes:
- cargo fmt --all across 37 files (formatting normalisation only)
- #![allow(unused_imports, dead_code)] on 24 split modules where the
  python-script splitter imported liberally to be safe; tighter cleanup
  per-import will happen as agents touch each module
- Removed truly-dead re-exports (cleanup_merge_workspace, slog_warn from
  http/mcp/mod.rs, CliArgs/print_help from main.rs)
- Prefixed _auth_msg in crdt_sync/server.rs (handshake helper return is
  bound but not consumed)
- Converted dangling /// doc block in crdt_sync/mod.rs to //! so it
  attaches to the module
- Removed empty lines after doc comments in 4 spots (clippy lint)

All 2636 tests pass; clippy --all-targets -- -D warnings clean.
2026-04-27 01:32:08 +00:00
dave 23e22ba49c refactor: split crdt_state.rs into 6 sub-modules with co-located tests
The 2122-line crdt_state.rs is split into a sub-module directory:

- types.rs: CRDT/view types + CrdtEvent (247 lines)
- state.rs: CrdtState struct, statics, init, apply_and_persist (531 lines)
- ops.rs: sync API + apply_remote_op + delta-sync tests (455 lines)
- write.rs: write_item + bug_511 test (273 lines)
- read.rs: read API + dump + dep helpers (469 lines)
- presence.rs: node identity + claim API + heartbeat (176 lines)
- mod.rs: doc, sub-module decls, re-exports, hex helper (53 lines)

Tests are co-located with the code they primarily exercise per Rust convention.

No behaviour change. All 26 crdt_state tests pass; full suite green
(2635 tests with --test-threads=1).
2026-04-26 20:54:15 +00:00
dave 795b172bba Revert "refactor: split top-5 largest files into mod.rs + tests.rs"
This reverts commit 65a3767a7a.
2026-04-26 20:15:58 +00:00
dave 65a3767a7a refactor: split top-5 largest files into mod.rs + tests.rs
Five files in server/src/ exceeded 1500 lines, with 50–75% of the line
count being inline `#[cfg(test)] mod tests { ... }` blocks. Agents
working on these files have to navigate huge buffers via Read calls,
costing turn budget that could go toward actual work.

Pattern: convert `foo.rs` to `foo/mod.rs` + `foo/tests.rs`.
Rust resolves `mod foo;` to either form, so no parent-module changes
needed.

Before / after (production-code lines, what an agent has to navigate
when editing the module):

  crdt_sync.rs:           3672 → 1003 (mod.rs) + 2667 (tests.rs)
  crdt_state.rs:          2122 → 1263 (mod.rs) + 854  (tests.rs)
  io/fs/scaffold.rs:      2045 →  702 (mod.rs) + 1342 (tests.rs)
  http/mcp/mod.rs:        1882 → 1410 (mod.rs) + 472  (tests.rs)
  http/mcp/story_tools.rs: 1864 →  725 (mod.rs) + 1137 (tests.rs)

Side change: scaffold/mod.rs's include_str! paths got an extra `../`
because the file moved one directory deeper.

Tests: full `cargo test` suite passes (2635 passed, 0 failed).
Formatting: cargo fmt --check clean.

Motivation: today's agent thrashing on 644 / 650 / 652 was partly due to
cumulative-counting (now fixed by 650) but also genuinely due to file
size — sonnet's 50-turn budget barely covers reading these files plus
making the change. Smaller production-code files mean more turn budget
left for the actual work.

Committed straight to master because this is an enabling refactor for
agent autonomy work; running it through the normal pipeline would
require an agent that has to navigate the very files it's about to
split, defeating the purpose.
2026-04-26 20:08:24 +00:00