Commit Graph

106 Commits

Author SHA1 Message Date
dave eb6b07531a huskies: merge 1114 story new project: --path flag to override default host directory 2026-05-17 14:48:49 +00:00
dave 59302b465d huskies: merge 1108 story Chat bootstrap Phase 3: SSH-remote editor access into the project container (any editor) 2026-05-16 23:37:59 +00:00
dave 10d992a7e4 huskies: merge 1106 story Chat bootstrap Phase 1: new project chat command spawns a bare project container and registers it with the gateway 2026-05-16 22:39:20 +00:00
dave 2857c3b46b huskies: merge 1094 bug delete_story leaks zombie rows in pipeline_items shadow table — 176 tombstoned items still report non-terminal stages 2026-05-15 12:27:48 +00:00
dave fc5481dbe4 huskies: merge 1093 bug Chat dispatcher spawns one Timmy per inbound message — needs coalesce window + per-session serial lock 2026-05-15 12:03:09 +00:00
dave 13f7dab5f0 huskies: merge 1088 2026-05-15 02:03:30 +00:00
dave b053f14d58 huskies: merge 1085 2026-05-15 01:38:05 +00:00
dave 0c23d209a0 huskies: merge 1077 2026-05-15 00:58:57 +00:00
dave f9b140add9 huskies: merge 1073 2026-05-15 00:37:01 +00:00
dave bb6a6063e8 huskies: merge 1066 2026-05-14 23:45:53 +00:00
dave c66016394b huskies: merge 1063 2026-05-14 21:53:56 +00:00
dave 8f666bd6b3 huskies: merge 1062 2026-05-14 20:36:51 +00:00
dave 5678f2a556 huskies: merge 1061 2026-05-14 20:12:51 +00:00
Timmy 667601012c fix: populate story_name in event buffer via CRDT lookup
`subscribe_to_watcher` was pushing StoredEvents into the event
buffer with story_name hardcoded to String::new(), so /api/events
polled by the gateway always omitted the title. The 1035 fix
patched the other path (gateway_relay status_to_stored) but left
this one bleeding empty strings.

Lookup happens once at the subscriber boundary rather than at all
44 watcher emit sites — the story_id is already in hand and
crdt_state::read_item is the canonical name source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 20:24:27 +01:00
dave b9709a6466 huskies: merge 1052 2026-05-14 18:11:57 +00:00
dave 1f9f34ab58 huskies: merge 1038 2026-05-14 17:06:50 +00:00
dave 311883f45d huskies: merge 1039 2026-05-14 16:33:47 +00:00
dave 9e06fff8a8 huskies: merge 1046 2026-05-14 16:20:07 +00:00
dave f1c96595de huskies: merge 1035 2026-05-14 13:17:38 +00:00
dave a80d0a497a huskies: merge 1029 2026-05-14 12:53:01 +00:00
dave c7a7cb4281 huskies: merge 997 2026-05-14 11:06:27 +00:00
dave 13ab97a615 huskies: merge 1010 2026-05-14 08:12:56 +00:00
dave 4520e0e6f9 huskies: merge 995 2026-05-14 07:55:40 +00:00
dave 52180bc402 huskies: merge 1017 2026-05-13 23:55:35 +00:00
dave 5ed1438ab9 huskies: merge 1015 2026-05-13 23:39:17 +00:00
dave 4e007bb770 huskies: merge 1009 2026-05-13 22:55:05 +00:00
dave caed894db9 huskies: merge 988 2026-05-13 17:28:52 +00:00
dave 580480094e huskies: merge 984 2026-05-13 16:47:51 +00:00
dave c3c9db3d8b huskies: merge 987 2026-05-13 16:30:31 +00:00
dave 430079ecbc huskies: merge 986 2026-05-13 16:01:51 +00:00
dave dcb43c465a huskies: merge 964 2026-05-13 14:56:08 +00:00
dave 9a6963ac04 huskies: merge 963 2026-05-13 12:53:03 +00:00
dave 184c214c34 huskies: merge 962 2026-05-13 12:05:01 +00:00
dave 8b53e20ca9 huskies: merge 961 2026-05-13 11:27:21 +00:00
dave 6bd11d41f9 huskies: merge 895 2026-05-13 08:52:59 +00:00
dave 4a8ed4348b huskies: merge 950 2026-05-13 08:46:22 +00:00
dave a7840ea4b0 huskies: merge 946 2026-05-13 08:00:49 +00:00
dave 9ce5a8df0c huskies: merge 945 2026-05-13 06:09:34 +00:00
dave 7ca5339450 huskies: merge 944 2026-05-13 05:07:28 +00:00
dave cd214d7246 huskies: merge 899 2026-05-12 23:16:25 +00:00
dave 0f0cf59329 huskies: merge 940 2026-05-12 23:11:29 +00:00
dave 937792f208 huskies: merge 898 2026-05-12 21:33:41 +00:00
Timmy d78dd9e8f9 feat(934): typed Stage enum replaces directory-string state model
The state machine's `Stage` enum becomes the source of truth for pipeline
state. Six stages of work land together:

  1. Clean wire vocabulary (`coding`, `merge`, `merge_failure`, ...) replaces
     legacy directory-style strings (`2_current`, `4_merge`, ...) on the wire.
     `Stage::from_dir` accepted both during deployment; new writes always
     emit the clean form via `stage_dir_name`. Lexicographic `dir >= "5_done"`
     checks in lifecycle.rs become typed `matches!` checks since the new
     vocabulary doesn't sort in pipeline order.
  2. `crdt_state::write_item` takes typed `&Stage`, serialising via
     `stage_dir_name` at the CRDT boundary. `#[cfg(test)] write_item_str`
     parses legacy strings for test fixtures.
  3. `WorkItem::stage()` returns typed `crdt_state::Stage`; `stage_str()`
     is gone from the public API. Projection dispatches on the typed enum.
  4. `frozen` becomes an orthogonal CRDT register. `Stage::Frozen` and
     `PipelineEvent::Freeze`/`Unfreeze` are removed; `transition_to_frozen`/
     `unfrozen` set the flag directly without touching the stage register.
  5. Watcher sweep and `tool_update_story`'s `blocked` setter route through
     `apply_transition` so the typed transition table validates every
     stage change. `update_story` gains a `frozen` field for symmetry.
  6. One-shot startup migration rewrites pre-934 directory-style stage
     registers (and sets `frozen=true` on items previously at `7_frozen`).
     `Stage::from_dir` drops legacy aliases. The db boundary keeps a small
     normaliser so callers with legacy strings (MCP, tests) still work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:31:59 +01:00
