Skip to content

Commit 992a836

Browse files
committed
test: block product lower-owner bypasses
1 parent c67a925 commit 992a836

3 files changed

Lines changed: 139 additions & 3 deletions

File tree

support/no_bypass_scanner/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ calls, direct generated SDK calls, direct env auth lookup, direct runtime
88
mutation, direct DB access, and direct trace writes in governed workflows. It
99
emits ref-only receipts and blocks release when findings remain open.
1010

11+
The structural gate also blocks product code from importing or calling lower
12+
owner modules directly. Product implementation may call AppKit surfaces and may
13+
author pure `Mezzanine.Pack` contracts, but product code must not reach into
14+
Mezzanine workflow/runtime modules, Citadel authority modules, Jido
15+
Integration, Execution Plane, OuterBrain, GroundPlane, AITrace, TRINITY, GEPA,
16+
provider SDKs, or generated SDKs for governed behavior.
17+
1118
The foundation gate also protects generic-stack cutovers. In product code, it
1219
allows provider words in command names, fixtures, docs, packs, receipts, and
1320
live examples, but flags provider-shaped implementation API names such as

support/no_bypass_scanner/lib/stack_lab/structural_gate_scanner.ex

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,31 @@ defmodule StackLab.StructuralGateScanner do
124124
]
125125
@provider_binding_refs ["linear_primary", "github_primary", "codex_primary"]
126126

