rhei-cli 0.3.0

Command-line driver for the Rhei agent runtime.
Documentation
    fn snapshot_machine(state_body: &str) -> String {
        format!(
            r#"
name: snapshot-test
version: 1.0
states:
  source:
    description: Source
    target: claude-code:anthropic:model
    snapshot:
      emit:
        name: build
  pending:
    description: Pending
    target: claude-code:anthropic:model
{state_body}
  done:
    description: Done
    final: true
transitions:
  - from: source
    to: pending
  - from: pending
    to: done
"#
        )
    }

    #[test]
    fn rejects_snapshot_inherit_unsupported_compat() {
        let yaml = snapshot_machine(
            r#"    snapshot:
      inherit:
        name: build
        compat: replay
"#,
        );

        let err = StateMachine::from_yaml_str(&yaml).expect_err("unsupported compat");

        assert!(err.to_string().contains("snapshot.inherit.compat"));
    }

    #[test]
    fn rejects_snapshot_inherit_select_target_all() {
        let yaml = snapshot_machine(
            r#"    snapshot:
      inherit:
        name: build
        select:
          state: source
          target: all
"#,
        );

        let err = StateMachine::from_yaml_str(&yaml).expect_err("select.target all");

        assert!(err.to_string().contains("snapshot.inherit.select.target 'all'"));
    }

    #[test]
    fn rejects_snapshot_unknown_keys_in_closed_objects() {
        let yaml = snapshot_machine(
            r#"    snapshot:
      inherit:
        name: build
        unexpected: true
"#,
        );

        let err = StateMachine::from_yaml_str(&yaml).expect_err("unknown snapshot key");

        assert!(err.to_string().contains("unknown field"));
    }

    #[test]
    fn target_slug_preserves_dot_underscore_and_detects_fanout_collisions() {
        let target =
            parse_execution_target("pi:openai:org_name/gpt.4o").expect("target should parse");
        assert_eq!(target.slug(), "pi-openai-org_name-gpt.4o");

        let yaml = r#"
name: collision
version: 1
states:
  pending:
    description: pending
    all_targets:
      - pi:openai:gpt/4o
      - pi:openai:gpt-4o
  done:
    description: done
    final: true
"#;
        let err = StateMachine::from_yaml_str(yaml).expect_err("slug collision");
        assert!(err.to_string().contains("snapshot target slug"));
    }

    #[test]
    fn rejects_unresolvable_and_ambiguous_snapshot_inherit() {
        let missing = snapshot_machine(
            r#"    snapshot:
      inherit:
        name: missing
"#,
        );
        let err = StateMachine::from_yaml_str(&missing).expect_err("missing emitter");
        assert!(err.to_string().contains("unresolvable snapshot.inherit"));

        let ambiguous = r#"
name: snapshot-test
version: 1
states:
  source-a:
    description: Source A
    target: claude-code:anthropic:model
    snapshot:
      emit:
        name: build
  source-b:
    description: Source B
    target: claude-code:anthropic:model
    snapshot:
      emit:
        name: build
  pending:
    description: Pending
    target: claude-code:anthropic:model
    snapshot:
      inherit:
        name: build
  done:
    description: Done
    final: true
"#;
        let err = StateMachine::from_yaml_str(ambiguous).expect_err("ambiguous emitter");
        assert!(err.to_string().contains("ambiguous snapshot.inherit"));
    }

    #[test]
    fn rejects_snapshot_inherit_same_without_effective_target_and_fanout_source_without_target() {
        let same_without_target = r#"
name: snapshot-test
version: 1
states:
  source:
    description: Source
    target: claude-code:anthropic:model
    snapshot:
      emit:
        name: build
  pending:
    description: Pending
    snapshot:
      inherit:
        name: build
        select:
          target: same
  done:
    description: Done
    final: true
"#;
        let err = StateMachine::from_yaml_str(same_without_target).expect_err("same target");
        assert!(err.to_string().contains("select.target: same"));

        let fanout = r#"
name: snapshot-test
version: 1
states:
  source:
    description: Source
    all_targets:
      - claude-code:anthropic:model-a
      - claude-code:anthropic:model-b
    snapshot:
      emit:
        name: build
  pending:
    description: Pending
    target: claude-code:anthropic:model-a
    snapshot:
      inherit:
        name: build
        select:
          state: source
  done:
    description: Done
    final: true
"#;
        let err = StateMachine::from_yaml_str(fanout).expect_err("fanout source");
        assert!(err.to_string().contains("fanout source"));
    }