From 0d6583f39718b366c9e14659054ea4ee5b85cc9a Mon Sep 17 00:00:00 2001 From: Alfonso Sastre Date: Sat, 5 Sep 2026 11:16:05 +0200 Subject: [PATCH] =?UTF-8?q?feat(graph):=20add=20full=5Fverify=20preset=20?= =?UTF-8?q?=E2=80=94=20one-command=20build=E2=86=92spec=E2=86=92test?= =?UTF-8?q?=E2=86=92review=20(#120)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chaining mechanism already existed generically via from_spec (any comma-separated agent list, e.g. --pipeline=build,spec,test,review already worked) — what was missing was a dedicated, memorable preset for exactly this sequence, matching #120's own title. The closest existing preset, impl_then_spec_then_test, runs test and review in PARALLEL after spec, so review sees the same untested output test is concurrently checking rather than reviewing what test verified. Adds full_verify() = build → spec → test → review as a strict sequence, wired into preset()/preset_names()/preset_shape()/is_preset(). Extended spec_matches_preset() to assert build,spec,test,review and full_verify produce identical shapes, matching the existing discipline for the other three presets. Live-verified end to end (not just type-checked) with run_graph_persistent against a real task: spec genuinely located and read the file build wrote (via glob/read tool calls), wrote and proved 4 real spec properties via lex_spec_check (self-correcting two real syntax mistakes along the way), test recognized the existing examples{} coverage instead of duplicating it, and review used effects_of/ attestation_query/lex_check for a real, evidence-based verdict — not four independent runs that happened to share a task description. Inspected all four persistent trails (.lex/sessions/{impl,spec,test, review}.db) afterward: each has distinct, mode-appropriate event counts and kinds — spec's trail alone has 4 real verified.spec_check attestations (matching the 4 proved properties) and 2 cap.failed events (matching the two self-corrected mistakes); test and review each have their own verified.type_check. Every stage's activity is attributable to that stage specifically. Fixes #120 Co-Authored-By: Claude Sonnet 5 --- src/server/graph.lex | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/server/graph.lex b/src/server/graph.lex index 1744f42..253a1bb 100644 --- a/src/server/graph.lex +++ b/src/server/graph.lex @@ -76,12 +76,21 @@ fn impl_then_spec_then_test() -> Node { SequenceNode([AgentNode(build_def()), AgentNode(spec_def()), ParallelNode([AgentNode(test_def()), AgentNode(review_def())])]) } +# #120's "one-command full verify": build, then spec, then test, then +# review — each stage strictly after the last, unlike +# impl_then_spec_then_test's parallel test/review tail. review here +# reviews what test already checked, not the same untested output test +# is concurrently checking. +fn full_verify() -> Node { + SequenceNode([AgentNode(build_def()), AgentNode(spec_def()), AgentNode(test_def()), AgentNode(review_def())]) +} + fn preset_names() -> List[Str] examples { - preset_names() => ["impl_then_test", "impl_and_test_parallel", "impl_then_spec_then_test"] + preset_names() => ["impl_then_test", "impl_and_test_parallel", "impl_then_spec_then_test", "full_verify"] } { - ["impl_then_test", "impl_and_test_parallel", "impl_then_spec_then_test"] + ["impl_then_test", "impl_and_test_parallel", "impl_then_spec_then_test", "full_verify"] } # Resolve a pipeline name from the command line. Unknown names are None @@ -97,7 +106,11 @@ fn preset(name :: Str) -> Option[Node] { if name == "impl_then_spec_then_test" { Some(impl_then_spec_then_test()) } else { - None + if name == "full_verify" { + Some(full_verify()) + } else { + None + } } } } @@ -112,6 +125,7 @@ fn preset_shape(name :: Str) -> Str preset_shape("impl_then_test") => "impl → test", preset_shape("impl_and_test_parallel") => "impl ∥ test", preset_shape("impl_then_spec_then_test") => "impl → spec → (test ∥ review)", + preset_shape("full_verify") => "impl → spec → test → review", preset_shape("nope") => "" } { @@ -126,6 +140,7 @@ fn is_preset(name :: Str) -> Bool is_preset("impl_then_test") => true, is_preset("impl_and_test_parallel") => true, is_preset("impl_then_spec_then_test") => true, + is_preset("full_verify") => true, is_preset("build") => false, is_preset("") => false } @@ -280,7 +295,11 @@ fn spec_matches_preset() -> Bool { if spec_shape("build,test") == preset_shape("impl_then_test") { if spec_shape("build|test") == preset_shape("impl_and_test_parallel") { - spec_shape("build,spec,test|review") == preset_shape("impl_then_spec_then_test") + if spec_shape("build,spec,test|review") == preset_shape("impl_then_spec_then_test") { + spec_shape("build,spec,test,review") == preset_shape("full_verify") + } else { + false + } } else { false }