dave 93443e2ff1 huskies: merge 921 2026-05-12 21:09:52 +00:00
Timmy 69d91d7707 feat(929): delete db/yaml_legacy.rs entirely — CRDT is the sole source of truth
Final 929 sweep: every YAML-shaped helper is gone. No production code
parses or writes YAML front matter anywhere.

Surface removed:
- db/yaml_legacy.rs (FrontMatter/StoryMetadata structs, parse_front_matter,
  set_front_matter_field, yaml_residue marker) — file deleted.
- ItemMeta::from_yaml — deleted; callers pass typed ItemMeta::named(...) or
  ItemMeta::default() and use typed CRDT setters (set_depends_on,
  set_blocked, set_retry_count, set_agent, set_qa_mode, set_review_hold,
  set_item_type, set_epic, set_mergemaster_attempted) for the rest.
- write_coverage_baseline_to_story_file + read_coverage_percent_from_json —
  the coverage_baseline YAML field was write-only (nothing read it back);
  removed along with its caller in agent_tools/lifecycle.rs.
- update_story_in_file's generic `front_matter` HashMap parameter —
  tool_update_story now intercepts every known field name and routes it
  to a typed CRDT setter; unknown keys are rejected with an explicit error
  pointing at the typed setters. The function only takes user_story /
  description sections now.
- All 117 ItemMeta::from_yaml callsites migrated. Where tests previously
  passed a YAML-shaped content blob and relied on the helper to extract
  name/depends_on/blocked/agent/qa, they now pass:
    write_item_with_content(id, stage, content, ItemMeta::named("Foo"))
    crate::crdt_state::set_depends_on(id, &[...])    // when needed
    crate::crdt_state::set_blocked(id, true)         // when needed
    crate::crdt_state::set_agent(id, Some("..."))    // when needed
- write_story_content + write_story_file (test helper) now take an
  explicit `name: Option<&str>` instead of parsing it from content.
- db::ops::move_item_stage stopped re-parsing YAML on every stage
  transition; metadata is read straight from the CRDT view when mirroring
  the row into SQLite.

New CRDT setters added for symmetry:
- crdt_state::set_name (mirrors set_agent — explicit name updates).

cargo fmt --check, clippy --all-targets -- -D warnings, and the
2830-test suite all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:55:25 +01:00
Timmy 4888f051c3 wip(929): stage 10 sweep — production callsites move to CRDT, yaml_legacy shrinks
After 932 (review_hold register) and 933 (item_type + epic registers), the
remaining production yaml_legacy callers all had typed CRDT equivalents.
Migrated:

