kibi-core 0.12.0 → 0.14.0

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.
@@ -8,6 +8,9 @@
8
8
  requirement_proof/4
9
9
  ]).
10
10
 
11
+ % Source contains non-ASCII text; do not depend on the host locale.
12
+ :- encoding(utf8).
13
+
11
14
  :- use_module(library(http/json)).
12
15
  :- use_module(library(crypto)).
13
16
  :- use_module(library(date)).
@@ -77,10 +80,10 @@ requirement_proof(ReqId, ReqProps, Context, Proof) :-
77
80
  logic_grounding_stage(ReqId, ReqProps, Inventory, Context, LogicStage),
78
81
  contradiction_stage(ReqId, LogicStage.status, Context, ContradictionStage),
79
82
  scenario_stage(ReqId, ScenarioStage, ScenarioIds),
80
- scenario_test_stage(ScenarioIds, ScenarioTestStage, _ScenarioTests),
83
+ scenario_test_stage(ScenarioIds, ScenarioTestStage, ScenarioTests),
81
84
  passing_e2e_stage(ScenarioTestStage, Context, PassingE2eStage, PassingE2eTests),
82
85
  executable_symbol_stage(PassingE2eTests, ExecutableStage, ExecutableSymbols),
83
- production_symbol_stage(ReqId, PassingE2eTests, ProductionStage, ProductionSymbols),
86
+ production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, PassingE2eStage.receiptEvidence, Context, ProductionStage, ProductionSymbols),
84
87
  source_coordinate_stage(ReqProps, ExecutableSymbols, ProductionSymbols, CoordinateStage),
85
88
  Stages = _{
86
89
  semanticInventory: SemanticStage,
@@ -103,7 +106,8 @@ requirement_proof(ReqId, ReqProps, Context, Proof) :-
103
106
  proofGaps: Gaps,
104
107
  proofAdvisories: Advisories,
105
108
  proofRepairs: Repairs,
106
- proofStages: Stages
109
+ proofStages: Stages,
110
+ testResolutions: ScenarioTestStage.resolutions
107
111
  }.
108
112
 
109
113
  exempt_requirement_proof(Reason, Proof) :-
@@ -236,7 +240,47 @@ inventory_entry_field(List, Key, Value) :-
236
240
  is_list(List),
237
241
  memberchk(Key=Value, List).
238
242
 
243
+ % Pure-function memos, content-addressed by variant_sha1/2, so they need no
244
+ % invalidation: equal inputs always produce equal outputs. Proof evaluation
245
+ % re-parses the same receipt histories and re-validates the same receipts on
246
+ % every coverage run; both are bounded so a long-lived engine cannot grow them
247
+ % without limit.
248
+ :- dynamic pure_memo/3.
249
+ :- dynamic pure_memo_size/1.
250
+
251
+ pure_memo_limit(4096).
252
+
253
+ pure_memo(Kind, Input, Goal, Output) :-
254
+ variant_sha1(Kind-Input, Key),
255
+ ( pure_memo(Key, Kind, Cached)
256
+ -> Output = Cached
257
+ ; call(Goal, Input, Result),
258
+ pure_memo_remember(Key, Kind, Result),
259
+ Output = Result
260
+ ).
261
+
262
+ pure_memo_remember(Key, Kind, Output) :-
263
+ ( pure_memo_size(Size)
264
+ -> true
265
+ ; Size = 0
266
+ ),
267
+ pure_memo_limit(Limit),
268
+ ( Size >= Limit
269
+ -> retractall(pure_memo(_, _, _)),
270
+ NextSize = 1
271
+ ; NextSize is Size + 1
272
+ ),
273
+ retractall(pure_memo_size(_)),
274
+ assertz(pure_memo_size(NextSize)),
275
+ assertz(pure_memo(Key, Kind, Output)).
276
+
239
277
  inventory_entries(Raw, Entries) :-
