feat(flows): checking, proving, the capability catalogue, and hmz check - #29
Merged
Conversation
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>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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. Pureastover 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 fromtest_flow_interface, which now imports it — andoffered()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 warningrlarhas 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 climbsspent()by what the scenario says.NEVER_DONEis the reviewer that never says the work is done — the executable proof that a run can end;SILENTis every guard tried at once. Refused loads come back as findings, and the config rules re-run on the live model.catalogue()/briefed()read what the installed humanize serves at call time (moments off the live enum, backend sets off theDRIVENclasses), 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, viaHmz().flows.check. Plus docs (reference + guide), the SPEC sections forchecking.py/proving.py/hmz check, andMINEhanded through for the flow that writes flows.Notes
flows/SPEC.md's## builtin/clause is untouched; the new sections were authorized separately.uv run pre-commit run --all-filesanduv run pytest(1938 passed).official/aot) depends on this branch; its golden compiles ran green end to end withclaude/claude-sonnet-5:medium(including a real toy-repo smoke of the compiled review loop) andcodex/gpt-5.6-sol:medium.mainby mistake and reverted in 294528a; this PR re-lands them properly.🤖 Generated with Claude Code