127+
@product_lower_owner_module_prefixes [
128+
"AITrace",
129+
"Anthropic",
130+
"Citadel",
131+
"Codex",
132+
"ExecutionPlane",
133+
"GEPA",
134+
"GEPAFramework",
135+
"Gepa",
136+
"GepaFramework",
137+
"GitHubEx",
138+
"GroundPlane",
139+
"Inference",
140+
"Jido",
141+
"Jido.Integration",
142+
"JidoIntegration",
143+
"Linear",
144+
"Mezzanine",
145+
"OpenAI",
146+
"OuterBrain",
147+
"ReqLLM",
148+
"Trinity",
149+
"TrinityFramework"
150+
]
151+
127152
@ground_plane_higher_layer_tokens [
128153
"AI",
129154
"ai",
@@ -551,11 +576,19 @@ defmodule StackLab.StructuralGateScanner do
551576
end
552577

553578
defp ast_node_findings(checked_path, _content, {:alias, meta, [alias_ast]}, _remote?) do
554-
{provider_remote_reference_findings(checked_path, meta, alias_ast, :alias), []}
579+
findings =
580+
provider_remote_reference_findings(checked_path, meta, alias_ast, :alias) ++
581+
product_lower_owner_reference_findings(checked_path, meta, alias_ast, :alias)
582+
583+
{findings, []}
555584
end
556585

557586
defp ast_node_findings(checked_path, _content, {:import, meta, [alias_ast | _]}, _remote?) do
558-
{provider_remote_reference_findings(checked_path, meta, alias_ast, :import), []}
587+
findings =
588+
provider_remote_reference_findings(checked_path, meta, alias_ast, :import) ++
589+
product_lower_owner_reference_findings(checked_path, meta, alias_ast, :import)
590+
591+
{findings, []}
559592
end
560593

561594
defp ast_node_findings(
@@ -565,7 +598,11 @@ defmodule StackLab.StructuralGateScanner do
565598
_remote?
566599
)
567600
when is_atom(function_name) do
568-
{provider_remote_reference_findings(checked_path, meta, alias_ast, :remote_call), []}
601+
findings =
602+
provider_remote_reference_findings(checked_path, meta, alias_ast, :remote_call) ++
603+
product_lower_owner_reference_findings(checked_path, meta, alias_ast, :remote_call)
604+
605+
{findings, []}
569606
end
570607

571608
defp ast_node_findings(checked_path, _content, {:%{}, meta, fields}, _remote?) do
@@ -777,6 +814,52 @@ defmodule StackLab.StructuralGateScanner do
777814

778815
defp provider_remote_reference_findings(_checked_path, _meta, _alias_ast, _role), do: []
779816

817+
defp product_lower_owner_reference_findings(
818+
%CheckedPath{repo: "extravaganza", zone: :product} = checked_path,
819+
meta,
820+
alias_ast,
821+
ast_role
822+
) do
823+
alias_text = alias_to_string(alias_ast)
824+
825+
cond do
826+
alias_text == "" ->
827+
[]
828+
829+
allowed_product_lower_owner_reference?(alias_text) ->
830+
[]
831+
832+
product_lower_owner_reference?(alias_text) ->
833+
[
834+
finding(checked_path, %{
835+
rule: :direct_lower_owner_import_in_product,
836+
reason: :product_direct_lower_owner_reference,
837+
line: meta_line(meta),
838+
token: alias_text,
839+
ast_role: ast_role,
840+
owner_phase: "Phase 1",
841+
remediation:
842+
"Product code must enter governed behavior through AppKit surfaces; pure Mezzanine.Pack authoring is the only lower exception."
843+
})
844+
]
845+
846+
true ->
847+
[]
848+
end
849+
end
850+
851+
defp product_lower_owner_reference_findings(_checked_path, _meta, _alias_ast, _role), do: []
852+
853+
defp product_lower_owner_reference?(alias_text) do
854+
Enum.any?(@product_lower_owner_module_prefixes, fn prefix ->
855+
alias_text == prefix or String.starts_with?(alias_text, prefix <> ".")
856+
end)
857+
end
858+
859+
defp allowed_product_lower_owner_reference?(alias_text) do
860+
alias_text == "Mezzanine.Pack" or String.starts_with?(alias_text, "Mezzanine.Pack.")
861+
end
862+
780863
defp closed_provider_dispatch_map_findings(checked_path, meta, fields) do
781864
if Zones.closed_provider_dispatch_scan_path?(checked_path) do
782865
provider_keys =

support/no_bypass_scanner/test/structural_gate_scanner_test.exs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,52 @@ defmodule StackLab.StructuralGateScannerTest do
141141
assert Enum.any?(receipt.findings, &(&1.rule == :provider_shaped_dto_field))
142142
end
143143

144+
test "fails product direct imports and calls into lower owner modules", %{roots: roots} do
145+
product_path =
146+
roots
147+
|> Map.fetch!("extravaganza")
148+
|> Path.join("apps/extravaganza_core/lib/extravaganza/product_bypass.ex")
149+
150+
allowed_pack_path =
151+
roots
152+
|> Map.fetch!("extravaganza")
153+
|> Path.join("apps/extravaganza_core/lib/extravaganza/pack_authoring.ex")
154+
155+
write_file(product_path, """
156+
defmodule Extravaganza.ProductBypass do
157+
alias Citadel.AuthorityContract
158+
import Jido.Integration.V2
159+
160+
def run(attrs) do
161+
Mezzanine.WorkflowRuntime.start(attrs)
162+
AITrace.Event.new("bypass")
163+
AuthorityContract
164+
end
165+
end
166+
""")
167+
168+
write_file(allowed_pack_path, """
169+
defmodule Extravaganza.PackAuthoring do
170+
alias Mezzanine.Pack
171+
172+
def pack(attrs), do: Pack.new(attrs)
173+
end
174+
""")
175+
176+
assert {:ok, receipt} =
177+
StructuralGateScanner.scan([product_path, allowed_pack_path], target_roots: roots)
178+
179+
assert receipt.status == :open_defect
180+
181+
product_findings = Enum.filter(receipt.findings, &(&1.path == product_path))
182+
183+
assert Enum.count(product_findings, &(&1.rule == :direct_lower_owner_import_in_product)) >= 2
184+
assert Enum.any?(product_findings, &(&1.token == "Mezzanine.WorkflowRuntime"))
185+
assert Enum.any?(product_findings, &(&1.token == "AITrace.Event"))
186+
187+
refute Enum.any?(receipt.findings, &(&1.path == allowed_pack_path))
188+
end
189+
144190
test "passes registered generic dispatch proof bundle", %{roots: roots} do
145191
path =
146192
roots

0 commit comments

Comments
 (0)