Follow-up from #50. Robustness and diagnostics, not a regression: no manifest that built and validated cleanly before 0.7.0 behaves differently now.
build_graph runs at emitter.py:121, before ManifestPhysicsView(manifest) at emitter.py:143. wire/graph_builder._settable_for calls manifest_physics.relay_locked, which therefore reads raw metadata no validator has seen, and treats anything other than the literal string controllable as locked:
return md.get("relay-behavior", "controllable") != "controllable" or _opt_bool(md, "always-on", default=False)
Two consequences.
-
An out-of-domain value silently locks the circuit. relay-behavior: CONTROLLABLE, or any typo, yields a circuit with no $settable on switch/relay and no error. Inside Emitter this is masked, because _parse_circuit rejects the value a few lines later. A caller using build_graph standalone gets the silently locked circuit and nothing else. That path is supported and exercised: tests/test_byo_transport.py and tests/wire/* build graphs without a ManifestPhysicsView.
-
always-on is now parsed from a new call site. A malformed value raises ManifestValidationError out of graph construction rather than out of manifest validation. For a manifest with both a missing relay-behavior and a bad always-on, the reported error changes from missing required metadata key 'relay-behavior' to key 'always-on': not a bool.
Either fix works: have relay_locked reject an unknown relay-behavior rather than defaulting it to locked (it already has the valid set next door at manifest_physics.py:135), or hand graph_builder an already-validated bit. The first keeps the "one derivation, two callers" shape the PR was after.
Follow-up from #50. Robustness and diagnostics, not a regression: no manifest that built and validated cleanly before 0.7.0 behaves differently now.
build_graphruns atemitter.py:121, beforeManifestPhysicsView(manifest)atemitter.py:143.wire/graph_builder._settable_forcallsmanifest_physics.relay_locked, which therefore reads raw metadata no validator has seen, and treats anything other than the literal stringcontrollableas locked:Two consequences.
An out-of-domain value silently locks the circuit.
relay-behavior: CONTROLLABLE, or any typo, yields a circuit with no$settableonswitch/relayand no error. InsideEmitterthis is masked, because_parse_circuitrejects the value a few lines later. A caller usingbuild_graphstandalone gets the silently locked circuit and nothing else. That path is supported and exercised:tests/test_byo_transport.pyandtests/wire/*build graphs without aManifestPhysicsView.always-onis now parsed from a new call site. A malformed value raisesManifestValidationErrorout of graph construction rather than out of manifest validation. For a manifest with both a missingrelay-behaviorand a badalways-on, the reported error changes frommissing required metadata key 'relay-behavior'tokey 'always-on': not a bool.Either fix works: have
relay_lockedreject an unknownrelay-behaviorrather than defaulting it to locked (it already has the valid set next door atmanifest_physics.py:135), or handgraph_builderan already-validated bit. The first keeps the "one derivation, two callers" shape the PR was after.