278
+ ( (atom(Raw) ; string(Raw))
279
+ -> pure_memo(inventory_entries, Raw, parse_inventory_entries, Entries)
280
+ ; parse_inventory_entries(Raw, Entries)
281
+ ).
282
+
283
+ parse_inventory_entries(Raw, Entries) :-
240
284
  ( is_list(Raw)
241
285
  -> Entries = Raw
242
286
  ; Raw = ^^(Value, _)
@@ -532,6 +576,7 @@ scenario_test_stage(ScenarioIds, Stage, ScenarioTests) :-
532
576
  ; Status = passed
533
577
  ),
534
578
  maplist(entity_source_ref, ScenarioTests, Sources),
579
+ maplist(test_resolution_record, ScenarioTests, Resolutions),
535
580
  Stage = _{
536
581
  status: Status,
537
582
  scenarios: ScenarioIds,
@@ -539,7 +584,8 @@ scenario_test_stage(ScenarioIds, Stage, ScenarioTests) :-
539
584
  scenarioTestTargets: ScenarioTestTargets,
540
585
  invalidScenarioTestTargets: InvalidScenarioTestTargets,
541
586
  sources: Sources,
542
- obligations: Obligations
587
+ obligations: Obligations,
588
+ resolutions: Resolutions
543
589
  }.
544
590
 
545
591
  scenario_test_obligation(ScenarioId, Obligation) :-
@@ -724,8 +770,8 @@ receipt_evidence_state(TestId, Scope, Props, Receipts, _Present, _Context,
724
770
  proof_contract_binding(Props, ContractBinding),
725
771
  (ContractBinding == invalid
726
772
  ; Receipts == []
727
- ; member(Receipt, Receipts), \+ valid_receipt_shape(TestId, Receipt)
728
- ; \+ chronological_receipt_history(Receipts)),
773
+ ; pure_memo(receipt_history_well_formed, TestId-Receipts,
774
+ receipt_history_well_formed, false)),
729
775
  !.
730
776
  receipt_evidence_state(TestId, Scope, Props, Receipts, _Present, Context, Evidence) :-
731
777
  receipt_for_current_mode(TestId, Context, Receipts, SnapshotReceipts),
@@ -742,6 +788,13 @@ receipt_evidence_state(TestId, Scope, Props, Receipts, _Present, Context, Eviden
742
788
  )
743
789
  ).
744
790
 
791
+ receipt_history_well_formed(TestId-Receipts, WellFormed) :-
792
+ ( forall(member(Receipt, Receipts), valid_receipt_shape(TestId, Receipt)),
793
+ chronological_receipt_history(Receipts)
794
+ -> WellFormed = true
795
+ ; WellFormed = false
796
+ ).
797
+
745
798
  proof_receipt_entries(Props, Receipts, Present) :-
