Skip to content

feat(flows): checking, proving, the capability catalogue, and hmz check - #29

Merged
futrime merged 8 commits into
mainfrom
feat/flow-checking
Aug 29, 2026
Merged

feat(flows): checking, proving, the capability catalogue, and hmz check#29
futrime merged 8 commits into
mainfrom
feat/flow-checking

Conversation

@DongyunZou

Copy link
Copy Markdown
Contributor

The toolchain the aot compiler (humanfia/flowverse#8) steers by, useful on its own: check a flow before anything runs it.

What this adds

  • hmz.flows.checking — the static read of a flow's legality. Pure ast over every file a flow holds, executing nothing: ten errors for a flow that cannot run, cannot be answered or cannot end (dead-loop, unknown-ask, foreign-import, …) and eight warnings for a run that may be regretted (unbounded-loop, unguarded-answer, loose-config, …). surface() reads the flow-facing interfaces themselves — lifted from test_flow_interface, which now imports it — and offered() reads the package's own tables, so the checker states the contract rather than keeping a copy to drift. A sweep test pins the false-positive rate over every builtin and official flow: nothing anywhere, except the one warning rlar has earned (a loop only its reviewer can end).
  • hmz.flows.proving — a flow driven by stubs against a clock, in a subprocess per scenario. The stubs claim every capability over the real driver base classes, so hooks fire as they would; every turn lands at once and climbs spent() by what the scenario says. NEVER_DONE is the reviewer that never says the work is done — the executable proof that a run can end; SILENT is every guard tried at once. Refused loads come back as findings, and the config rules re-run on the live model.
  • The capability cataloguecatalogue()/briefed() read what the installed humanize serves at call time (moments off the live enum, backend sets off the DRIVEN classes), so anything writing a flow against it — the aot compiler, a person — is writing against this installation rather than a snapshot.
  • hmz check — both readings from a command line, --static/--strict/--json, exit 0/1/2, via Hmz().flows.check. Plus docs (reference + guide), the SPEC sections for checking.py/proving.py/hmz check, and MINE handed through for the flow that writes flows.

Notes

  • flows/SPEC.md's ## builtin/ clause is untouched; the new sections were authorized separately.
  • Every commit passes uv run pre-commit run --all-files and uv run pytest (1938 passed).
  • Downstream: feat: add aot, the flow that writes a flow flowverse#8 (official/aot) depends on this branch; its golden compiles ran green end to end with claude/claude-sonnet-5:medium (including a real toy-repo smoke of the compiled review loop) and codex/gpt-5.6-sol:medium.
  • History note: these commits were briefly pushed to main by mistake and reverted in 294528a; this PR re-lands them properly.

🤖 Generated with Claude Code

DongyunZou and others added 6 commits August 28, 2026 23:34
Pure ast over every file a flow holds, executing nothing, answering with
findings rather than raising: ten errors for a flow that cannot run,
cannot be answered or cannot end, and eight warnings for a run that may
be regretted. surface() reads the flow-facing interfaces themselves --
lifted from the interface test, which now imports it -- and offered()
reads the package's own tables, so the checker states the contract it
checks rather than keeping a copy to drift.

Every rule is the proof of an absence, one function at a time, and the
sweep over the builtin and official flows pins the false-positive rate:
nothing anywhere, except the one warning rlar has earned -- a loop only
its reviewer can end.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The second of the two readings: the flow loaded and driven for real, in
a subprocess per scenario, by stubs that claim every capability over the
real driver base classes -- so the hooks a flow hangs fire as they
would, every turn lands at once, and each costs what the scenario says.
NEVER_DONE is the reviewer that never says the work is done: a loop with
a bound of its own walks to the end of it in milliseconds, and one
without is caught by the turn cap or killed by the parent's clock --
the executable proof that a run can end. SILENT answers every turn with
nothing, which is every guard tried at once.

A refused load comes back as a finding rather than a raise, and the
config rules run again on the model the loading actually resolved. The
proof's world sleeps for free and works in a scratch directory taken
away with the process.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
catalogue() reads what this installed humanize serves at call time: the
primitives every backend has, each moment outside EVERYWHERE with the
backends whose drivers declare it, the shape a turn can be held to, the
tools a flow may offer, and the goal feature -- all off the live enum,
the DRIVEN table and the interfaces surface() reads, so a flow written
against the catalogue is written against this installation rather than
against a snapshot that drifts. briefed() renders it as the one page a
compiler -- or a person choosing what to build on -- steers by, split
into what every backend serves and what has to be declared on the place.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two readings from a command line: the static one over every file the
flow holds, which executes nothing, then the flow loaded in a subprocess
held to a clock so its live config model is read too. One finding a line
with a count under them, --json for a script, --static to keep the flow
unloaded, --strict to hold warnings to the bar. Exit 0 with nothing
blocking, 1 with any error -- or any warning under --strict -- and 2 for
a line to correct or a name no flow answers to.

The readings are reached through Hmz().flows.check, so anything else
that checks a flow makes the same call and is refused the same way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The reference grows a Checking-a-flow section beside Testing-a-flow: the
two readings, the rule table with what each code found, the scenarios a
proof drives against, and the catalogue. A guide walks the command --
one line, what an error is against a warning, --static for a flow nobody
has read, the never-done proof as a library call, --json in a script --
and Writing-a-flow points at it from Check-your-work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s live

The aot flow lands what it compiles in the flows of your own, and the
two places those are is a fact written in verses.py -- handed through
rather than spelled again where it would drift.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DongyunZou and others added 2 commits August 29, 2026 00:47
A field that takes at least three answers was answered with none: the
fabrication gave every list [], the model refused it, and a flow that
bounds its own retries read as one that could not be driven at all. Now
a list is answered with as many fabricated elements as its bound asks,
and an Annotated element is answered as the type it annotates.

Found scanning the official flows: parallel_flame_chase's coordinator
takes a plan of at least three lanes. Its own cross-field validator is
still past what deterministic fabrication can satisfy -- which the
proof reports honestly, as a flow that fails closed after three tries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A shaped answer's Literal field compared against a value the shape does not
offer -- review.verdict == "DONE" over Literal["done", "redo"] -- is a guard
that never opens, or one that never shuts. Both readings pass, both provings
pass, and the flow silently steers by a value no answer will ever hold; found
where the shape is declared in the same file and left be where it is not.

The rule oh-my-humanize's freeze checker runs on its edge conditions
(references undeclared verdict), carried over to answers held to a shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@futrime
futrime merged commit e079bf2 into main Aug 29, 2026
6 checks passed
@futrime
futrime deleted the feat/flow-checking branch August 29, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants