cortex-runtime 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. cortex/__init__.py +51 -0
  2. cortex/__main__.py +13 -0
  3. cortex/client.py +263 -0
  4. cortex/compat.py +20 -0
  5. cortex/exceptions.py +57 -0
  6. cortex/plugin.py +50 -0
  7. cortex/py.typed +1 -0
  8. cortex/schema/__init__.py +29 -0
  9. cortex/schema/events.py +127 -0
  10. cortex/tools/__init__.py +3 -0
  11. cortex/tools/cli/__init__.py +3 -0
  12. cortex/tools/cli/main.py +139 -0
  13. cortex/tools/cli/runner.py +94 -0
  14. cortex/tools/cli/scaffolder.py +109 -0
  15. cortex/tools/gen_test_bin.py +35 -0
  16. cortex/tools/kernel/__init__.py +3 -0
  17. cortex/tools/kernel/actors/__init__.py +3 -0
  18. cortex/tools/kernel/actors/executor.py +44 -0
  19. cortex/tools/kernel/actors/planner.py +38 -0
  20. cortex/tools/kernel/context.py +22 -0
  21. cortex/tools/kernel/drivers/__init__.py +3 -0
  22. cortex/tools/kernel/drivers/mock_robot.py +84 -0
  23. cortex/tools/kernel/drivers/rtl_verilator.py +49 -0
  24. cortex/tools/kernel/graph/__init__.py +3 -0
  25. cortex/tools/kernel/graph/analyzer.py +52 -0
  26. cortex/tools/kernel/graph/execution_graph.py +44 -0
  27. cortex/tools/kernel/mailbox.py +35 -0
  28. cortex/tools/kernel/plugin/__init__.py +3 -0
  29. cortex/tools/kernel/plugin/loader.py +87 -0
  30. cortex/tools/kernel/plugin/manifest.py +53 -0
  31. cortex/tools/kernel/registry.py +36 -0
  32. cortex/tools/kernel/schema/__init__.py +3 -0
  33. cortex/tools/kernel/schema/contract.py +16 -0
  34. cortex/tools/kernel/schema/event.py +54 -0
  35. cortex/tools/kernel/schema/message.py +105 -0
  36. cortex/tools/kernel/schema/workflow.py +38 -0
  37. cortex/tools/kernel/services/__init__.py +3 -0
  38. cortex/tools/kernel/services/event_store.py +25 -0
  39. cortex/tools/kernel/services/execution_intelligence.py +49 -0
  40. cortex/tools/kernel/services/graph_builder.py +81 -0
  41. cortex/tools/kernel/services/replay.py +50 -0
  42. cortex/tools/kernel/services/verification.py +85 -0
  43. cortex/tools/kernel/transport.py +50 -0
  44. cortex/tools/run_phase2_verification.sh +20 -0
  45. cortex/tools/verification/__init__.py +5 -0
  46. cortex/tools/verification/adapters/__init__.py +3 -0
  47. cortex/tools/verification/adapters/base.py +14 -0
  48. cortex/tools/verification/adapters/coq.py +73 -0
  49. cortex/tools/verification/adapters/rtl.py +77 -0
  50. cortex/tools/verification/adapters/rust.py +72 -0
  51. cortex/tools/verification/archive.py +58 -0
  52. cortex/tools/verification/bus.py +28 -0
  53. cortex/tools/verification/contract.py +70 -0
  54. cortex/tools/verification/engine.py +121 -0
  55. cortex/tools/verification/generator/__init__.py +3 -0
  56. cortex/tools/verification/generator/composer.py +36 -0
  57. cortex/tools/verification/generator/program.py +46 -0
  58. cortex/tools/verification/generator/state.py +54 -0
  59. cortex/tools/verification/invariants/__init__.py +3 -0
  60. cortex/tools/verification/invariants/capability.py +62 -0
  61. cortex/tools/verification/metrics/__init__.py +3 -0
  62. cortex/tools/verification/metrics/base.py +18 -0
  63. cortex/tools/verification/metrics/opcode.py +25 -0
  64. cortex/tools/verification/metrics/state_space.py +26 -0
  65. cortex/tools/verification/metrics/trap.py +26 -0
  66. cortex/tools/verification/mutation.py +48 -0
  67. cortex/tools/verification/oracle.py +164 -0
  68. cortex/tools/verification/schema/__init__.py +44 -0
  69. cortex/tools/verification/schema/event.py +44 -0
  70. cortex/tools/verification/shrink.py +26 -0
  71. cortex/tools/verify.py +64 -0
  72. cortex_runtime-0.2.0.dist-info/METADATA +220 -0
  73. cortex_runtime-0.2.0.dist-info/RECORD +76 -0
  74. cortex_runtime-0.2.0.dist-info/WHEEL +4 -0
  75. cortex_runtime-0.2.0.dist-info/entry_points.txt +2 -0
  76. cortex_runtime-0.2.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,70 @@
