Files
huskies/server/src/io/story_metadata/parser.rs
T
Timmy 6e704a33b7 wip(929): stage 5 — drop FS-based dep checks and qa-mode parser from io/story_metadata
Migrate the last three callers of the FS-scanning dependency helpers to the
CRDT-direct equivalents and delete the dead helpers:

- agents/pool/auto_assign/story_checks.rs: has_unmet_dependencies and
  check_archived_dependencies now wrap check_unmet_deps_crdt /
  check_archived_deps_crdt directly. Tests rewritten to seed the CRDT.
- http/mcp/story_tools/story/update.rs: bug-503 archived-dep warning now
  reads from CRDT instead of scanning 6_archived.
- agents/pool/pipeline/advance/helpers.rs: resolve_qa_mode_from_store is
  CRDT-only (the FS fallback for content-store-empty stories is gone).
- io/story_metadata/parser.rs: resolve_qa_mode_from_content removed.
- io/story_metadata/deps.rs: check_unmet_deps and dep_is_done deleted,
  along with the unused check_unmet_deps_from_list helper.
- io/story_metadata/mod.rs: re-exports trimmed accordingly.

check_archived_deps_from_list survives because story-creation still calls
it before the CRDT entry exists (used from story_tools/story/create.rs).

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

84 lines
2.6 KiB
Rust

//! Pure-content helpers and CRDT-backed metadata lookups.
//!
//! Story 865 stripped YAML front matter from stored content and the codebase
//! at large; the only remaining functions here read the CRDT or operate on
//! the markdown body directly.
use super::types::QaMode;
/// Parse unchecked todo items (`- [ ] ...`) from a markdown string.
pub fn parse_unchecked_todos(contents: &str) -> Vec<String> {
contents
.lines()
.filter_map(|line| {
let trimmed = line.trim();
trimmed.strip_prefix("- [ ] ").map(|text| text.to_string())
})
.collect()
}
/// Resolve the effective QA mode for a story by ID via the CRDT.
///
/// Returns `default` when the story has no entry or its `qa_mode` register is
/// unset. Spikes are **not** handled here — callers override to `Human` for
/// spikes themselves.
pub fn resolve_qa_mode(story_id: &str, default: QaMode) -> QaMode {
crate::crdt_state::read_item(story_id)
.and_then(|view| view.qa_mode().map(str::to_string))
.as_deref()
.and_then(QaMode::from_str)
.unwrap_or(default)
}
/// Return `true` if the story is in the `Frozen` pipeline stage.
///
/// Checks the typed CRDT stage via `read_typed`. Used by the pipeline advance
/// code to suppress stage transitions for frozen stories.
pub fn is_story_frozen_in_store(story_id: &str) -> bool {
crate::pipeline_state::read_typed(story_id)
.ok()
.flatten()
.map(|item| item.stage.is_frozen())
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_unchecked_todos_mixed() {
let input = "## AC\n- [ ] First thing\n- [x] Done thing\n- [ ] Second thing\n";
assert_eq!(
parse_unchecked_todos(input),
vec!["First thing", "Second thing"]
);
}
#[test]
fn parse_unchecked_todos_all_checked() {
let input = "- [x] Done\n- [x] Also done\n";
assert!(parse_unchecked_todos(input).is_empty());
}
#[test]
fn parse_unchecked_todos_no_checkboxes() {
let input = "# Story\nSome text\n- A bullet\n";
assert!(parse_unchecked_todos(input).is_empty());
}
#[test]
fn parse_unchecked_todos_leading_whitespace() {
let input = " - [ ] Indented item\n";
assert_eq!(parse_unchecked_todos(input), vec!["Indented item"]);
}
#[test]
fn resolve_qa_mode_falls_back_to_default_when_crdt_empty() {
crate::crdt_state::init_for_test();
assert_eq!(
resolve_qa_mode("9999_no_such_story", QaMode::Server),
QaMode::Server
);
}
}