- agents/lifecycle.rs:
  - transition_to_merge_failure writes to MergeJob.error CRDT entry instead
    of YAML body. The legacy `merge_failure: "..."` front-matter write is gone.
  - reject_story_from_qa inlines the QA-rejection notes append; no longer
    needs yaml_legacy::write_rejection_notes_to_content.
  - fields_to_clear_transform helper deleted along with all five callers —
    blocked/retry_count/merge_failure are typed CRDT fields now, so clearing
    the equivalent YAML keys is redundant.

- http/workflow/pipeline.rs:
  - load_pipeline_state reads merge_failure from MergeJob.error (mirrors
    status_tools.rs).
  - validate_story_dirs checks the typed CRDT `name` register instead of
    parsing YAML front matter.

- http/mcp/status_tools.rs: review_hold reads the typed CRDT register
  (yaml_residue wrap was the last one in this file).
- http/mcp/story_tools/criteria.rs: story_name reads from CRDT.
- service/agents/mod.rs::get_work_item_content: name/agent come from CRDT.
- service/notifications/io/mod.rs::read_story_name: same.
- http/workflow/bug_ops/{bug,refactor}.rs: name-fallback paths drop YAML
  parsing in favour of the CRDT-derived item.name.

Dead helpers removed from db/yaml_legacy.rs:
  yaml_residue, write_merge_failure_in_content, write_rejection_notes_to_content,
  clear_front_matter_field_in_content, write_review_hold_in_content,
  clear_front_matter_field, write_review_hold (the last four shipped in 932).
Remaining surface: FrontMatter / StoryMetadata structs, parse_front_matter,
set_front_matter_field — kept for `coverage_baseline` writes via
test_results.rs and the generic update_story front_matter escape hatch.

Test fixtures rewritten to seed the CRDT register instead of relying on
YAML parsing during write_item_with_content:
- has_review_hold_returns_* tests
- item_type_from_id_uses_crdt_register_for_numeric_ids
- tool_list_epics_shows_member_rollup
- get_work_item_content (both copies — http/agents + service/agents)
- validate_story_dirs_missing_name_in_crdt
- server_side_merge_*_sets_merge_failure (assert MergeJob.error, not YAML)

cargo fmt --check, clippy --all-targets -- -D warnings, and the
2856-test suite all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:13:17 +01:00
dave 03a99b3cf1 huskies: merge 927 2026-05-12 17:55:12 +00:00
dave 19b7edb60c huskies: merge 918 2026-05-12 16:36:09 +00:00
dave 379ff16d3e huskies: merge 905 2026-05-12 15:02:58 +00:00
Timmy 734d3f2eb0 fix(gateway): bot.toml is read; perm_rx channel stays open
Two latent bugs in `service/gateway/io.rs::spawn_gateway_bot`, exposed
today after a long-overdue gateway rebuild:

1. The permission channel sender was bound as `_perm_tx` (the underscore
   prefix signalling "unused") and dropped at function return. The
   matrix bot's permission_listener task — which holds `perm_rx` for
   its lifetime per story 884 — then saw the channel close immediately
   and exited with "perm_rx channel closed" 1s after starting. Net
   effect: the listener was effectively absent on every gateway boot,
   so non-MCP tool permission requests had no destination at all
   (separate from the architectural mismatch that 898 will fix; this
   was a strictly worse "listener never even ran" version of the same
   problem). Bind as `perm_tx` and `mem::forget` it to keep the
   channel open for the gateway's lifetime, mirroring the existing
   `shutdown_tx` pattern two lines below.

2. `bot_name` was hardcoded to `"Assistant"`, ignoring
   `bot.toml::display_name`. So the gateway's matrix bot announced
   itself as "Assistant" and treated user messages addressed to
   "Timmy" (the actual configured display_name) as unaddressed,
   silently dropping them. `ambient_rooms` and
   `permission_timeout_secs` were similarly ignored. Load
   `BotConfig::load(config_dir)` and apply the same field plumbing
   the standard-mode initialisation in `main.rs:211-232` already
   uses.

Symptoms seen in production today:
- gateway.log: `Sending startup announcement: Assistant is online.`
  followed by repeated `Ignoring unaddressed message from
  @yossarian:crashlabs.io` lines.
- gateway.log: `permission listener started` immediately followed
  (same timestamp) by `permission listener exiting (perm_rx channel
  closed)`.

After this lands, rebuild the gateway binary and restart so it picks
up `bot.toml` correctly and the listener stays alive for the bot's
lifetime.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:28:06 +01:00