1
+ """
2
+ YAML Contract Parser & Schema Validator for Verification Runs
3
+ """
4
+
5
+ import os
6
+ from dataclasses import dataclass
7
+ from typing import Any
8
+
9
+
10
+ @dataclass
11
+ class VerificationContract:
12
+ schema_version: str
13
+ contract_id: str
14
+ description: str
15
+ toolchain_requirements: dict[str, str]
16
+ fuzzing_parameters: dict[str, Any]
17
+ targets: dict[str, str]
18
+ oracle: dict[str, Any]
19
+ output_requirements: dict[str, str]
20
+
21
+ @classmethod
22
+ def load(cls, contract_path: str) -> "VerificationContract":
23
+ if not os.path.exists(contract_path):
24
+ raise FileNotFoundError(f"Contract file not found: {contract_path}")
25
+
26
+ # Basic lightweight YAML parser without third-party requirement
27
+ data: dict[str, Any] = {}
28
+ current_key = None
29
+
30
+ with open(contract_path, "r") as f:
31
+ for line in f:
32
+ line = line.strip()
33
+ if not line or line.startswith("#"):
34
+ continue
35
+
36
+ if ":" in line and not line.startswith("-"):
37
+ parts = line.split(":", 1)
38
+ key = parts[0].strip()
39
+ val = parts[1].strip()
40
+
41
+ if val == "":
42
+ current_key = key
43
+ data[current_key] = {}
44
+ else:
45
+ # strip quotes if present
46
+ if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")):
47
+ val = val[1:-1]
48
+ elif val.lower() == "true":
49
+ val = True
50
+ elif val.lower() == "false":
51
+ val = False
52
+ elif val.isdigit():
53
+ val = int(val)
54
+
55
+ if current_key and line.startswith(" "):
56
+ data[current_key][key] = val
57
+ else:
58
+ data[key] = val
59
+ current_key = None
60
+
61
+ return cls(
62
+ schema_version=data.get("schema_version", "1.0.0"),
63
+ contract_id=data.get("contract_id", "default"),
64
+ description=data.get("description", ""),
65
+ toolchain_requirements=data.get("toolchain_requirements", {}),
66
+ fuzzing_parameters=data.get("fuzzing_parameters", {}),
67
+ targets=data.get("targets", {}),
68
+ oracle=data.get("oracle", {}),
69
+ output_requirements=data.get("output_requirements", {})
70
+ )
@@ -0,0 +1,121 @@
1
+ """
2
+ Core Verification Engine Orchestrator
3
+ """
4
+
5
+ import json
6
+ import os
7
+ from typing import Any
8
+
9
+ from cortex.tools.verification.adapters.coq import CoqAdapter
10
+ from cortex.tools.verification.adapters.rtl import RTLAdapter
11
+ from cortex.tools.verification.adapters.rust import RustAdapter
12
+ from cortex.tools.verification.archive import CounterexampleArchive
13
+ from cortex.tools.verification.contract import VerificationContract
14
+ from cortex.tools.verification.generator.composer import ScenarioComposer
15
+ from cortex.tools.verification.metrics.opcode import OpcodeMetric
16
+ from cortex.tools.verification.metrics.state_space import StateSpaceMetric
17
+ from cortex.tools.verification.metrics.trap import TrapMetric
18
+ from cortex.tools.verification.mutation import FaultMutationEngine
19
+ from cortex.tools.verification.oracle import VerificationOracle
20
+ from cortex.tools.verification.shrink import SemanticShrinker
21
+
22
+
23
+ class VerificationEngine:
24
+ def __init__(self, contract: VerificationContract, seed_val: int):
25
+ self.contract = contract
26
+ self.seed_val = seed_val
27
+ self.oracle = VerificationOracle(
28
+ version=contract.oracle.get("version", "v2.1.0"),
29
+ strict_trap_matching=contract.oracle.get("strict_trap_cause_matching", True)
30
+ )
31
+ self.shrinker = SemanticShrinker(
32
+ max_shrunk_steps=contract.fuzzing_parameters.get("max_shrunk_steps", 50)
33
+ )
34
+ self.archiver = CounterexampleArchive(
35
+ archive_dir=contract.output_requirements.get("counterexample_directory", "artifacts/counterexamples/")
36
+ )
37
+
38
+ self.opcode_metric = OpcodeMetric()
39
+ self.trap_metric = TrapMetric()
40
+ self.state_metric = StateSpaceMetric()
41
+
42
+ def run_verification(
43
+ self,
44
+ iterations: int = 100,
45
+ inject_failure: str | None = None
46
+ ) -> dict[str, Any]:
47
+ coq_adapter = CoqAdapter()
48
+ rust_adapter = RustAdapter()
49
+ rtl_adapter = RTLAdapter()
50
+
51
+ total_steps_evaluated = 0
52
+
53
+ for i in range(iterations):
54
+ iter_seed = self.seed_val + i
55
+ composer = ScenarioComposer(iter_seed)
56
+ scenario = composer.compose_scenario(num_instructions=6)
57
+
58
+ # Generate temp artifacts
59
+ os.makedirs("artifacts/temp/", exist_ok=True)
60
+ composer.export_artifacts(
61
+ scenario,
62
+ "artifacts/temp/test_scenario.json",
63
+ "artifacts/temp/test_payload.bin"
64
+ )
65
+
66
+ # Parse traces
67
+ coq_trace = coq_adapter.parse_trace("Research/artifacts/phase2/coq_trace.json")
68
+ rust_trace = rust_adapter.parse_trace("Research/artifacts/phase2/emulator_trace.json")
69
+ rtl_trace = rtl_adapter.parse_trace("Research/artifacts/phase2/rtl_trace.json")
70
+
71
+ # Apply mutation if injected
72
+ if inject_failure:
73
+ mutation_engine = FaultMutationEngine(inject_failure)
74
+ rtl_trace = mutation_engine.apply_mutation(rtl_trace)
75
+
76
+ # Record metrics
77
+ for step in coq_trace:
78
+ self.opcode_metric.record_step(step)
79
+ self.trap_metric.record_step(step)
80
+ self.state_metric.record_step(step)
81
+ total_steps_evaluated += 1
82
+
83
+ # Evaluate equivalence
84
+ diagnostic = self.oracle.evaluate_equivalence(coq_trace, rust_trace, rtl_trace)
85
+
86
+ if diagnostic["status"] == "FAIL":
87
+ failing_step = diagnostic.get("failing_step", 1)
88
+ shrunk_scenario = self.shrinker.shrink_scenario(scenario, failing_step)
89
+ case_hash = self.archiver.archive_failure(
90
+ shrunk_scenario,
91
+ diagnostic,
92
+ seed=f"0x{iter_seed:08X}"
93
+ )
94
+ return {
95
+ "status": "FAIL",
96
+ "iteration": i + 1,
97
+ "seed": f"0x{iter_seed:08X}",
98
+ "diagnostic": diagnostic,
99
+ "counterexample_hash": case_hash
100
+ }
101
+
102
+ # Generate run summary JSON
103
+ summary = {
104
+ "seed": f"0x{self.seed_val:08X}",
105
+ "iterations": iterations,
106
+ "total_steps_evaluated": total_steps_evaluated,
107
+ "status": "PASSED",
108
+ "metrics": {
109
+ "opcode_coverage": self.opcode_metric.get_summary(),
110
+ "trap_coverage": self.trap_metric.get_summary(),
111
+ "state_space_explored": self.state_metric.get_summary()
112
+ }
113
+ }
114
+
115
+ output_dir = self.contract.output_requirements.get("archive_directory", "artifacts/phase3a/")
116
+ os.makedirs(output_dir, exist_ok=True)
117
+ summary_path = os.path.join(output_dir, "run_summary.json")
118
+ with open(summary_path, "w") as f:
119
+ json.dump(summary, f, indent=2)
120
+
121
+ return summary
@@ -0,0 +1,3 @@
1
+ """
2
+ Scenario & Machine State Generators
3
+ """
@@ -0,0 +1,36 @@
1
+ """
2
+ Scenario Composer: Merges Machine State and Instruction Stream into Reproducible Payloads
3
+ """
4
+
5
+ import json
6
+ import struct
7
+ from typing import Any
8
+
9
+ from cortex.tools.verification.generator.program import ProgramGenerator
10
+ from cortex.tools.verification.generator.state import StateGenerator
11
+
12
+
13
+ class ScenarioComposer:
14
+ def __init__(self, seed: int):
15
+ self.seed = seed
16
+ self.state_gen = StateGenerator(seed)
17
+ self.prog_gen = ProgramGenerator(seed)
18
+
19
+ def compose_scenario(self, num_instructions: int = 8) -> dict[str, Any]:
20
+ initial_state = self.state_gen.generate_initial_state()
21
+ program = self.prog_gen.generate_program(num_instructions)
22
+
23
+ return {
24
+ "seed": f"0x{self.seed:08X}",
25
+ "initial_state": initial_state,
26
+ "program": program
27
+ }
28
+
29
+ def export_artifacts(self, scenario: dict[str, Any], json_path: str, bin_path: str):
30
+ with open(json_path, "w") as f:
31
+ json.dump(scenario, f, indent=2)
32
+
33
+ with open(bin_path, "wb") as f:
34
+ for inst in scenario["program"]:
35
+ # Big-endian 32-bit instruction word
36
+ f.write(struct.pack(">I", inst["raw_uint32"]))
@@ -0,0 +1,46 @@
1
+ """
2
+ Instruction Stream Fuzzing Generator for Cortex Capabilities
3
+ """
4
+
5
+ import random
6
+ from typing import Any
7
+
8
+
9
+ class ProgramGenerator:
10
+ def __init__(self, seed: int):
11
+ self.rng = random.Random(seed)
12
+
13
+ def generate_program(self, num_instructions: int = 8) -> list[dict[str, Any]]:
14
+ instructions = []
15
+
16
+ opcodes = [
17
+ ("INVOKE_CAP", 0x01, 0x04000000),
18
+ ("RESTRICT_CAP", 0x03, 0x0C000000),
19
+ ("REVOKE_CAP", 0x05, 0x14000000),
20
+ ("GRANT_CAP", 0x02, 0x08000000),
21
+ ("ILLEGAL_OP", 0x0F, 0x00000000),
22
+ ]
23
+
24
+ for _ in range(num_instructions):
25
+ op_name, op_code, raw_base = self.rng.choice(opcodes)
26
+ src_reg = self.rng.randint(0, 3)
27
+ dst_reg = self.rng.randint(0, 3)
28
+ imm = self.rng.choice([0x0000, 0x1000, 0x2000, 0x4000, 0x7000, 0xFFFF])
29
+
30
+ if op_name == "RESTRICT_CAP":
31
+ raw = raw_base | (src_reg << 16) | imm
32
+ elif op_name == "INVOKE_CAP" or op_name == "REVOKE_CAP":
33
+ raw = raw_base | (src_reg << 16)
34
+ elif op_name == "GRANT_CAP":
35
+ raw = raw_base | (src_reg << 16) | (dst_reg << 20) | imm
36
+ else:
37
+ raw = 0x00000000
38
+
39
+ instructions.append({
40
+ "opcode_name": op_name,
41
+ "opcode_val": op_code,
42
+ "raw_hex": f"0x{raw:08x}",
43
+ "raw_uint32": raw
44
+ })
45
+
46
+ return instructions
@@ -0,0 +1,54 @@
1
+ """
2
+ Initial STCR & Machine Register State Fuzzing Generator
3
+ """
4
+
5
+ import random
6
+ from typing import Any
7
+
8
+
9
+ class StateGenerator:
10
+ def __init__(self, seed: int):
11
+ self.rng = random.Random(seed)
12
+
13
+ def generate_initial_state(self) -> dict[str, Any]:
14
+ stcr_file = []
15
+
16
+ # STCR0 always starts as root execution capability by default or random
17
+ root_v = True
18
+ root_mask = 0x7000 # READ | WRITE | EXEC (0x4000 | 0x2000 | 0x1000)
19
+ root_base = 0x2000
20
+ root_epoch = 0
21
+
22
+ stcr_file.append({
23
+ "index": 0,
24
+ "valid": root_v,
25
+ "permissions": root_mask,
26
+ "base_address": root_base,
27
+ "epoch": root_epoch
28
+ })
29
+
30
+ for reg_id in range(1, 32):
31
+ is_valid = self.rng.choice([True, False, False, False]) # 25% chance of valid STCR slot
32
+ if is_valid:
33
+ mask = self.rng.choice([0x4000, 0x2000, 0x1000, 0x7000, 0x0000])
34
+ base = self.rng.choice([0x0, 0x1000, 0x2000, 0x8000, 0xFFFF0000])
35
+ epoch = self.rng.choice([0, 1, 100, 65534, 65535]) # include boundary epoch limits
36
+ else:
37
+ mask = 0
38
+ base = 0
39
+ epoch = 0
40
+
41
+ stcr_file.append({
42
+ "index": reg_id,
43
+ "valid": is_valid,
44
+ "permissions": mask,
45
+ "base_address": base,
46
+ "epoch": epoch
47
+ })
48
+
49
+ return {
50
+ "pc": 0x1000,
51
+ "privilege_mode": "Machine",
52
+ "reg_hec": self.rng.choice([0, 1, 65535]),
53
+ "stcr_registers": stcr_file
54
+ }
@@ -0,0 +1,3 @@
1
+ """
2
+ Unary Capability Safety Invariant Plugins
3
+ """
@@ -0,0 +1,62 @@
1
+ """
2
+ Capability Safety Invariants Engine
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from cortex.tools.verification.schema.event import CommitEventV1
8
+
9
+ # Define invariant boundary constants to avoid magic numbers
10
+ MIN_EPOCH = 0
11
+ MAX_EPOCH = 65535
12
+ NEUTRAL_TRAP_EXPECTED_VAL = 0
13
+ MIN_RETIREMENT_STEP = 1
14
+
15
+ @dataclass
16
+ class InvariantResult:
17
+ passed: bool
18
+ invariant_id: str
19
+ error_message: str = ""
20
+
21
+ @classmethod
22
+ def PASS(cls, invariant_id: str) -> "InvariantResult":
23
+ return cls(passed=True, invariant_id=invariant_id)
24
+
25
+ @classmethod
26
+ def FAIL(cls, invariant_id: str, error_message: str) -> "InvariantResult":
27
+ return cls(passed=False, invariant_id=invariant_id, error_message=error_message)
28
+
29
+
30
+ class EpochMonotonicityInvariant:
31
+ """INV_03: Capability epochs must remain in valid 16-bit range."""
32
+ def check(self, event: CommitEventV1) -> InvariantResult:
33
+ if event.architectural is None:
34
+ return InvariantResult.FAIL("INV_03_ATOMIC_STCR_UPDATE", "Missing architectural state")
35
+ for stcr in event.architectural.stcr:
36
+ epoch = stcr.get("epoch", stcr.get("max_epoch", 0))
37
+ if not isinstance(epoch, int) or epoch < MIN_EPOCH or epoch > MAX_EPOCH:
38
+ return InvariantResult.FAIL("INV_03_ATOMIC_STCR_UPDATE", f"Epoch {epoch} out of 16-bit bounds")
39
+ return InvariantResult.PASS("INV_03_ATOMIC_STCR_UPDATE")
40
+
41
+
42
+ class NeutralTrapInvariant:
43
+ """INV_02: Verifies that committed neutral traps maintain trap_val == 0."""
44
+ def check(self, event: CommitEventV1) -> InvariantResult:
45
+ if event.architectural is None:
46
+ return InvariantResult.FAIL("INV_02_NEUTRAL_TRAP_ZERO_VAL", "Missing architectural state")
47
+ trap = event.architectural.trap
48
+ is_triggered = trap.get("triggered", False)
49
+ trap_val = trap.get("trap_val", 0)
50
+ if is_triggered and trap_val != NEUTRAL_TRAP_EXPECTED_VAL:
51
+ return InvariantResult.FAIL("INV_02_NEUTRAL_TRAP_ZERO_VAL", f"Non-zero trap_val {trap_val} on neutral trap")
52
+ return InvariantResult.PASS("INV_02_NEUTRAL_TRAP_ZERO_VAL")
53
+
54
+
55
+ class SingleRetirementInvariant:
56
+ """INV_01: Exactly one architectural retirement event per execution step."""
57
+ def check(self, event: CommitEventV1) -> InvariantResult:
58
+ if event.observation is None:
59
+ return InvariantResult.FAIL("INV_01_SINGLE_RETIREMENT", "Missing observation metadata")
60
+ if event.observation.step < MIN_RETIREMENT_STEP:
61
+ return InvariantResult.FAIL("INV_01_SINGLE_RETIREMENT", f"Invalid retirement step {event.observation.step}")
62
+ return InvariantResult.PASS("INV_01_SINGLE_RETIREMENT")
@@ -0,0 +1,3 @@
1
+ """
2
+ Multi-Dimensional Metric Plugins
3
+ """
@@ -0,0 +1,18 @@
1
+ """
2
+ Base Metric Plugin Interface
3
+ """
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Any
7
+
8
+ from cortex.tools.verification.schema import CanonicalState
9
+
10
+
11
+ class BaseMetric(ABC):
12
+ @abstractmethod
13
+ def record_step(self, step: CanonicalState):
14
+ pass
15
+
16
+ @abstractmethod
17
+ def get_summary(self) -> dict[str, Any]:
18
+ pass
@@ -0,0 +1,25 @@
1
+ """
2
+ Instruction Opcode Coverage Metric Tracker
3
+ """
4
+
5
+ from typing import Any
6
+
7
+ from cortex.tools.verification.metrics.base import BaseMetric
8
+ from cortex.tools.verification.schema import CanonicalState
9
+
10
+
11
+ class OpcodeMetric(BaseMetric):
12
+ def __init__(self):
13
+ self.seen_opcodes: set[str] = set()
14
+ self.total_opcodes = 18
15
+
16
+ def record_step(self, step: CanonicalState):
17
+ self.seen_opcodes.add(step.instruction)
18
+
19
+ def get_summary(self) -> dict[str, Any]:
20
+ count = len(self.seen_opcodes)
21
+ pct = min(100.0, (count / float(self.total_opcodes)) * 100.0)
22
+ return {
23
+ "unique_opcodes_seen": count,
24
+ "coverage_percentage": round(pct, 1)
25
+ }
@@ -0,0 +1,26 @@
1
+ """
2
+ Unique Architectural State Explorer Metric Tracker
3
+ """
4
+
5
+ import hashlib
6
+ import json
7
+ from typing import Any
8
+
9
+ from cortex.tools.verification.metrics.base import BaseMetric
10
+ from cortex.tools.verification.schema import CanonicalState
11
+
12
+
13
+ class StateSpaceMetric(BaseMetric):
14
+ def __init__(self):
15
+ self.visited_states: set[str] = set()
16
+
17
+ def record_step(self, step: CanonicalState):
18
+ state_dict = step.to_dict()
19
+ state_str = json.dumps(state_dict, sort_keys=True).encode("utf-8")
20
+ state_hash = hashlib.sha256(state_str).hexdigest()[:16]
21
+ self.visited_states.add(state_hash)
22
+
23
+ def get_summary(self) -> dict[str, Any]:
24
+ return {
25
+ "unique_states_explored": len(self.visited_states)
26
+ }
@@ -0,0 +1,26 @@
1
+ """
2
+ Trap Cause Path Coverage Metric Tracker
3
+ """
4
+
5
+ from typing import Any
6
+
7
+ from cortex.tools.verification.metrics.base import BaseMetric
8
+ from cortex.tools.verification.schema import CanonicalState
9
+
10
+
11
+ class TrapMetric(BaseMetric):
12
+ def __init__(self):
13
+ self.seen_trap_codes: set[int] = set()
14
+ self.total_trap_codes = 5
15
+
16
+ def record_step(self, step: CanonicalState):
17
+ if step.trap.triggered:
18
+ self.seen_trap_codes.add(step.trap.cause_code)
19
+
20
+ def get_summary(self) -> dict[str, Any]:
21
+ count = len(self.seen_trap_codes)
22
+ pct = min(100.0, (count / float(self.total_trap_codes)) * 100.0)
23
+ return {
24
+ "trap_cause_codes_seen": sorted(list(self.seen_trap_codes)),
25
+ "coverage_percentage": round(pct, 1)
26
+ }
@@ -0,0 +1,48 @@
1
+ from dataclasses import replace
2
+
3
+ from cortex.tools.verification.schema import CanonicalState
4
+
5
+
6
+ class FaultMutationEngine:
7
+ failure_vector: str
8
+
9
+ def __init__(self, failure_vector: str):
10
+ self.failure_vector = failure_vector
11
+
12
+ def apply_mutation(self, rtl_trace: list[CanonicalState]) -> list[CanonicalState]:
13
+ """
14
+ Injects synthetic faults into engine traces to verify pipeline sensitivity.
15
+ """
16
+ if not rtl_trace:
17
+ return rtl_trace
18
+
19
+ mutated: list[CanonicalState] = []
20
+ for step in rtl_trace:
21
+ stcr_clone = [replace(s) for s in step.stcr]
22
+ trap_clone = replace(step.trap)
23
+ c_step = CanonicalState(
24
+ step=step.step,
25
+ pc=step.pc,
26
+ instruction=step.instruction,
27
+ privilege_mode=step.privilege_mode,
28
+ reg_hec=step.reg_hec,
29
+ registers=dict(step.registers),
30
+ stcr=stcr_clone,
31
+ trap=trap_clone
32
+ )
33
+ mutated.append(c_step)
34
+
35
+ if self.failure_vector == "rtl.trap_suppress":
36
+ # Suppress trap flag on step 3
37
+ for step in mutated:
38
+ if step.step == 3:
39
+ step.trap.triggered = False
40
+ step.trap.cause_code = 0
41
+ step.trap.cause_name = "None"
42
+ elif self.failure_vector == "rtl.stcr0.epoch_mismatch":
43
+ # Off-by-one epoch mismatch on step 2
44
+ for step in mutated:
45
+ if step.step == 2 and step.stcr:
46
+ step.stcr[0].epoch += 1
47
+
48
+ return mutated