fix: read agent pin from CRDT register, not just YAML front matter

After story 871 the `agent` pin lives in the typed CRDT register
(`PipelineItemView.agent`), not the YAML front matter — the YAML
mutation was removed at the same time. Both spawn-resolution paths
(`auto_assign::story_checks::read_story_front_matter_agent` and
`start::validation::read_front_matter_agent`) still read only YAML
via parse_front_matter, which returns None for any story whose pin
was set via the post-871 typed setter. The spawn then falls back to
"first available coder," silently downgrading opus-pinned stories to
the first available sonnet — which is why 855/864/866 kept hitting the
80-turn watchdog limit despite the user's explicit opus pin.

Now: both paths consult `crdt_state::read_item()` first and use
`view.agent` if non-empty. YAML parsing remains as a fallback so older
stories whose CRDT entry doesn't yet have the field still resolve.

Adds a regression test that seeds an item with empty YAML, sets the
typed CRDT register via `set_agent`, and asserts
`read_story_front_matter_agent` returns the CRDT value.
This commit is contained in:
dave
2026-04-30 16:36:18 +00:00
parent 7a0c186d94
commit a8eac3c278
2 changed files with 45 additions and 3 deletions
@@ -60,6 +60,15 @@ pub(super) fn read_front_matter_agent(story_id: &str, agent_name: Option<&str>)
if agent_name.is_some() {
return None;
}
// After story 871 the pin lives in the CRDT typed register; fall back
// to legacy YAML parsing for stories whose CRDT entry doesn't yet have
// the field populated.
if let Some(view) = crate::crdt_state::read_item(story_id)
&& let Some(agent) = view.agent.as_ref()
&& !agent.is_empty()
{
return Some(agent.clone());
}
crate::db::read_content(story_id).and_then(|contents| {
crate::io::story_metadata::parse_front_matter(&contents)
.ok()?