Skip to content

runtime: retry_policy, cache_policy, durability and subgraphs are unwrapped #9

Description

@Shashankss1205

Summary

Four LangGraph capabilities are not wrapped by the governed kernel: retry_policy, cache_policy, durability, and subgraphs. .inner reaches them, but the execution entry points there fail closed — driving a compiled graph through raw LangGraph raises MissingRunContextError rather than silently running with no budget and no trace. So .inner is an inspection escape hatch, not a way to run the graph, and these four features are currently unreachable from a governed run.

Why this matters

Every one of these interacts with a guarantee this kernel makes. A retry that the runtime does not know about is spend the budget never metered and an attempt the trace never recorded. A cache hit is a node that appears to have executed for free. Making a feature available without quietly voiding the audit trail is exactly the graph engineering problem this repo exists to solve — which is why these were left out rather than passed through.

Where in the code

  • grapharc/runtime/graph.pyadd_node, compile, the _wrap node wrappers, _enter / _leave
  • grapharc/runtime/graph.pyCompiledGraphARC, and the fail-closed .inner entry points
  • grapharc/runtime/budget.py — the meter and deadline_guard, which any retry must respect
  • grapharc/observe/trace.py — the event shape; note attempt already exists for resume
  • grapharc/observe/replay.py_build_executions keys on (node, step, attempt)

Confirm none are wrapped:

grep -n "retry_policy\|cache_policy\|durability" grapharc/runtime/graph.py   # no hits

What to change

Take one of these per PR — do not attempt all four at once. Each needs its semantics settled first:

retry_policy — the hardest and most valuable.

  • Every attempt must appear on the trace, and (node, step, attempt) must stay unique or replay will merge two attempts into one execution.
  • Tokens spent by a failed attempt are real spend and must be metered; a retry must not reset the budget.
  • A retry must not extend a max_seconds deadline. The guard's exit-time check must still refuse a node that overran across attempts.
  • Interaction with the existing resume attempt counter must be defined, not discovered.

cache_policy

  • A cache hit is not an execution. Decide what the trace says: a distinct phase, or an end event flagged as cached? metrics.nodes_executed counts end events, so this choice changes reported numbers.
  • A cached result must still pass the write-permission allowlist and state type validation — the cache must not become a way to smuggle a value past the declared write channel.

durability

  • Interacts with the checkpointer, which is LangGraph's, and with trace continuity, which is GraphARC's. Define what a durability level promises about the trace.

Subgraphs

  • The largest design question: a subgraph has its own state schema, its own writes, and potentially its own budget. Does it get its own meter that charges back to the parent, as the governed loop's rounds do? How do its nodes appear in replay — nested, or flattened with a path?

How to verify

uv run pytest -q
uv run ruff check .

Whichever you take, the gate is a failure test, matching this repo's convention that each stage ships one: for retries, assert a failed attempt's tokens were charged and both attempts appear with non-overlapping (step, attempt); for caching, assert a cache hit cannot write an undeclared field.

Acceptance criteria

  • The chosen feature is reachable from a governed run, not only via .inner
  • Budget metering stays correct across the new behaviour
  • Every execution, attempt or cache hit is recoverable from the trace, and replay reconstructs it correctly
  • Write permissions and state type validation still apply
  • .inner execution entry points still fail closed
  • The README "still unwrapped from LangGraph" bullet is updated to reflect what is now wrapped
  • uv run pytest green, uv run ruff check . clean

Skill level — experience required

These were deliberately left unwrapped, not overlooked, and the reasons are subtle: each one can silently invalidate the budget meter or the audit trail if passed straight through. You need a working understanding of the node wrapper, the meter, the deadline guard and replay's reconstruction before you start. State in a comment which of the four you are taking and what semantics you propose — a PR that simply forwards the argument to LangGraph will not be accepted, because that is exactly what the current code declines to do.

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureChanges a subsystem boundary or a cross-cutting contractenhancementNew feature or requestexperience requiredDeep familiarity with the codebase or domain needed; not a starter taskhelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions