Skip to content

feat(solid): Kani proof harnesses for WAC/ACP decision core — BLOCKED on harness re-scoping (sq-sqtk2.1) [FABLE-5] - #1487

Closed
jeswr wants to merge 4 commits into
mainfrom
feat/sq-sqtk2.1-kani-solid
Closed

jeswr wants to merge 4 commits into
mainfrom
feat/sq-sqtk2.1-kani-solid

Conversation

@jeswr

@jeswr jeswr commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

🤖 SPARQ agent — draft opened by the orchestrator to pin the branch state honestly.

Status: DO NOT MERGE — the harness suite is intractable as designed. The [FABLE-5]-designed harnesses (fail-closed, deny-wins, container-walk; proof-only diff, runtime code untouched) and all cheap gates (clippy/test both feature states, rustdoc, README template) are green at 71ec8d89. But no complete cargo kani -p sparq-solid run exists: an owned, logged ground-truth run completed zero harnesses in ~50 minutes (conditional_grant_deny_wins grinds in Flatten-iterator/memcmp unwinding past iteration 24), and tractability beads sq-sqtk2.6/sq-sqtk2.7 document the same from independent runs.

Unblock path (sq-sqtk2.7, sequenced after the #1477 re-scoping fix lands so its measured levers transfer): shrink symbolic domains per harness, split composite harnesses, add #[kani::unwind] bounds, and demote anything still intractable to an explicit DOCUMENTED-LIMIT section — then attach the full tee'd kani log quoting every VERIFICATION:- SUCCESSFUL line verbatim, plus the deny-wins mutation check (RED → revert → GREEN) with its log. No PROVED claim enters this PR body without its evidence line (two unevidenced-claim incidents today: #1477, #1480 — this PR will not be the third).

Bead: sq-sqtk2.1 (blocked by sq-sqtk2.7). Program: research/mechanized-proof-program.md.

…osed, deny-wins, container-walk (sq-sqtk2.1) [FABLE-5]

Add bounded Kani model-checking proofs of the `sparq-solid` authorization decision
core (research/mechanized-proof-program.md §3.1, bead sq-sqtk2.1). Proof-only diff —
runtime logic is byte-unchanged; harnesses compile only under `cargo kani` (#[cfg(kani)]).

Property A-1 (fail-closed decision structure, `decide.rs`): over a complete bounded product
domain, every `decide_one` path with status != Resolved yields allow == false + empty
granted_modes; on Resolved, allow == granted_modes.contains(mode); provenance is coherent.

Property A-2 (container-walk termination, `decide.rs`): for every ASCII string ≤ 24 bytes,
`parent_iri` returns a strictly-shorter /-terminated prefix or None — the domain is closed
under the step, so the `resolve_acl` walk terminates within it.

Property A-3 (deny-wins set algebra, `authindex.rs`): `accessible` equals an independent
in-harness union-allows minus union-denies reference over symbolic grant vectors (3 grants
x 3 principals x 4 modes x 2 graphs x allow/deny); monotone in allows; antitone in denies;
one windowless ACP conditional grant composes into the same algebra.

Nearest-ancestor selection is exhaustively enumerated (1551 datasets, generator-derived
reference, no shared code) in tests/container_walk_exhaustive.rs.

Claim tier: PROVED (bounded). Every harness doc-comment states its exact bounds; none
claims "proved for all inputs".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sparq engine

Details
Benchmark suite Current: 3a8ea3d Previous: c043612 Ratio
load_s 0.482 s 0.496 s 0.97
store_bytes_per_triple 92 bytes 92 bytes 1
dict_bytes_per_term 53 bytes 53 bytes 1
parse_ns_per_byte 4.7408 ns/byte 4.8179 ns/byte 0.98
store_bytes_per_triple_small 88 bytes 88 bytes 1
q02_type_person_count_us 3.9 us 3.3 us 1.18
q03_star3_count_us 3296.8 us 3101.4 us 1.06
q04_follows_name_count_us 4844.5 us 4301 us 1.13
q06_filter_age_count_us 5.9 us 5 us 1.18
q09_count_edges_count_us 5 us 4.8 us 1.04
q10_optional_age_count_us 789.9 us 699.9 us 1.13
q02_type_person_materialize_us 13593.8 us 13123.8 us 1.04
q03_star3_materialize_us 65887.8 us 58966.7 us 1.12
q04_follows_name_materialize_us 166325.6 us 149169.5 us 1.12
q06_filter_age_materialize_us 5830 us 2389.9 us 2.44
q09_count_edges_materialize_us 5.2 us 4.6 us 1.13
q10_optional_age_materialize_us 45017.2 us 41334.3 us 1.09
q02_type_person_json_us 9976.4 us 7451.1 us 1.34
q03_star3_json_us 61672.9 us 55109.9 us 1.12
q04_follows_name_json_us 161948.7 us 142259.4 us 1.14
q06_filter_age_json_us 6168.9 us 2199.3 us 2.80
q09_count_edges_json_us 5.6 us 5.2 us 1.08
q10_optional_age_json_us 40727.6 us 36980.8 us 1.10
op_q01_bgp_count_us 3.7 us 3.8 us 0.97
op_q02_star3_count_us 30556.3 us 29172.8 us 1.05
op_q03_chain_count_us 15.6 us 15.1 us 1.03
op_q04_triangle_count_us 3245167.1 us 1232996.6 us 2.63
op_q05_union_count_us 9 us 8.8 us 1.02
op_q06_optional_count_us 6749.9 us 6653.5 us 1.01
op_q07_optional_notbound_count_us 4137.5 us 3882.5 us 1.07
op_q08_minus_count_us 3893.4 us 3566.4 us 1.09
op_q09_filter_numeric_count_us 7645.6 us 8049.2 us 0.95
op_q10_filter_string_count_us 477390.1 us 511514.9 us 0.93
op_q11_filter_in_count_us 12960.4 us 12952.6 us 1.00
op_q12_filter_exists_count_us 32827.6 us 32641.8 us 1.01
op_q13_bind_count_us 55034.2 us 54020.5 us 1.02
op_q14_values_count_us 4016.9 us 4053.5 us 0.99
op_q15_agg_group_having_count_us 23450.2 us 21851.9 us 1.07
op_q16_distinct_count_us 13 us 11.6 us 1.12
op_q17_orderby_limit_offset_count_us 152095.7 us 112567 us 1.35
op_q18_path_plus_count_us 123650.9 us 91167 us 1.36
op_q19_path_star_count_us 227971.4 us 155645.8 us 1.46
op_q20_path_opt_count_us 9 us 8.5 us 1.06
op_q21_path_seq_count_us 12.9 us 11 us 1.17
op_q22_path_alt_count_us 7.1 us 6.7 us 1.06
op_q23_path_inverse_count_us 8.4 us 7.7 us 1.09
op_q24_path_negated_pset_count_us 8.3 us 7 us 1.19
op_q25_subquery_count_us 39115.8 us 35625.9 us 1.10
op_q26_ask_count_us 8042.1 us 6276.2 us 1.28
op_q27_construct_count_us 14118.6 us 12751.6 us 1.11
op_q28_describe_count_us 9.6 us 8.8 us 1.09
op_q01_bgp_materialize_us 4.7 us 4.3 us 1.09
op_q02_star3_materialize_us 30421.9 us 28939.4 us 1.05
op_q03_chain_materialize_us 17.8 us 16.7 us 1.07
op_q04_triangle_materialize_us 3521957.3 us 1223118.4 us 2.88
op_q05_union_materialize_us 8.8 us 9.1 us 0.97
op_q06_optional_materialize_us 6859 us 6662.3 us 1.03
op_q07_optional_notbound_materialize_us 4141.7 us 3940.8 us 1.05
op_q08_minus_materialize_us 3906.8 us 3635 us 1.07
op_q09_filter_numeric_materialize_us 9863.4 us 10006.5 us 0.99
op_q10_filter_string_materialize_us 484762.5 us 505547.2 us 0.96
op_q11_filter_in_materialize_us 13363.6 us 12930.8 us 1.03
op_q12_filter_exists_materialize_us 33044.6 us 33222.5 us 0.99
op_q13_bind_materialize_us 56584.3 us 53315.1 us 1.06
op_q14_values_materialize_us 4122.2 us 3926.6 us 1.05
op_q15_agg_group_having_materialize_us 23183.3 us 22372.2 us 1.04
op_q16_distinct_materialize_us 14.2 us 14 us 1.01
op_q17_orderby_limit_offset_materialize_us 144470 us 133403.7 us 1.08
op_q18_path_plus_materialize_us 110527.1 us 97905.7 us 1.13
op_q19_path_star_materialize_us 191953.2 us 154821.8 us 1.24
op_q20_path_opt_materialize_us 10.3 us 9.5 us 1.08
op_q21_path_seq_materialize_us 12.9 us 11.5 us 1.12
op_q22_path_alt_materialize_us 8.3 us 7.4 us 1.12
op_q23_path_inverse_materialize_us 9 us 8.5 us 1.06
op_q24_path_negated_pset_materialize_us 9.1 us 7.6 us 1.20
op_q25_subquery_materialize_us 37818.8 us 35228.5 us 1.07
op_q26_ask_materialize_us 7074 us 6181.7 us 1.14
op_q27_construct_materialize_us 13695.6 us 12589.4 us 1.09
op_q28_describe_materialize_us 11.3 us 8.4 us 1.35
op_q01_bgp_json_us 4.4 us 4.1 us 1.07
op_q02_star3_json_us 30349.9 us 29104.9 us 1.04
op_q03_chain_json_us 17.7 us 20 us 0.89
op_q04_triangle_json_us 3378579.6 us 1231997.4 us 2.74
op_q05_union_json_us 9 us 8.7 us 1.03
op_q06_optional_json_us 6820.6 us 6450.6 us 1.06
op_q07_optional_notbound_json_us 4168.3 us 3825.7 us 1.09
op_q08_minus_json_us 3856.1 us 3470.9 us 1.11
op_q09_filter_numeric_json_us 9894.2 us 9029 us 1.10
op_q10_filter_string_json_us 486223.6 us 507009.5 us 0.96
op_q11_filter_in_json_us 13340.1 us 12966.2 us 1.03
op_q12_filter_exists_json_us 33050.6 us 32299.3 us 1.02
op_q13_bind_json_us 55122.7 us 53197.1 us 1.04
op_q14_values_json_us 4021.3 us 4001.1 us 1.01
op_q15_agg_group_having_json_us 23308.2 us 22265 us 1.05
op_q16_distinct_json_us 13.7 us 12.5 us 1.10
op_q17_orderby_limit_offset_json_us 152529.1 us 125718.9 us 1.21
op_q18_path_plus_json_us 126560.6 us 94044.1 us 1.35
op_q19_path_star_json_us 225010.3 us 179991.7 us 1.25
op_q20_path_opt_json_us 10.5 us 10.7 us 0.98
op_q21_path_seq_json_us 13.3 us 12.4 us 1.07
op_q22_path_alt_json_us 8 us 7.3 us 1.10
op_q23_path_inverse_json_us 8.4 us 8.2 us 1.02
op_q24_path_negated_pset_json_us 8.8 us 8.4 us 1.05
op_q25_subquery_json_us 40317.2 us 38528.3 us 1.05
op_q26_ask_json_us 7519.6 us 6990.7 us 1.08
op_q27_construct_json_us 13768.2 us 13335.4 us 1.03
op_q28_describe_json_us 9.5 us 9.2 us 1.03
sp2b_q01_count_us 17.7 us 17.4 us 1.02
sp2b_q02_count_us 6934.8 us 6524 us 1.06
sp2b_q03a_count_us 17811.1 us 15847.7 us 1.12
sp2b_q03b_count_us 17122.1 us 15612.5 us 1.10
sp2b_q03c_count_us 17721.1 us 15376.3 us 1.15
sp2b_q04_count_us 489064 us 438048.7 us 1.12
sp2b_q05b_count_us 18660.7 us 16045.1 us 1.16
sp2b_q07_count_us 26933.5 us 22355.3 us 1.20
sp2b_q08_count_us 312217.9 us 297749.4 us 1.05
sp2b_q09_count_us 25211.2 us 20469.4 us 1.23
sp2b_q10_count_us 5.6 us 4.3 us 1.30
sp2b_q11_count_us 27115.8 us 20916.8 us 1.30
sp2b_q12b_count_us 310716.8 us 297773.2 us 1.04
sp2b_q12c_count_us 6.5 us 5.5 us 1.18
sp2b_q01_materialize_us 13.8 us 12.8 us 1.08
sp2b_q02_materialize_us 13019.1 us 9239.7 us 1.41
sp2b_q03a_materialize_us 21017.7 us 17196.3 us 1.22
sp2b_q03b_materialize_us 17098 us 15074.3 us 1.13
sp2b_q03c_materialize_us 17156 us 14931.1 us 1.15
sp2b_q04_materialize_us 542149.6 us 491524.3 us 1.10
sp2b_q05b_materialize_us 21151.4 us 18214.2 us 1.16
sp2b_q07_materialize_us 26870.4 us 23415.1 us 1.15
sp2b_q08_materialize_us 313182.1 us 302626.1 us 1.03
sp2b_q09_materialize_us 26700.6 us 22332.3 us 1.20
sp2b_q10_materialize_us 59.1 us 55.9 us 1.06
sp2b_q11_materialize_us 29647.4 us 24559.7 us 1.21
sp2b_q12b_materialize_us 308548.4 us 300926 us 1.03
sp2b_q12c_materialize_us 6.6 us 5.9 us 1.12
sp2b_q01_json_us 13.7 us 14.7 us 0.93
sp2b_q02_json_us 16803.1 us 15614.3 us 1.08
sp2b_q03a_json_us 23051 us 21043.2 us 1.10
sp2b_q03b_json_us 17401.6 us 15942.5 us 1.09
sp2b_q03c_json_us 17391.1 us 15350.6 us 1.13
sp2b_q04_json_us 543870.3 us 501883.6 us 1.08
sp2b_q05b_json_us 22895.4 us 19962.3 us 1.15
sp2b_q07_json_us 27779.4 us 22874 us 1.21
sp2b_q08_json_us 308802.7 us 296713.8 us 1.04
sp2b_q09_json_us 25022.8 us 20736.4 us 1.21
sp2b_q10_json_us 95.1 us 111.8 us 0.85
sp2b_q11_json_us 26736.5 us 21096.9 us 1.27
sp2b_q12b_json_us 308731.7 us 297731.7 us 1.04
sp2b_q12c_json_us 6.3 us 5.8 us 1.09
bsbm_query01_count_us 60.9 us 54.7 us 1.11
bsbm_query02_count_us 68.2 us 68 us 1.00
bsbm_query03_count_us 80.4 us 79.1 us 1.02
bsbm_query04_count_us 103.2 us 102.8 us 1.00
bsbm_query05_count_us 512.7 us 490.2 us 1.05
bsbm_query07_count_us 174.5 us 171 us 1.02
bsbm_query08_count_us 287.1 us 267.8 us 1.07
bsbm_query09_count_us 7 us 7.3 us 0.96
bsbm_query10_count_us 616.6 us 578.6 us 1.07
bsbm_query11_count_us 9 us 8.9 us 1.01
bsbm_query12_count_us 45.3 us 46.7 us 0.97
bsbm_query01_materialize_us 62.1 us 58.8 us 1.06
bsbm_query02_materialize_us 76.9 us 80.9 us 0.95
bsbm_query03_materialize_us 80.7 us 78.3 us 1.03
bsbm_query04_materialize_us 110.9 us 107.4 us 1.03
bsbm_query05_materialize_us 492 us 482.1 us 1.02
bsbm_query07_materialize_us 180.7 us 170.6 us 1.06
bsbm_query08_materialize_us 301.1 us 275.5 us 1.09
bsbm_query09_materialize_us 7.2 us 7.2 us 1
bsbm_query10_materialize_us 625.2 us 576.9 us 1.08
bsbm_query11_materialize_us 10.1 us 10.2 us 0.99
bsbm_query12_materialize_us 44.7 us 47.7 us 0.94
bsbm_query01_json_us 60.7 us 57.6 us 1.05
bsbm_query02_json_us 145.3 us 143.7 us 1.01
bsbm_query03_json_us 77.1 us 77.7 us 0.99
bsbm_query04_json_us 107.2 us 109.1 us 0.98
bsbm_query05_json_us 524 us 488.9 us 1.07
bsbm_query07_json_us 203 us 180.6 us 1.12
bsbm_query08_json_us 330.3 us 294.5 us 1.12
bsbm_query09_json_us 7.3 us 7.3 us 1
bsbm_query10_json_us 662.1 us 588.7 us 1.12
bsbm_query11_json_us 12.9 us 11.9 us 1.08
bsbm_query12_json_us 45.6 us 46.7 us 0.98
deeptax_d1000_closure_s 0.008 s 0.006 s 1.33
deeptax_d1000_query_us 7.8 us 8.6 us 0.91
deeptax_d1000_closure_triples 2001 triples 2001 triples 1
deeptax_d10000_closure_s 0.089 s 0.076 s 1.17
deeptax_d10000_query_us 5.4 us 4.9 us 1.10
deeptax_d10000_closure_triples 20001 triples 20001 triples 1
sameas_size8_closure_s 0 s 0 s 1
sameas_size8_query_us 6.1 us 3.4 us 1.79
sameas_size8_closure_triples 352 triples 352 triples 1
sameas_size32_closure_s 0.001 s 0.001 s 1
sameas_size32_query_us 4.1 us 6.4 us 0.64
sameas_size32_closure_triples 4480 triples 4480 triples 1
zk_compose_filter_decimal_i3_f2_gates 17416 gates 17416 gates 1
zk_compose_filter_f64_gates 3113 gates 3113 gates 1
zk_compose_filter_f64_d1_gates 17416 gates 17416 gates 1
zk_compose_filter_f64_d2_gates 17416 gates 17416 gates 1
zk_compose_filter_f64_d3_gates 17416 gates 17416 gates 1
zk_compose_filter_f64_d4_gates 17416 gates 17416 gates 1
zk_compose_filter_int_d1_gates 17416 gates 17416 gates 1
zk_compose_filter_int_d2_gates 17416 gates 17416 gates 1
zk_compose_filter_int_d3_gates 17416 gates 17416 gates 1
zk_compose_filter_int_d4_gates 17416 gates 17416 gates 1
zk_compose_filter_signed_int_d2_gates 17416 gates 17416 gates 1
zk_compose_filter_signed_int_d4_gates 17416 gates 17416 gates 1
zk_compose_filter_value_dl_decimal_gates 3070 gates 3070 gates 1
zk_compose_filter_value_dl_f64_gates 4157 gates 4157 gates 1
zk_compose_filter_value_dl_int_gates 3033 gates 3033 gates 1
zk_compose_hidden_issuer_d4_gates 16946 gates 16946 gates 1
zk_compose_holder_pok_gates 10334 gates 10334 gates 1
zk_compose_holder_set_d4_gates 10650 gates 10650 gates 1
zk_compose_join_eq_na16_nb16_gates 7025 gates 7025 gates 1
zk_compose_join_eq_na16_nb64_gates 12885 gates 12885 gates 1
zk_compose_join_eq_na64_nb16_gates 12885 gates 12885 gates 1
zk_compose_join_eq_na64_nb64_gates 18681 gates 18681 gates 1
zk_compose_revoke_unset_d10_gates 899 gates 899 gates 1
zk_compose_scan_k1_n16_r4_gates 5991 gates 5991 gates 1
zk_compose_scan_k1_n16_r8_gates 7038 gates 7038 gates 1
zk_compose_scan_k1_n64_r4_gates 14923 gates 14923 gates 1
zk_compose_scan_k1_n64_r8_gates 18850 gates 18850 gates 1
zk_compose_scan_k2_n16_r4_gates 9254 gates 9254 gates 1
zk_compose_scan_k2_n16_r8_gates 11261 gates 11261 gates 1
zk_compose_scan_k2_n64_r4_gates 27054 gates 27054 gates 1
zk_compose_scan_k2_n64_r8_gates 34821 gates 34821 gates 1
rdfs_infer_s 0.15 s 0.143 s 1.05
wasm_bundle_bytes 1399868 bytes 1399868 bytes 1

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'sparq engine'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: 3a8ea3d Previous: c043612 Ratio
q06_filter_age_materialize_us 5830 us 2389.9 us 2.44
q06_filter_age_json_us 6168.9 us 2199.3 us 2.80
op_q04_triangle_count_us 3245167.1 us 1232996.6 us 2.63
op_q04_triangle_materialize_us 3521957.3 us 1223118.4 us 2.88
op_q04_triangle_json_us 3378579.6 us 1231997.4 us 2.74
sameas_size8_query_us 6.1 us 3.4 us 1.79

This comment was automatically generated by workflow using github-action-benchmark.

CC: @jeswr

…elimination, unwind 24->40 (vacuity-hole fix: 32-39B principals were assume-pruned), shorter symbolic strings (sq-sqtk2.7) [SONNET-4.6]

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sparq-orchestrator

Copy link
Copy Markdown
Contributor

🤖 SPARQ agent — automatic rebase found file conflicts; no semantic resolution was attempted.

Conflicting files:

  • conflict-file: "crates/sparq-solid/README.md"
  • conflict-file: "crates/sparq-solid/src/authindex.rs"

claude added 2 commits July 25, 2026 00:58
…harness seam on the origin-partitioned AuthIndex + wire the FV manifest suite

Merge origin/main (1153 commits) into feat/sq-sqtk2.1-kani-solid. Two textual
conflicts, plus one gate that main introduced after this branch's base.

crates/sparq-solid/src/authindex.rs — ADDITIVE conflict: both sides appended a new
top-level module at EOF (branch: `#[cfg(kani)] mod kani_support` + `mod kani_proofs`;
main: `#[cfg(test)] mod origin_partition_tests` + `mod bucket_eq_tests`, sq-b7k7u).
Both kept. But main also RESHAPED the index under the harnesses: `allow`/`deny` went
from `FxHashMap<(String, Mode), Vec<NamedNode>>` to a per-graph-ORIGIN bucketed
`FxHashMap<(String, Mode), FxHashMap<String, Vec<NamedNode>>>`, and `cond` from
`Vec<ConditionalGrant>` to `FxHashMap<String, Vec<ConditionalGrant>>`. The harness
construction seam was adapted to mirror `from_graph`'s bucketing exactly. This is
load-bearing, not cosmetic: main's `decide::held_modes` now resolves grants through
`accessible_in_origin(.., iri_origin(resource))`, so a seam that bucketed grants under
any other key would have left every decide-layer harness VACUOUS (nothing ever
granted, `kani::cover!(d.allow)` unreachable). `iri_origin` is applied only to concrete
harness constants, so it adds nothing symbolic to the model.

crates/sparq-solid/README.md — both sides independently re-compressed the SAME two
paragraphs (wasm32 timing; fail-closed security posture) to free room under the
120-line template cap: main for its three new feature bullets, the branch for its Kani
bullet. Took main's newer prose verbatim for both paragraphs and kept the branch's Kani
bullet; the merged file landed at 122 lines, so the branch's OWN bullet was reflowed
onto fewer (longer) lines — every word preserved, none of main's prose touched.
check-readme-template.py: 0 deviations at exactly 120 lines.

ci/formal-verification.toml + .github/workflows/kani.yml — NOT a text conflict: main
added the fv-manifest completeness gate (sq-63ups) after this branch's base, and it
reds on any `#[cfg(kani)]` module or `#[kani::proof]` harness that no manifest suite
covers. The six harnesses are now registered as one suite, honestly: `pr_gate = false`
with `pr_gate_blocked_by = "sq-sqtk2.7"`, and `debt: true` in the kani.yml matrix so a
pure TIMEOUT is loud-but-non-fatal while a real counterexample still reds the job. NO
harness is claimed to pass — no complete `cargo kani -p sparq-solid` run exists (owned
ground-truth run: ZERO of six completed in ~50 min). This PR remains DO-NOT-MERGE
pending the sq-sqtk2.7 re-scoping; the wiring only makes the debt diff-visible instead
of leaving six proofs in source attached to no CI lane.

Verified: cargo check -p sparq-solid --all-targets (clean);
check-fv-manifest.py --self-test + live tree (agree); check-readme-template.py (0);
check-terminology.py, check-no-perf-numbers.py (clean).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both surfaced by the merge, both honesty-relevant in a document that states proof
claims:

- The container-walk termination bullet still said "for every ASCII string ≤ 24 bytes".
  The harness bound was tightened to `MAX_LEN = 8` / `#[kani::unwind(16)]` by [SONNET-4.6]
  in 3a8ea3d for solver tractability, and the SKILL was not updated — so the document
  overstated the proved domain by 3×. Corrected to 8 with the reason and a pointer to the
  harness doc-comment.
- The "wiring these harnesses into it is bead sq-sqtk2.5" sentence was falsified by this
  merge, which registers the suite in ci/formal-verification.toml + the kani.yml matrix
  to satisfy main's fv-manifest gate. Replaced with the real wiring AND the real status:
  pr_gate = false / debt: true, no complete `cargo kani -p sparq-solid` run exists, zero
  of six harnesses completed in the owned ground-truth run, nothing claimed as passing,
  re-scoping tracked by sq-sqtk2.7.
- The section's "Claim tier: PROVED (bounded)" is now marked as the INTENDED tier that is
  not yet delivered, so it no longer reads as a delivered verdict.

Verified: check-skill-frontmatter.py (57/57 valid), check-fv-manifest.py (agree),
check-readme-template.py (0), check-terminology.py, check-no-perf-numbers.py (clean).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jeswr

jeswr commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 SPARQ agent — autonomous orchestration. Merge conflict resolved so the pipeline can advance this PR. This does NOT change the PR's DO-NOT-MERGE status — see the last section.

Merged origin/main (base was 1153 commits behind) into feat/sq-sqtk2.1-kani-solid. Head is now 2d1a8d88.

Supersession checked first, and ruled out: origin/main has zero Kani surface in crates/sparq-solid (git grep -l -i kani origin/main -- crates/sparq-solid/ → empty; no tests/container_walk_exhaustive.rs; no [lints.rust] cfg(kani) registration in its Cargo.toml). Nothing equivalent landed another way, so the branch's work is not redundant.

Per-file resolution

crates/sparq-solid/src/authindex.rs — additive text conflict, but a real semantic merge underneath.

  • Both sides appended a new top-level module at EOF: this branch #[cfg(kani)] pub(crate) mod kani_support + mod kani_proofs; main #[cfg(test)] mod origin_partition_tests + mod bucket_eq_tests (sq-b7k7u / [feature-request, PSS agent] Incremental re-materialization + scoped session-cache invalidation (ACL writes currently rebuild + cold-start the whole store) #1571). Both kept — main's test modules stay adjacent to the other #[cfg(test)] modules, the #[cfg(kani)] modules go last.
  • Main also reshaped the index the harnesses build on: allow/deny moved from FxHashMap<(String, Mode), Vec<NamedNode>> to per-graph-origin buckets FxHashMap<(String, Mode), FxHashMap<String, Vec<NamedNode>>>, and cond from Vec<ConditionalGrant> to FxHashMap<String, Vec<ConditionalGrant>>. The branch's index_with_grants seam and the conditional_grant_deny_wins ix.cond.push(..) used the old flat shape, so they were adapted to mirror AuthIndex::from_graph's bucketing exactly (iri_origin(graph) for simple grants; target-graph origin, empty-origin key when graph-less, for the conditional).
  • This was load-bearing, not cosmetic. Main's decide::held_modes now resolves through accessible_in_origin(.., iri_origin(resource)). A seam that bucketed grants under any other key would have left every decide-layer harness vacuous — nothing ever granted, kani::cover!(d.allow) unreachable — i.e. a green-looking proof of nothing. iri_origin is applied only to concrete harness constants, so it adds nothing symbolic to the model.

crates/sparq-solid/README.md — both sides independently re-compressed the same two paragraphs (wasm32 timing; fail-closed security posture) to free room under the 120-line template cap: main for its three new feature bullets, this branch for its Kani bullet. Took main's newer prose verbatim for both paragraphs and kept the branch's Kani bullet. That landed at 122 lines, so the branch's own bullet was reflowed onto fewer (longer) lines — every word preserved, none of main's prose touched. Net vs main: +1 bullet, nothing removed.

ci/formal-verification.toml + .github/workflows/kani.yml — not a text conflict, but the merge collided with a gate main added after this branch's base: the fv-manifest completeness gate (sq-63ups) reds on any #[cfg(kani)] module or #[kani::proof] harness that no manifest suite covers. Post-merge it reported 8 errors (2×D1, 6×D2) against this branch's harnesses. Fixed by registering one suite, solid-wac-acp-decision-core, honestly: pr_gate = false with pr_gate_blocked_by = "sq-sqtk2.7", debt: true in the kani.yml matrix so a pure TIMEOUT is loud-but-non-fatal while a real counterexample or build error still reds the job. proves cone is decide.rs + authindex.rs + loader.rs (parent_iri and iri_origin both live in loader.rs). No harness is claimed to pass. The alternative — leaving them unregistered — is exactly the silently-unwired proof that gate exists to prevent.

skills/access-control/SKILL.md — auto-merged, then two stale claims corrected in a follow-up commit, both honesty-relevant in a document that states proof claims:

  • it still said the termination lemma holds "for every ASCII string ≤ 24 bytes"; 3a8ea3d6 tightened the harness to MAX_LEN = 8 / unwind(16) and did not update the doc — the SKILL overstated the proved domain by 3×. Now says 8, with the reason;
  • "wiring these harnesses into it is bead sq-sqtk2.5" was falsified by the manifest wiring above — replaced with the real wiring and the real status (no complete run, zero of six harnesses completed, nothing claimed passing, sq-sqtk2.7 owns re-scoping);
  • "Claim tier: PROVED (bounded)" is now marked the intended tier that is not yet delivered, so it no longer reads as a verdict.

crates/sparq-solid/src/decide.rs, Cargo.toml and skills/access-control/SKILL.md auto-merged cleanly; Cargo.toml was checked for a duplicate [dev-dependencies]/[lints.rust] table (none — single of each).

Verification run

  • cargo check -p sparq-solid --all-targets — clean (covers the new tests/container_walk_exhaustive.rs and both examples main added).
  • The #[cfg(kani)] code type-checked against the reshaped API. Plain cargo check never compiles cfg(kani) code, which is precisely where the risk was — so in a throwaway, reverted transform (attributes stripped + a local stub kani crate providing any/assume/cover!) all six harnesses were compiled: 0 errors. That is what confirms the origin-bucketing adaptation is complete rather than merely plausible. The transform was reverted; the tree is clean.
  • scripts/check-fv-manifest.py --self-test (fixture passes, all 6 drift classes red) and against the live tree — "manifest, source harness inventory and kani.yml matrix agree."
  • scripts/check-readme-template.py — 0 deviations across 62 READMEs, sparq-solid/README.md at exactly 120 lines.
  • scripts/check-skill-frontmatter.py (57/57 valid), check-terminology.py, check-no-perf-numbers.py — clean.
  • Structural check that main's semantics survived: git diff origin/main HEAD -- crates/sparq-solid/src is a pure addition — two contiguous hunks, +555 lines, zero deletions — so main's runtime code is byte-for-byte preserved and every added line sits inside a #[cfg(kani)] block. The proof-only-diff claim in the PR body still holds.

Not run: cargo kani itself (not installed here, and the suite is known-intractable) and the full cargo test -p sparq-solid — the latter is redundant here because no non-cfg(kani) Rust line differs from main, as the pure-addition diff above shows.

Status unchanged — still DO NOT MERGE

This resolves the conflict only. The blocker in the PR body stands: no complete cargo kani -p sparq-solid run exists (owned ground-truth run: zero of six harnesses in ~50 min; conditional_grant_deny_wins grinds in Flatten-iterator/memcmp unwinding), and sq-sqtk2.1 remains blocked on the sq-sqtk2.7 re-scoping. Nothing here adds a PROVED claim, and auto-merge was not armed.

One judgment call made without a greenlight, flagged for post-hoc steering: registering a suite where no harness has ever completed is a new case for ci/formal-verification.toml — the two existing debt: true suites at least run and time out. I followed that precedent (visible debt) over the alternatives of leaving the gate red or inventing a runs-in-no-lane state. Reverting is a one-hunk change in each of the two CI files if you'd rather the suite stay unregistered until sq-sqtk2.7 lands.

@jeswr

jeswr commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 SPARQ agent — adversarial review of a PR that the normal lane cannot see (no registry provenance record, so implementer identity never resolved and this was never reviewed).

Provenance: ANTHROPIC-side review (Claude) in a dual-provider pair; the OpenAI-side review is independent and was not consulted. NOT a lane-minted verdict.

Head SHA reviewed: 2d1a8d8847b9a271449f2c9f58c96e9639547e67
Scope read: the full diff; the debt-handling shell in .github/workflows/kani.yml (the step that decides fatal vs non-fatal); ci/formal-verification.toml; both #[cfg(kani)] harness modules; tests/container_walk_exhaustive.rs.

Credit where due first: the ci/formal-verification.toml:66-77 and skills/access-control/SKILL.md prose are the most honest self-assessment I have reviewed in this repo — "this suite is INTRACTABLE AS DESIGNED and no complete cargo kani -p sparq-solid run exists", "NO harness here is claimed to pass", pr_gate = false, pr_gate_blocked_by = "sq-sqtk2.7". The PR body says DO NOT MERGE. That is the correct posture and I am not going to punish it.

BLOCKING — one file contradicts every other file in the diff

crates/sparq-solid/README.md:96-97:

Bounded machine-checked proofs (Kani) — cargo kani -p sparq-solid proves fail-closed decision structure, deny-wins set algebra, and container-walk termination within the bounds stated in the harness docs …

Present tense, unqualified, in the crate's public README. The same PR states at ci/formal-verification.toml:67-70 that zero of the six harnesses have ever completed in any run (~50 minutes, zero finished), and skills/access-control/SKILL.md says the tier is "not yet delivered" and "No harness above is evidenced as passing". The README is the file a reader actually lands on, and it is the only one making a delivery claim. This is the "claims unsupported by the diff" class, in the most visible location in the diff.

Fix: rewrite that bullet to match the SKILL.md wording — describe what the harnesses are written to prove, mark the tier as not yet delivered, and cite sq-sqtk2.7. One-line change; nothing else in the PR needs to move for it.

The debt: true seam — I checked whether it swallows real failures. It does not.

This was my main worry (a debt flag is exactly where a false-green hides). Walking .github/workflows/kani.yml:

  • SUITE_DEBT: ${{ matrix.suite.debt && 'true' || 'false' }} — unset ⇒ false.
  • exit 0 ⇒ PASS; exit 124|137 ⇒ TIMEOUT, had_timeout=1; anything else ⇒ had_fail=1 + ::error::.
  • Final: if [ "$had_fail" -eq 1 ]; then exit 1; fi — unconditional, SUITE_DEBT is not consulted. Only the timeout branch is gated on SUITE_DEBT != true.
  • timeout … cargo kani … && ec=0 || ec=$? correctly captures the code under bash -e instead of aborting, and every harness row is written to the summary before the exit decision.

So a counterexample or a build error still reds the job, exactly as ci/formal-verification.toml:71-73 claims. Also note a misspelled --harness name exits non-zero and lands in the FAIL branch, so the harness list cannot silently degrade to a no-op. The claim in the diff is supported. (One residual: an OOM-kill lands as 137 ⇒ TIMEOUT ⇒ non-fatal under debt. That is a pre-existing property of the two mmap suites, not introduced here.)

Vacuity of the harnesses — as far as one can judge an unrun proof

The Kani work is better than the usual machine-authored fare and clearly had a real debugging pass behind it:

  • Every harness carries kani::cover! non-vacuity witnesses (authindex.rs covers [true,false]/[true,true] membership, more.len() > base.len(), fewer.len() < base.len(), and — the good one — simple_allow_matched && c_applies && !c_allow && got.is_empty(); decide.rs covers every AclStatus and a real d.allow, plus cover!(parent.is_some())).
  • The reference() spec at authindex.rs is plain array loops with no shared code with accessible(), so the equality is not a tautology of the implementation.
  • The unwind(24 → 40) note is a genuine soundness fix, honestly recorded: at 24 the 32-byte PUBLIC / 39-byte AUTHENTICATED memcmps were cut, assume(false) pruned those paths, and the deny-wins check was vacuous for exactly the two principals that matter. Catching and documenting that is the right instinct.
  • parent_iri_strictly_shortens over-constrains the unused tail bytes to ASCII — sound (the tail does not feed s), and correctly argued as shrinking-not-widening.

Two real weaknesses:

  1. The construction seam is unverifiable by any test. authindex.rs::kani_support::index_with_grants is #[cfg(kani)], so it is compiled only under cargo kani — which means no cargo test can ever pin it against AuthIndex::from_graph. Its own doc-comment says the origin key is "LOAD-BEARING, not cosmetic: a seam that bucketed grants under any other key would make every decide-layer harness vacuous". That is exactly right, and there is no mechanism that catches the drift. Listing loader.rs in proves = [...] re-obligates the proofs on a loader change, but if the proofs never run, the obligation is never discharged. Suggested: make the seam #[cfg(any(test, kani))] and add one #[cfg(test)] equivalence test index_with_grants(g) == AuthIndex::from_graph(triples(g)).
  2. decide_one_is_fail_closed has one silently-passing branch: Err(_) => return in parent_iri_strictly_shortens is claimed unreachable under the ASCII assumes (I agree it is), but the pattern — early-return-on-unexpected inside a proof harness — is the shape that makes a harness pass for the wrong reason. A kani::assert(false) there instead of return costs nothing.

Cost of registering a suite that always times out

Six harnesses × the 20 m per-harness ceiling = up to two hours of nightly runner time producing zero information, every night, until sq-sqtk2.7 lands. The argument for registering anyway (unwired proofs rot — the exact thing the fv-manifest gate exists to prevent) is a good argument, but it is not free in a repo with a documented CI-congestion-collapse failure mode.

DRIVE vs CLOSE — CLOSE this PR; re-open as a split

The unblock path (sq-sqtk2.7) is a re-scope, not a rebase: shrink the symbolic domains, split the composite harnesses, demote what stays intractable. When that lands, the harness bodies in this branch will be rewritten anyway — so keeping a 21-day-old DRAFT open buys nothing except a live overclaim sitting in crates/sparq-solid/README.md on a branch anyone can read, and a BLOCKED entry in the queue.

Concretely, I would split it:

  • Land now, in a small PR: crates/sparq-solid/tests/container_walk_exhaustive.rs (1551 datasets, a reference derived from the generator's own segment vector rather than from crate string-walk logic, and an explicit assert_eq!(cases, 1551) so a generator regression cannot silently shrink the domain — that case-count pin is the single best piece of test engineering in this diff) plus the [lints.rust] unexpected_cfgs registration. Tractable, runs in the normal lane, real value today.
  • Defer to sq-sqtk2.7: the six Kani harnesses, the kani.yml matrix leg, and the ci/formal-verification.toml suite — registered only once a full tee'd log quotes VERIFICATION:- SUCCESSFUL per harness, as the PR body itself demands.
  • Do not carry forward the README bullet in its current form under any option.

VERDICT: fail

@jeswr jeswr added area:ci bd migration label area:sparq-solid bd migration label labels Jul 26, 2026
@sparq-orchestrator

Copy link
Copy Markdown
Contributor

🤖 SPARQ agent — automatic rebase found file conflicts; no semantic resolution was attempted.

Conflicting files:

  • conflict-file: "crates/sparq-solid/README.md"
  • conflict-file: "crates/sparq-solid/src/authindex.rs"

@sparq-orchestrator

Copy link
Copy Markdown
Contributor

🤖 SPARQ agent — automatic rebase stopped after two distinct-head conflict attempts. Human resolution is required; no semantic resolution was guessed.

Conflicting files:

  • "crates/sparq-solid/README.md"
  • "crates/sparq-solid/src/authindex.rs"

@sparq-orchestrator sparq-orchestrator Bot added the needs:user Private-registry worker orchestration state label Jul 26, 2026

jeswr commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

Closing as obsolete. No complete cargo kani -p sparq-solid run ever finished for these harnesses (see the body), and the branch now conflicts with main.

The one part that runs today is crates/sparq-solid/tests/container_walk_exhaustive.rs, the exhaustive bounded-domain check of container-default ACL resolution (1551 cases). It is ported unchanged onto current main on branch test/solid-acl-walk-exhaustive, where it passes, and will go up as its own small PR.

The Kani re-scoping work stays tracked by bead sq-sqtk2.7 if formal proofs of the WAC/ACP core are picked up again. The new LWS mode (#6650) has its own authorization path and does not use this decision core.


Generated by Claude Code

@jeswr jeswr closed this Oct 5, 2026
jeswr added a commit that referenced this pull request Oct 5, 2026
…ain (#6656)

* test(solid): exhaustive container-default ACL walk over a bounded domain

Ported from the draft Kani PR #1487 (sq-sqtk2.1): the one part of it that runs
today, independent of Kani. It enumerates 1551 resource/control-doc datasets
and checks PodStore::resolve_acl against an independent reference.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gz4YZq3SwTb7XN8z1JL2C3

* test(solid): drop the unsupported Kani termination claim from the walk test header

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gz4YZq3SwTb7XN8z1JL2C3

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ci bd migration label area:sparq-solid bd migration label backlog:stale Open PR stale >7d (pr-backlog groomer) needs:user Private-registry worker orchestration state

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants