From 9957669188e27aec413c8e41fb363be963c5072c Mon Sep 17 00:00:00 2001 From: rohan Date: Wed, 4 Mar 2026 15:35:52 +0530 Subject: [PATCH] feat: exempt BoundaryAction blocks from G-002 signature completeness check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BoundaryAction blocks have no inputs by design — they model exogenous signals entering the system from outside. G-002 now skips the input requirement for blocks with block_type="boundary" while still requiring outputs. The default block compiler now propagates the block's kind field into BlockIR.block_type so role information is available in the IR. --- .../gds-framework/gds/compiler/compile.py | 1 + .../gds/verification/generic_checks.py | 12 ++++- .../gds-framework/tests/test_verification.py | 51 +++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/packages/gds-framework/gds/compiler/compile.py b/packages/gds-framework/gds/compiler/compile.py index 2eeb026..4573ec8 100644 --- a/packages/gds-framework/gds/compiler/compile.py +++ b/packages/gds-framework/gds/compiler/compile.py @@ -208,6 +208,7 @@ def _default_block_compiler(block: AtomicBlock) -> BlockIR: """Default block compiler — extracts name and interface slots.""" return BlockIR( name=block.name, + block_type=getattr(block, "kind", ""), signature=( _ports_to_sig(block.interface.forward_in), _ports_to_sig(block.interface.forward_out), diff --git a/packages/gds-framework/gds/verification/generic_checks.py b/packages/gds-framework/gds/verification/generic_checks.py index df6cdbd..08ad623 100644 --- a/packages/gds-framework/gds/verification/generic_checks.py +++ b/packages/gds-framework/gds/verification/generic_checks.py @@ -65,13 +65,23 @@ def check_g001_domain_codomain_matching(system: SystemIR) -> list[Finding]: def check_g002_signature_completeness(system: SystemIR) -> list[Finding]: """G-002: Every block must have at least one non-empty input slot and at least one non-empty output slot. + + BoundaryAction blocks (block_type == "boundary") are exempt from the + input requirement — they have no inputs by design, since they model + exogenous signals entering the system from outside. """ findings = [] for block in system.blocks: fwd_in, fwd_out, bwd_in, bwd_out = block.signature has_input = bool(fwd_in) or bool(bwd_in) has_output = bool(fwd_out) or bool(bwd_out) - has_required = has_input and has_output + + # BoundaryAction blocks have no inputs by design — only check outputs + is_boundary = block.block_type == "boundary" + if is_boundary: + has_required = has_output + else: + has_required = has_input and has_output missing = [] if not has_input: diff --git a/packages/gds-framework/tests/test_verification.py b/packages/gds-framework/tests/test_verification.py index 7c3652a..fbccda0 100644 --- a/packages/gds-framework/tests/test_verification.py +++ b/packages/gds-framework/tests/test_verification.py @@ -82,6 +82,57 @@ def test_missing_output_flags(self): failed = [f for f in findings if not f.passed] assert len(failed) >= 1 + def test_boundary_action_no_inputs_passes(self): + """BoundaryAction blocks have no inputs by design — G-002 should pass.""" + sys = SystemIR( + name="Test", + blocks=[ + BlockIR( + name="Sensor", + block_type="boundary", + signature=("", "Temperature", "", ""), + ), + ], + wirings=[], + ) + findings = check_g002_signature_completeness(sys) + assert len(findings) == 1 + assert findings[0].passed + + def test_boundary_action_no_outputs_still_fails(self): + """BoundaryAction with no outputs should still fail G-002.""" + sys = SystemIR( + name="Test", + blocks=[ + BlockIR( + name="BadBoundary", + block_type="boundary", + signature=("", "", "", ""), + ), + ], + wirings=[], + ) + findings = check_g002_signature_completeness(sys) + assert len(findings) == 1 + assert not findings[0].passed + + def test_non_boundary_no_inputs_still_fails(self): + """Non-boundary blocks without inputs should still fail G-002.""" + sys = SystemIR( + name="Test", + blocks=[ + BlockIR( + name="Orphan", + block_type="policy", + signature=("", "Signal", "", ""), + ), + ], + wirings=[], + ) + findings = check_g002_signature_completeness(sys) + assert len(findings) == 1 + assert not findings[0].passed + # ── G-003: Direction consistency ─────────────────────────────