746
799
  ( memberchk(proof_receipts=Raw, Props)
747
800
  -> Present = true,
@@ -1069,6 +1122,14 @@ test_missing_executable_symbol(TestId) :-
1069
1122
  \+ (kb_relationship(executable_for, SymbolId, TestId), kb_entity(SymbolId, symbol, _)).
1070
1123
 
1071
1124
  production_symbol_stage(ReqId, PassingE2eTests, Stage, ProductionSymbols) :-
1125
+ findall(TestId,
1126
+ (kb_relationship(specified_by, ReqId, ScenarioId),
1127
+ scenario_test(ScenarioId, TestId)),
1128
+ ScenarioTests0),
1129
+ sort(ScenarioTests0, ScenarioTests),
1130
+ production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, [], _{}, Stage, ProductionSymbols).
1131
+
1132
+ production_symbol_stage(ReqId, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, Stage, ProductionSymbols) :-
1072
1133
  findall(SymbolId,
1073
1134
  (kb_relationship(implements, SymbolId, ReqId),
1074
1135
  kb_entity(SymbolId, symbol, _),
@@ -1080,12 +1141,16 @@ production_symbol_stage(ReqId, PassingE2eTests, Stage, ProductionSymbols) :-
1080
1141
  include(symbol_not_covered_by_tests(PassingE2eTests), ProductionSymbols, UncoveredSymbols),
1081
1142
  production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests, UncoveredSymbols, Status, StatusReason),
1082
1143
  maplist(symbol_coordinate_ref, ProductionSymbols, Coordinates),
1144
+ production_coverage_explanations(
1145
+ ReqId, ProductionSymbols, StructuralSymbols, UncoveredSymbols,
1146
+ PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, Explanations),
1083
1147
  StageBase = _{
1084
1148
  status: Status,
1085
1149
  symbols: ProductionSymbols,
1086
1150
  structuralSymbols: StructuralSymbols,
1087
1151
  uncoveredSymbols: UncoveredSymbols,
1088
- coordinates: Coordinates
1152
+ coordinates: Coordinates,
1153
+ explanations: Explanations
1089
1154
  },
1090
1155
  ( Status == passed
1091
1156
  -> Stage = StageBase
@@ -1127,6 +1192,271 @@ production_stage_status(ProductionSymbols, StructuralSymbols, PassingE2eTests, U
1127
1192
  symbol_not_covered_by_tests(Tests, SymbolId) :-
1128
1193
  \+ (member(TestId, Tests), kb_relationship(covered_by, SymbolId, TestId)).
1129
1194
 
1195
+ production_coverage_explanations(ReqId, ProductionSymbols, StructuralSymbols, UncoveredSymbols,
1196
+ PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, Explanations) :-
1197
+ maplist(production_symbol_explanation(ReqId, UncoveredSymbols, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context),
1198
+ ProductionSymbols, ProductionExplanations),
1199
+ maplist(structural_symbol_explanation(ReqId, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context),
1200
+ StructuralSymbols, StructuralExplanations),
1201
+ append(ProductionExplanations, StructuralExplanations, Explanations).
1202
+
1203
+ structural_symbol_explanation(ReqId, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Explanation) :-
1204
+ symbol_classification(SymbolId, Classification),
1205
+ symbol_coverage_candidates(structural, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Candidates),
1206
+ explanation_reason_text(structural_unit_contract, ReasonText),
1207
+ Explanation = _{
1208
+ symbolId: SymbolId,
1209
+ requirementId: ReqId,
1210
+ classification: Classification,
1211
+ status: covered,
1212
+ reason: structural_unit_contract,
1213
+ reasonText: ReasonText,
1214
+ coverageCandidates: Candidates
1215
+ }.
1216
+
1217
+ production_symbol_explanation(ReqId, UncoveredSymbols, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Explanation) :-
1218
+ symbol_classification(SymbolId, Classification),
1219
+ symbol_coverage_candidates(behavioral, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Candidates),
1220
+ production_symbol_rollup(UncoveredSymbols, PassingE2eTests, SymbolId, Candidates, Status, Reason),
1221
+ explanation_reason_text(Reason, ReasonText),
1222
+ Explanation = _{
1223
+ symbolId: SymbolId,
1224
+ requirementId: ReqId,
1225
+ classification: Classification,
1226
+ status: Status,
1227
+ reason: Reason,
1228
+ reasonText: ReasonText,
1229
+ coverageCandidates: Candidates
1230
+ }.
1231
+
1232
+ production_symbol_rollup(_Uncovered, [], _SymbolId, _Candidates, uncovered, stage_blocked_no_passing_e2e) :-
1233
+ !.
1234
+ production_symbol_rollup(UncoveredSymbols, _Passing, SymbolId, Candidates, uncovered, Reason) :-
1235
+ memberchk(SymbolId, UncoveredSymbols),
1236
+ !,
1237
+ (Candidates == [] -> Reason = covered_by_missing ; Reason = no_qualifying_e2e_coverage).
1238
+ production_symbol_rollup(_Uncovered, _Passing, _SymbolId, _Candidates, covered, covered).
1239
+
1240
+ symbol_classification(SymbolId, Classification) :-
1241
+ kb_entity(SymbolId, symbol, Props),
1242
+ memberchk(symbol_role=RawRole, Props),
1243
+ normalize_atom(RawRole, Role),
1244
+ Role \= '',
1245
+ !,
1246
+ Classification = Role.
1247
+ symbol_classification(_SymbolId, unknown).
1248
+
1249
+ symbol_coverage_candidates(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, SymbolId, Candidates) :-
1250
+ findall(TestId, kb_relationship(covered_by, SymbolId, TestId), TestIds0),
1251
+ sort(TestIds0, TestIds),
1252
+ maplist(coverage_candidate_dict(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context), TestIds, Candidates).
1253
+
1254
+ coverage_candidate_dict(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, TestId, Candidate) :-
1255
+ coverage_candidate_qualification(Mode, PassingE2eTests, ScenarioTests, ReceiptEvidence, Context, TestId, Qualifies, Primary, Secondaries),
1256
+ explanation_reason_text(Primary, ReasonText),
1257
+ cheap_candidate_scope(TestId, Scope),
1258
+ reused_receipt_state(ReceiptEvidence, TestId, ReceiptState),
1259
+ CandidateBase = _{
1260
+ testId: TestId,
1261
+ relationship: covered_by,
1262
+ qualifies: Qualifies,
1263
+ reason: Primary,
1264
+ reasonText: ReasonText,
1265
+ secondaryReasons: Secondaries,
1266
+ scope: Scope
1267
+ },
1268
+ ( ReceiptState == none
1269
+ -> Candidate = CandidateBase
1270
+ ; put_dict(receiptState, CandidateBase, ReceiptState, Candidate)
1271
+ ).
1272
+
1273
+ % Shared candidate evaluator. Mode selects the coverage contract:
1274
+ % behavioral — production symbols require passing scenario-backed E2E
1275
+ % structural — type-shape symbols may also qualify via a unit contract
1276
+ coverage_candidate_qualification(_Mode, PassingE2eTests, _ScenarioTests, _ReceiptEvidence, _Context, TestId, true, covered, []) :-
1277
+ memberchk(TestId, PassingE2eTests),
1278
+ !.
1279
+ coverage_candidate_qualification(_Mode, _Passing, _Scenario, _Evidence, _Context, TestId, false, missing_test_entity, []) :-
1280
+ \+ kb_entity(TestId, test, _),
1281
+ !.
1282
+ coverage_candidate_qualification(structural, _Passing, _Scenario, _Evidence, _Context, TestId, true, structural_unit_contract, []) :-
1283
+ structural_unit_contract_test(TestId),
1284
+ !.
1285
+ coverage_candidate_qualification(_Mode, _Passing, ScenarioTests, ReceiptEvidence, Context, TestId, false, Primary, Secondaries) :-
1286
+ cheap_scope_reject_reason(TestId, ScopeReason),
1287
+ ( \+ memberchk(TestId, ScenarioTests)
1288
+ -> Primary = test_not_in_requirement_scenario_chain,
1289
+ maybe_singleton(ScopeReason, ScopeSecondaries),
1290
+ reused_receipt_reject_reasons(ReceiptEvidence, TestId, ReceiptSecondaries),
1291
+ append(ScopeSecondaries, ReceiptSecondaries, Secondaries)
1292
+ ; ScopeReason \= none
1293
+ -> Primary = ScopeReason,
1294
+ reused_receipt_reject_reasons(ReceiptEvidence, TestId, Secondaries)
1295
+ ; receipt_reject_for_in_chain(ReceiptEvidence, Context, TestId, Primary, Secondaries)
1296
+ ).
1297
+
1298
+ structural_unit_contract_test(TestId) :-
1299
+ kb_entity(TestId, test, TestProps),
1300
+ test_scope(TestProps, unit),
1301
+ memberchk(status=RawStatus, TestProps),
1302
+ normalize_atom(RawStatus, Status),
1303
+ memberchk(Status, [active, passing]).
1304
+
1305
+ cheap_candidate_scope(TestId, Scope) :-
1306
+ kb_entity(TestId, test, Props),
1307
+ !,
1308
+ test_scope(Props, Scope).
1309
+ cheap_candidate_scope(_TestId, unknown).
1310
+
1311
+ cheap_scope_reject_reason(TestId, Reason) :-
1312
+ cheap_candidate_scope(TestId, Scope),
1313
+ scope_reject_reason(Scope, Reason).
1314
+
1315
+ scope_reject_reason(unit, test_scope_is_unit) :- !.
1316
+ scope_reject_reason(end_to_end, none) :- !.
1317
+ scope_reject_reason(_Scope, test_scope_is_non_e2e).
1318
+
1319
+ maybe_singleton(none, []) :- !.
1320
+ maybe_singleton(Reason, [Reason]).
1321
+
1322
+ reused_receipt_state(Evidence, TestId, State) :-
1323
+ member(Item, Evidence),
1324
+ Item.testId == TestId,
1325
+ !,
1326
+ State = Item.state.
1327
+ reused_receipt_state(_Evidence, _TestId, none).
1328
+
1329
+ reused_receipt_item(Evidence, TestId, Item) :-
1330
+ member(Item, Evidence),
1331
+ Item.testId == TestId,
1332
+ !.
1333
+
1334
+ reused_receipt_reject_reasons(Evidence, TestId, Reasons) :-
1335
+ reused_receipt_item(Evidence, TestId, Item),
1336
+ !,
1337
+ receipt_item_reject_reason(Item, Reason),
1338
+ maybe_singleton(Reason, Reasons).
1339
+ reused_receipt_reject_reasons(_Evidence, _TestId, []).
1340
+
1341
+ receipt_reject_for_in_chain(Evidence, _Context, TestId, Primary, []) :-
1342
+ reused_receipt_item(Evidence, TestId, Item),
1343
+ !,
1344
+ receipt_item_reject_reason(Item, Reason),
1345
+ (Reason == none -> Primary = no_qualifying_e2e_coverage ; Primary = Reason).
1346
+ receipt_reject_for_in_chain(_Evidence, Context, TestId, Primary, []) :-
1347
+ is_dict(Context),
1348
+ Context \= _{},
1349
+ test_receipt_evidence(Context, TestId, Evidence),
1350
+ !,
1351
+ receipt_item_reject_reason(Evidence, Reason),
1352
+ (Reason == none -> Primary = no_qualifying_e2e_coverage ; Primary = Reason).
1353
+ receipt_reject_for_in_chain(_Evidence, _Context, _TestId, no_qualifying_e2e_coverage, []).
1354
+
1355
+ receipt_item_reject_reason(Item, stale_proof_receipt) :-
1356
+ Item.state == stale,
1357
+ get_dict(codeSnapshot, Item, _),
1358
+ !.
1359
+ receipt_item_reject_reason(Item, receipt_snapshot_mismatch) :-
1360
+ Item.state == stale,
1361
+ !.
1362
+ receipt_item_reject_reason(Item, Reason) :-
1363
+ receipt_state_reject_reason(Item.state, Reason).
1364
+
1365
+ receipt_state_reject_reason(missing, missing_proof_receipt) :- !.
1366
+ receipt_state_reject_reason(snapshot_unavailable, proof_snapshot_unavailable) :- !.
1367
+ receipt_state_reject_reason(contract_mismatch, receipt_contract_mismatch) :- !.
1368
+ receipt_state_reject_reason(invalid, invalid_proof_receipt) :- !.
1369
+ receipt_state_reject_reason(failed, failed_proof_receipt) :- !.
1370
+ receipt_state_reject_reason(not_end_to_end, none) :- !.
1371
+ receipt_state_reject_reason(stale, receipt_snapshot_mismatch) :- !.
1372
+ receipt_state_reject_reason(passed, none) :- !.
1373
+ receipt_state_reject_reason(none, none) :- !.
1374
+ receipt_state_reject_reason(_, none).
1375
+
1376
+ explanation_reason_text(Reason, Text) :-
1377
+ explanation_reason_text_def(Reason, Text),
1378
+ !.
1379
+ explanation_reason_text(Reason, Reason) :-
1380
+ atom(Reason).
1381
+
1382
+ explanation_reason_text_def(covered, "Qualifying covered_by to a passing scenario-backed E2E test").
1383
+ explanation_reason_text_def(covered_by_missing, "No covered_by relationship to any test").
1384
+ explanation_reason_text_def(no_qualifying_e2e_coverage, "covered_by relationships exist but none qualify as passing scenario-backed E2E coverage").
1385
+ explanation_reason_text_def(structural_unit_contract, "type-shape symbol is satisfied by a unit structural contract").
1386
+ explanation_reason_text_def(executable_test, "Symbol is executable test code linked with executable_for").
1387
+ explanation_reason_text_def(stage_blocked_no_passing_e2e, "Production coverage cannot be evaluated because no passing E2E evidence is available for the current snapshot").
1388
+ explanation_reason_text_def(missing_test_entity, "covered_by target is not a test entity").
1389
+ explanation_reason_text_def(test_not_in_requirement_scenario_chain, "Test is not in this requirement's scenario chain").
1390
+ explanation_reason_text_def(test_scope_is_unit, "Test verification_scope is unit, which cannot cover behavioral production symbols").
1391
+ explanation_reason_text_def(test_scope_is_non_e2e, "Test verification_scope is not end_to_end").
1392
+ explanation_reason_text_def(proof_snapshot_unavailable, "Workspace snapshot is unavailable so receipt currency cannot be proven").
1393
+ explanation_reason_text_def(missing_proof_receipt, "Scenario-backed E2E test has no kibi.proof-receipt.v1").
1394
+ explanation_reason_text_def(receipt_snapshot_mismatch, "Receipts exist but none match the current snapshot or binding").
1395
+ explanation_reason_text_def(stale_proof_receipt, "Current-snapshot receipt is older than the allowed proof age").
1396
+ explanation_reason_text_def(receipt_contract_mismatch, "Receipt does not match the current proof contract hash or fingerprint").
1397
+ explanation_reason_text_def(invalid_proof_receipt, "Receipt evidence is malformed or otherwise uncheckable").
1398
+ explanation_reason_text_def(failed_proof_receipt, "Latest current receipt outcome is not passed").
1399
+
1400
+ test_resolution_record(TestId, _{
1401
+ testId: TestId,
1402
+ requiredProofs: RequiredProofs,
1403
+ executableFor: ExecutableFor
1404
+ }) :-
1405
+ test_required_proof_refs(TestId, RequiredProofs),
1406
+ test_executable_for_refs(TestId, ExecutableFor).
1407
+
1408
+ test_required_proof_refs(TestId, Refs) :-
1409
+ kb_entity(TestId, test, Props),
1410
+ memberchk(proof_contract=RawContract, Props),
1411
+ proof_contract_dict(RawContract, Contract),
1412
+ contract_required_symbol_ids(Contract, SymbolIds),
1413
+ !,
1414
+ maplist(symbol_resolution_ref, SymbolIds, Refs).
1415
+ test_required_proof_refs(_TestId, []).
1416
+
1417
+ contract_required_symbol_ids(Contract, SymbolIds) :-
1418
+ ( get_dict(required_proofs, Contract, Raw)
1419
+ ; get_dict('required_proofs', Contract, Raw)
1420
+ ),
1421
+ !,
1422
+ inventory_entries(Raw, Entries),
1423
+ findall(SymbolId,
1424
+ (member(Entry, Entries), required_proof_entry_symbol(Entry, SymbolId)),
1425
+ SymbolIds0),
1426
+ sort(SymbolIds0, SymbolIds).
1427
+ contract_required_symbol_ids(_, []).
1428
+
1429
+ required_proof_entry_symbol(Entry, SymbolId) :-
1430
+ ( inventory_entry_field(Entry, symbol_id, Raw)
1431
+ ; inventory_entry_field(Entry, symbolId, Raw)
1432
+ ),
1433
+ normalize_atom(Raw, SymbolId),
1434
+ SymbolId \= ''.
1435
+
1436
+ test_executable_for_refs(TestId, Refs) :-
1437
+ findall(SymbolId,
1438
+ (kb_relationship(executable_for, SymbolId, TestId), kb_entity(SymbolId, symbol, _)),
1439
+ SymbolIds0),
1440
+ sort(SymbolIds0, SymbolIds),
1441
+ maplist(symbol_resolution_ref, SymbolIds, Refs).
1442
+
1443
+ symbol_resolution_ref(SymbolId, Ref) :-
1444
+ kb_entity(SymbolId, symbol, _),
1445
+ !,
1446
+ symbol_coordinate_ref(SymbolId, Coord),
1447
+ symbol_classification(SymbolId, Role),
1448
+ put_dict(_{role: Role, exists: true}, Coord, Ref).
1449
+ symbol_resolution_ref(SymbolId, _{
1450
+ id: SymbolId,
1451
+ sourceFile: '',
1452
+ sourceLine: 0,
1453
+ sourceColumn: 0,
1454
+ sourceEndLine: 0,
1455
+ sourceEndColumn: 0,
1456
+ role: unresolved,
1457
+ exists: false
1458
+ }).
1459
+
1130
1460
  source_coordinate_stage(ReqProps, ExecutableSymbols, ProductionSymbols, Stage) :-
1131
1461
  append(ExecutableSymbols, ProductionSymbols, Symbols0),
1132
1462
  sort(Symbols0, Symbols),
@@ -33,6 +33,14 @@ known_rule('strict-req-fact-pairing').
33
33
  known_rule('predicate-verifiability').
34
34
  known_rule('strict-readiness').
35
35
  known_rule('semantic-completeness').
36
+ known_rule('proof-contract-symbols').
37
+ known_rule('entity-id-style').
38
+ known_rule('domain-redundancy').
39
+ known_rule('domain-implication').
40
+ known_rule('subject-key-identity').
41
+ known_rule('subject-key-shape').
42
+ known_rule('ontology-quality').
43
+ known_rule('predicate-schema-conformance').
36
44
 
37
45
  rule_enforcement_class('must-priority-coverage', canonical).
38
46
  rule_enforcement_class('symbol-coverage', canonical).
@@ -53,6 +61,14 @@ rule_enforcement_class('strict-req-fact-pairing', advisory).
53
61
  rule_enforcement_class('predicate-verifiability', advisory).
54
62
  rule_enforcement_class('strict-readiness', migration).
55
63
  rule_enforcement_class('semantic-completeness', migration).
64
+ rule_enforcement_class('proof-contract-symbols', advisory).
65
+ rule_enforcement_class('entity-id-style', advisory).
66
+ rule_enforcement_class('domain-redundancy', advisory).
67
+ rule_enforcement_class('domain-implication', advisory).
68
+ rule_enforcement_class('subject-key-identity', advisory).
69
+ rule_enforcement_class('subject-key-shape', advisory).
70
+ rule_enforcement_class('ontology-quality', advisory).
71
+ rule_enforcement_class('predicate-schema-conformance', advisory).
56
72
 
57
73
  rule_implementation('must-priority-coverage', prolog).
58
74
  rule_implementation('symbol-coverage', prolog).
@@ -73,6 +89,14 @@ rule_implementation('strict-req-fact-pairing', prolog).
73
89
  rule_implementation('predicate-verifiability', prolog).
74
90
  rule_implementation('strict-readiness', prolog).
75
91
  rule_implementation('semantic-completeness', prolog).
92
+ rule_implementation('proof-contract-symbols', prolog).
93
+ rule_implementation('entity-id-style', prolog).
94
+ rule_implementation('domain-redundancy', prolog).
95
+ rule_implementation('domain-implication', prolog).
96
+ rule_implementation('subject-key-identity', prolog).
97
+ rule_implementation('subject-key-shape', prolog).
98
+ rule_implementation('ontology-quality', prolog).
99
+ rule_implementation('predicate-schema-conformance', typescript).
76
100
 
77
101
  rule_predicate('must-priority-coverage', check_must_priority_coverage).
78
102
  rule_predicate('symbol-coverage', check_symbol_coverage).
@@ -91,6 +115,13 @@ rule_predicate('strict-req-fact-pairing', check_strict_req_fact_pairing).
91
115
  rule_predicate('predicate-verifiability', check_predicate_verifiability).
92
116
  rule_predicate('strict-readiness', check_strict_readiness).
93
117
  rule_predicate('semantic-completeness', check_semantic_completeness).
118
+ rule_predicate('proof-contract-symbols', check_proof_contract_symbols).
119
+ rule_predicate('entity-id-style', check_entity_id_style).
120
+ rule_predicate('domain-redundancy', check_domain_redundancy).
121
+ rule_predicate('domain-implication', check_domain_implication).
122
+ rule_predicate('subject-key-identity', check_subject_key_identity).
123
+ rule_predicate('subject-key-shape', check_subject_key_shape).
124
+ rule_predicate('ontology-quality', check_ontology_quality).
94
125
 
95
126
  rule_predicate_arity('must-priority-coverage', 1).
96
127
  rule_predicate_arity('symbol-coverage', 1).
@@ -109,6 +140,13 @@ rule_predicate_arity('strict-req-fact-pairing', 1).
109
140
  rule_predicate_arity('predicate-verifiability', 1).
110
141
  rule_predicate_arity('strict-readiness', 1).
111
142
  rule_predicate_arity('semantic-completeness', 1).
143
+ rule_predicate_arity('proof-contract-symbols', 1).
144
+ rule_predicate_arity('entity-id-style', 1).
145
+ rule_predicate_arity('domain-redundancy', 1).
146
+ rule_predicate_arity('domain-implication', 1).
147
+ rule_predicate_arity('subject-key-identity', 1).
148
+ rule_predicate_arity('subject-key-shape', 1).
149
+ rule_predicate_arity('ontology-quality', 1).
112
150
 
113
151
  rule_description('must-priority-coverage', 'Every must-priority requirement must have a scenario and a test').
114
152
  rule_description('symbol-coverage', 'Production symbols need qualifying coverage via covered_by plus a canonical requirement/scenario test path').
@@ -129,3 +167,11 @@ rule_description('strict-req-fact-pairing', 'Detect requirements with incomplete
129
167
  rule_description('predicate-verifiability', 'Detect requires_predicate links that do not target ground fact_kind: predicate facts').
130
168
  rule_description('strict-readiness', 'Report strict contradiction-readiness levels for requirements that are still prose-only or otherwise not contradiction-ready').
131
169
  rule_description('semantic-completeness', 'Every inventoried assertive proposition is modeled or explicitly classified').
170
+ rule_description('proof-contract-symbols', 'Detect unresolved required_proofs.symbol_id values, type-shape required proofs, and proof_bindings.source_file disagreement with the named symbol sourceFile').
171
+ rule_description('entity-id-style', 'Entity IDs name the governed behavior and match their filename stem; new purely numeric IDs are reported at creation boundaries').
172
+ rule_description('domain-redundancy', 'Two current requirements must not ground the identical logical term or share a ground fact unless linked by supersedes or restates').
173
+ rule_description('domain-implication', 'Informational: one requirement\'s numeric bound strictly implies another requirement\'s bound on the same subject and property').
174
+ rule_description('subject-key-identity', 'Subject keys must name a shared component, not be derived from a requirement ID').
175
+ rule_description('subject-key-shape', 'Subject keys follow dotted component.aspect[.sub] with lowercase snake segments').
176
+ rule_description('ontology-quality', 'Informational: predicate schemas whose argument values mostly occur in only one fact are carrying prose instead of a shared vocabulary').
177
+ rule_description('predicate-schema-conformance', 'Predicate facts match a predicate_schema (project-local, or the built-in catalog in the default namespace) and use its declared argument constants').