feat: add chainweaver attest <flow> for observed-determinism evidence (#154)#162
Open
dgenio wants to merge 1 commit into
Open
feat: add chainweaver attest <flow> for observed-determinism evidence (#154)#162dgenio wants to merge 1 commit into
chainweaver attest <flow> for observed-determinism evidence (#154)#162dgenio wants to merge 1 commit into
Conversation
Closes #154. Introduces chainweaver/attest.py — a deterministic-by-evidence attestation loop — and the `chainweaver attest` CLI verb that drives it. Pipeline: 1. Generate N reproducible inputs (seed-driven, stdlib random.Random) or accept a user-supplied list via --seed-input. 2. For each input, run the flow M times. 3. Hash the canonical JSON of every final_output; assert all M agree. 4. Emit AttestationReport: chainweaver version, flow name + version, flow_schema_fingerprint, tool_schema_hashes, N, repeats, seed, host_info (no PII), wall-clock duration, observed_deterministic, aggregate_fingerprint, and a divergences list. Framing: this is *observed*-deterministic evidence, not a formal proof. Re-running with the same flow, tools, and seed yields a byte-identical aggregate_fingerprint. Scope-delta from the issue body: The issue references Hypothesis strategies "as in #143". Hypothesis is built around the @given decorator for property exploration and shrinking, not "give me N reproducible inputs by seed" — getting deterministic enumeration requires reaching into its internals. I shipped a small stdlib random.Random-seeded generator instead (~60 LoC); it covers int / float / bool / str / list[X] / dict / Literal / Optional / nested BaseModel. The seam is explicit so #143's Hypothesis-based generator can replace _generate_inputs() later without touching the rest of the loop. CLI flags: - --tools <module> (repeatable, -t): tool import paths - --runs N (default 100): number of distinct inputs - --repeats M (default 3, >= 2): runs per input - --seed S (default 0): generator seed - --seed-input <file>: bypass the generator with a JSON array of objects - --format json|table (-f): default json — the attestation artifact Exit codes: - 0 — observed-deterministic across all inputs - 1 — divergence, execution failure, or CLI-level error - 2 — flow file or tools module not found / not importable Public API additions (exported in __init__.py __all__): - AttestationInputError - AttestationReport - attest_flow Tests: 16 cases in tests/test_attest.py covering: - Programmatic attest_flow(): deterministic flow passes, seed reproducibility, different seeds → different fingerprints, flaky tool fails, seed_inputs bypass, repeats < 2 raises, missing input_schema raises, structurally-different flow → different flow_schema_fingerprint, multi-type generator coverage. - CLI: happy-path JSON, table format, --seed-input bypass, missing flow file (exit 2), repeats < 2 (exit 1), malformed --seed-input (exit 1), non-array --seed-input (exit 1). Verification: $ ruff check chainweaver/ tests/ examples/ # All checks passed $ ruff format --check chainweaver/ tests/ ... # 56 files already formatted $ python -m mypy chainweaver/ tests/ # Success: no issues $ python -m pytest tests/ -q --no-cov # 543 passed in 2.12s Stacked on top of #161 (analyzer foundation); chains through #161 → #160 → #159 → #158 → #157 → main as those merge. https://claude.ai/code/session_01QcSJ3NWhe5B4k1EP25Hx3n
This was referenced May 16, 2026
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.
Summary
Adds
chainweaver/attest.pyplus thechainweaver attest <flow>CLI verb. Turns ChainWeaver's "compiled flows are deterministic" claim into a reproducible, machine-verifiable artifact.Stacked on top of #161 (analyzer foundation); base cascades through
#161 → #160 → #159 → #158 → #157 → mainas those merge.Closes #154.
Changes
chainweaver/attest.py— new module (~330 LoC).attest_flow()API +AttestationReport(Pydantic) +AttestationInputError+ a seeded stdlib input generator.chainweaver/cli.py— newattest_command(~165 LoC including table renderer + flag definitions).chainweaver/__init__.py— exportsAttestationInputError,AttestationReport,attest_flowvia__all__.tests/test_attest.py— new file, 16 test cases (API + CLI + generator coverage).Pipeline
random.Random) or accept a user-supplied list via--seed-input.final_output; assert all M agree.AttestationReport: ChainWeaver version, flow name+version,flow_schema_fingerprint,tool_schema_hashes, N, repeats, seed, host info (no PII), duration,observed_deterministic,aggregate_fingerprint, divergences.Framing
This is observed-deterministic evidence, not a formal proof. Re-running with the same flow, tools, and seed yields a byte-identical
aggregate_fingerprint.CLI surface
--tools <module>(-t, repeatable)--runs N(default100)--repeats M(default3, must be>= 2)--seed S(default0)--seed-input <file>--format json|table(-f)json— the attestation artifactExit codes
0— observed-deterministic across all inputs.1— divergence, execution failure, or CLI-level error.2— flow file or tools module not found / not importable.Scope-delta call (Mode B)
The issue body says "use Hypothesis strategies as in #143." I shipped a stdlib
random.Random-seeded generator instead. Reasoning:@givenfor property exploration and shrinking, not "give me N reproducible inputs by seed."ConjectureData, etc.).int,float,bool,str,list[X],dict[K, V],Literal,Optional[X], nestedBaseModel)._generate_inputs()can be swapped for a Hypothesis-backed implementation once Add Hypothesis property-based determinism test harness #143 lands, without touching the rest of the loop.Net: no
hypothesisdependency added. If you'd rather take the hypothesis route, say the word and I'll swap_generate_inputs()behind an optional extra.Testing
ruff check chainweaver/ tests/ examples/)ruff format --check chainweaver/ tests/ examples/)python -m mypy chainweaver/ tests/)Tests cover both halves: the programmatic
attest_flow()API (deterministic happy path, seed reproducibility, different seeds → different fingerprints, flaky-tool failure,seed_inputsbypass, validation errors, structural-fingerprint sensitivity, multi-type generator coverage) and the CLI surface (happy-path JSON, table format,--seed-inputbypass, missing flow file, repeats validation, malformed/non-array seed-input).Diff stat:
4 files changed, 1136 insertions(+).Related Issues
Closes #154. Companion to:
ChainAnalyzer(feat: addChainAnalyzerfor offline schema-compatibility analysis (#77) #161) — static analysis; this is the runtime equivalent.Checklist
AGENTS.mdanddocs/agent-context/)AttestationInputError,AttestationReport,attest_flowin__all__; CLI docstring listsattestTradeoffs / risks
random.Randomvs Hypothesis. Documented above; the user accepted the relaxation but property-based testing wasn't the right tool for "reproducible N samples by seed." Switching later is a localized change to_generate_inputs().flow_schema_fingerprintexcludes status / description. Intentional: those fields don't affect runtime behavior, and including them would causeaggregate_fingerprintto change on cosmetic edits.executor._toolsaccess.attest_flow()reads the executor's private tool registry to computetool_schema_hashes. Adding a public iterator onFlowExecutorwould be cleaner but is out of scope here (touches an unrelated class). Same-package access is consistent with howcli.pyalready reaches into module internals.Scope notes
Closes #154 only. Adjacent items deferred:
FlowExecutor.toolspublic iterator — small follow-up; not bundled.https://claude.ai/code/session_01QcSJ3NWhe5B4k1EP25Hx3n
Generated by Claude Code