feat(#29) + chore(#30): PRD-gap pass + hygiene sweep #32

Merged
timur merged 2 commits from feature/issue-29-prd-gaps into development 2026-05-14 10:07:30 +00:00
Owner

Closes #29 (the load-bearing items) and #30. Brings the codebase closer to PRD framing.

#29 — implementation gaps to match PRD

Schema renames (logic.oschema)

  • ExecutionStatusPlayStatus
  • FlowInputFlowField (single value type used for both inputs/outputs)
  • FlowField.input_typefield_type

Span gets a kind + source location (PRD §7)

  • New SpanKind value type (flow_root | step | rpc | subflow | other)
  • Span.kind (default step), Span.source_file, Span.source_line
  • Python SDK sets kind on every span: flow_root for the root span, step for @flow / flow.step / flow.span, rpc for instrument()-wrapped client method calls, subflow for flow.invoke(spawn=True)
  • @flow wrapper captures source_file + source_line from fn.__code__

Step-key version invalidation (PRD §5)

  • step_key now mixes HERO_FLOW_VERSION_SID into the SHA. Workflow version bump invalidates the memoization cache globally — paused plays resuming against a fresh version see fresh execution instead of stale cached outputs.

Unified sub-flow API (PRD §3)

  • New flow.invoke(name, *, spawn=False, version_sid="", **inputs) on the SDK. Single explicit API for both execution modes:
    • spawn=False (default) → in-process; resolves via the existing workflow-library lookup, exec's the source, calls the entry @flow.
    • spawn=True → calls play_run_async + play_wait; returns the child's output_data parsed as JSON. Opens a kind=subflow span on the parent carrying child_play_sid.
  • Removed the _HeroFlowImporter meta-path importer per the issue's recommendation. Migrated the seed flows (service_agent.py, service_code_gen.py) from from <flow_name> import <fn> to flow.invoke("<name>", ...).

Prefill fail-fast (PRD §6)

  • New Play.prefill_only field, set true when play_start is invoked with prefill_resumes_json non-empty.
  • Cleared on the first interactive play_resume so subsequent unanswered pauses still park the play.
  • HERO_PREFILL_ONLY=1 env var plumbs the flag to the SDK; flow.pause raises Failed instead of _AwaitingResume when set + no cached payload.

Admin UI graph polish (PRD §6 / #29 §6)

  • Replayed spans render with a dashed left border + dimmed opacity + "↻ replayed" inline marker.
  • New toolbar above the span tree: Expand all, Collapse all, Expand to failures (auto-expands every ancestor of any failed span — one-click debugging for fan-outs).

#30 — hygiene sweep

Deleted

  • Stale orphan src/logic/ at repo root (duplicate of crates/hero_logic/src/logic/, never wired into the workspace).

Terminology — "DAG control flow engine" → "Python flow runtime" everywhere the framing applied to the engine itself. Kept "DAG" hits that refer to legitimate concepts (e.g. the previously-removed DAG node executor in historical comments).

Deferred (called out explicitly)

  • herolib_base lifecycle migration for all three binaries (#29 §7) — sizeable refactor touching service.toml + main.rs in three crates. Worth a focused follow-up PR.
  • Workflow.outputs auto-populated from @flow(outputs=...) (#29 §4) — needs a Rust-side Python decorator parser. Left to a follow-up so the regex parser gets dedicated review.
  • Subflow card inline child-Play graph expansion (#29 §6) — recursive inline rendering needs Cytoscape and is larger UI work; the child_play_sid link already lands users on the child.

Test plan

  • cargo build --workspace clean
  • cargo test -p hero_logic --lib — 50 tests pass including the pause/replay integration test
  • Install + restart; python3 examples/pause_resume_demo.py → ✓ pause→resume→replay round trip passed
  • python3 examples/pause_resume_prefill.py → ✓ prefill_resumes_json round trip passed
  • Visual smoke of the Expand-to-failures toolbar on a play with a known failed span
  • Confirm replayed spans render dashed in the graph view on a paused-and-resumed play

🤖 Generated with Claude Code

Closes #29 (the load-bearing items) and #30. Brings the codebase closer to PRD framing. ## #29 — implementation gaps to match PRD **Schema renames** (`logic.oschema`) - `ExecutionStatus` → `PlayStatus` - `FlowInput` → `FlowField` (single value type used for both inputs/outputs) - `FlowField.input_type` → `field_type` **Span gets a kind + source location** (PRD §7) - New `SpanKind` value type (`flow_root | step | rpc | subflow | other`) - `Span.kind` (default `step`), `Span.source_file`, `Span.source_line` - Python SDK sets `kind` on every span: `flow_root` for the root span, `step` for `@flow` / `flow.step` / `flow.span`, `rpc` for `instrument()`-wrapped client method calls, `subflow` for `flow.invoke(spawn=True)` - `@flow` wrapper captures `source_file` + `source_line` from `fn.__code__` **Step-key version invalidation** (PRD §5) - `step_key` now mixes `HERO_FLOW_VERSION_SID` into the SHA. Workflow version bump invalidates the memoization cache globally — paused plays resuming against a fresh version see fresh execution instead of stale cached outputs. **Unified sub-flow API** (PRD §3) - New `flow.invoke(name, *, spawn=False, version_sid="", **inputs)` on the SDK. Single explicit API for both execution modes: - `spawn=False` (default) → in-process; resolves via the existing workflow-library lookup, exec's the source, calls the entry `@flow`. - `spawn=True` → calls `play_run_async` + `play_wait`; returns the child's `output_data` parsed as JSON. Opens a `kind=subflow` span on the parent carrying `child_play_sid`. - Removed the `_HeroFlowImporter` meta-path importer per the issue's recommendation. Migrated the seed flows (`service_agent.py`, `service_code_gen.py`) from `from <flow_name> import <fn>` to `flow.invoke("<name>", ...)`. **Prefill fail-fast** (PRD §6) - New `Play.prefill_only` field, set `true` when `play_start` is invoked with `prefill_resumes_json` non-empty. - Cleared on the first interactive `play_resume` so subsequent unanswered pauses still park the play. - `HERO_PREFILL_ONLY=1` env var plumbs the flag to the SDK; `flow.pause` raises `Failed` instead of `_AwaitingResume` when set + no cached payload. **Admin UI graph polish** (PRD §6 / #29 §6) - Replayed spans render with a dashed left border + dimmed opacity + "↻ replayed" inline marker. - New toolbar above the span tree: **Expand all**, **Collapse all**, **Expand to failures** (auto-expands every ancestor of any failed span — one-click debugging for fan-outs). ## #30 — hygiene sweep **Deleted** - Stale orphan `src/logic/` at repo root (duplicate of `crates/hero_logic/src/logic/`, never wired into the workspace). **Terminology** — "DAG control flow engine" → "Python flow runtime" everywhere the framing applied to the engine itself. Kept "DAG" hits that refer to legitimate concepts (e.g. the previously-removed DAG node executor in historical comments). ## Deferred (called out explicitly) - **`herolib_base` lifecycle migration** for all three binaries (#29 §7) — sizeable refactor touching `service.toml` + `main.rs` in three crates. Worth a focused follow-up PR. - **`Workflow.outputs` auto-populated from `@flow(outputs=...)`** (#29 §4) — needs a Rust-side Python decorator parser. Left to a follow-up so the regex parser gets dedicated review. - **Subflow card inline child-Play graph expansion** (#29 §6) — recursive inline rendering needs Cytoscape and is larger UI work; the `child_play_sid` link already lands users on the child. ## Test plan - [x] `cargo build --workspace` clean - [x] `cargo test -p hero_logic --lib` — 50 tests pass including the pause/replay integration test - [x] Install + restart; `python3 examples/pause_resume_demo.py` → ✓ pause→resume→replay round trip passed - [x] `python3 examples/pause_resume_prefill.py` → ✓ prefill_resumes_json round trip passed - [ ] Visual smoke of the **Expand-to-failures** toolbar on a play with a known failed span - [ ] Confirm replayed spans render dashed in the graph view on a paused-and-resumed play 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Closes #29 (the load-bearing items) and #30. Brings the codebase
closer to PRD framing; what's deferred is called out below.

## #29 — implementation gaps to match PRD

Schema renames (logic.oschema):
- ExecutionStatus → PlayStatus (matches PRD §7 terminology).
- FlowInput → FlowField (single value type used for both inputs/outputs).
- FlowField.input_type → field_type to match the rename.

Span gets a kind + source location (PRD §7):
- New SpanKind value type (flow_root | step | rpc | subflow | other).
- Span.kind (default "step"), Span.source_file, Span.source_line.
- Python SDK sets kind on every span: flow_root for the root span,
  step for @flow / flow.step / flow.span, rpc for instrument()-wrapped
  client method calls, subflow for flow.invoke(spawn=True).
- @flow wrapper captures source_file + source_line from fn.__code__
  so the graph view can "jump to source" without re-introspecting at
  log time.

Step-key version invalidation (PRD §5):
- step_key now mixes HERO_FLOW_VERSION_SID into the SHA. Workflow
  version bump invalidates the memoization cache globally — paused
  plays resuming against a fresh version see fresh execution instead
  of stale cached outputs.

Unified sub-flow API (PRD §3):
- New `flow.invoke(name, *, spawn=False, version_sid="", **inputs)` on
  the SDK. Single explicit API for both execution modes:
  - spawn=False (default) → in-process; resolves via the existing
    workflow-library lookup, exec's the source, calls the entry @flow.
  - spawn=True  → calls play_run_async + play_wait; returns the
    child's output_data parsed as JSON. Opens a kind=subflow span on
    the parent carrying child_play_sid.
- Removed the `_HeroFlowImporter` meta-path importer per the issue's
  recommendation ("keep one mental model"). Migrated the seed flows
  (service_agent.py, service_code_gen.py) from
  `from <flow_name> import <fn>` to `flow.invoke("<name>", ...)`.

Prefill fail-fast (PRD §6):
- New Play.prefill_only field, set true when play_start is invoked
  with prefill_resumes_json non-empty.
- Cleared on the first interactive play_resume so subsequent
  unanswered pauses still park the play.
- HERO_PREFILL_ONLY=1 env var plumbs the flag to the SDK; flow.pause
  raises Failed instead of _AwaitingResume when set + no cached
  payload.

Admin UI graph polish (PRD §6 / #29 §6):
- Replayed spans render with a dashed left border + dimmed opacity +
  "↻ replayed" inline marker so a reader can tell at a glance what
  ran vs what came from the cache.
- New toolbar above the span tree: Expand all, Collapse all, and
  Expand-to-failures (auto-expands every ancestor of any failed
  span — one-click debugging for fan-outs).

## #30 — hygiene sweep

Deleted:
- Stale orphan `src/logic/` at repo root (duplicate of
  `crates/hero_logic/src/logic/`, never wired into the workspace).

Terminology — "DAG control flow engine" → "Python flow runtime"
everywhere the framing applied to the engine itself. Kept "DAG"
hits that refer to legitimate concepts (e.g. the previously-removed
DAG node executor in historical comments).

## Deferred (explicit, NOT silently dropped)

- `herolib_base` lifecycle migration for all three binaries (#29 §7)
  — sizeable refactor touching service.toml + main.rs in three crates.
  Worth a focused follow-up PR rather than bundling here.
- `Workflow.outputs` auto-populated from `@flow(outputs=...)` on save
  (#29 §4) — needs a Rust-side Python decorator parser. Spec is
  clear; left to a follow-up so the regex parser gets dedicated
  review.
- Subflow card inline child-Play graph expansion (#29 §6) — the
  child_play_sid link already lands users on the child; recursive
  inline rendering needs Cytoscape and is larger UI work.

## Test coverage

All 50 lib tests pass, including the new
`pause_exits_75_records_pending_resume_then_replay_uses_cached_payload`
integration. Live server smoke against examples/pause_resume_demo.py
+ pause_resume_prefill.py — both green after refresh of the example
scripts for the field_type rename.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
timur merged commit c91df390f8 into development 2026-05-14 10:07:30 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_logic!32
No description provided.