Skip to content

Commit 14eb9b1

Browse files
BayyinahEnterpriseClaude (cowork)
andcommitted
release: v0.11.3 (F22 corrective -- dispatch whitelist alignment)
Closes Round 29 audit F22 MEDIUM. The gate11-rust-smoke-test CI job has been red since v0.11.0 (PR #20) because `verification.step2_3_check_version_and_language` strictly rejected language != 'python' even though Manifest.from_dict accepted ('python', 'rust') from v0.11.0 onwards. The dispatch surface contradicted the documented schema surface; the structural-honesty thesis applied recursively to the project itself. v0.11.3 aligns the dispatch whitelist with the schema whitelist. After the merge commit, the gate11-rust-smoke-test CI job goes green for the first time since v0.11.0 -- the empirical proof of F22 closure. Single-purpose hotfix release. Out of scope: - Go verifier (Phase G11.2 al-Mursalat / v0.12.0) - ONNX verifier (Phase G11.3 an-Naziat / v0.13.0) - Cross-substrate verification corpus (Phase G11.4 Tasdiq al-Bayan / v0.14.0) - v1.0 self-attestation Until those phases ship, manifests with language='go' or language='onnx' fail-closed at step 2_3 with a clear CASM-V-001 error naming the future phase that will add support. Local verification (sandbox): - pytest --collect-only -q -> 596 tests collected (target: 596, +7 from v0.11.2's 589: tests/test_gate11_dispatch_f22_corrective.py) - pytest -q -> 550 passed, 46 skipped - ruff check + ruff format --check + mypy clean - furqan-lint version -> 'furqan-lint 0.11.3' - Em-dash check (extended scope) clean - release_sweep.py clean (README pins synchronized to v0.11.3) Co-authored-by: Claude (cowork) <claude+cowork@anthropic.com>
1 parent 474aee4 commit 14eb9b1

7 files changed

Lines changed: 261 additions & 18 deletions

File tree

CHANGELOG.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,73 @@ introduced this convention.
1919

2020
---
2121

22+
## [0.11.3] - 2026-05-09
23+
24+
### Hotfix corrective
25+
26+
Closes Round 29 audit F22 (MEDIUM): the `gate11-rust-smoke-test`
27+
CI job has been red since v0.11.0 (PR #20) because
28+
`verification.step2_3_check_version_and_language` strictly
29+
rejected `language != "python"` even though
30+
`Manifest.from_dict` accepted both `("python", "rust")` from
31+
v0.11.0 onwards. The dispatch surface contradicted the
32+
documented schema surface.
33+
34+
The root cause was the structural-honesty thesis applied to
35+
the project itself: the documented surface (schema accepts
36+
rust) did not match the substrate behavior (dispatch rejects
37+
rust). v0.11.0 shipped the rust schema acceptance but not the
38+
matching dispatch acceptance.
39+
40+
#### Closure
41+
42+
- F22 MEDIUM: `verification.step2_3_check_version_and_language`
43+
now accepts `language in ("python", "rust")`, matching the
44+
schema validator. After this corrective the
45+
`gate11-rust-smoke-test` CI job goes green for the first
46+
time since v0.11.0; this is the empirical proof of F22
47+
closure.
48+
49+
#### Scope deliberately tight
50+
51+
This is a single-purpose patch release. Out of scope:
52+
53+
- Go verifier (Phase G11.2 al-Mursalat); accepting `language="go"`
54+
at the dispatch site is deferred until the Go verifier
55+
itself ships in v0.12.0. Until then a Go manifest
56+
fails-closed at step 2_3 with a clear `CASM-V-001` error
57+
naming the future phase.
58+
- ONNX verifier (Phase G11.3 an-Naziat); same structure.
59+
- Cross-substrate verification corpus (Phase G11.4 Tasdiq
60+
al-Bayan); requires Phase G11.2 + G11.3 to ship first.
61+
- v1.0 self-attestation; requires the canonical chain through
62+
G11.4 to complete.
63+
64+
#### Tests
65+
66+
Test count: 589 (v0.11.2 ship state) -> 596 (v0.11.3).
67+
Net delta: +7 (in `tests/test_gate11_dispatch_f22_corrective.py`):
68+
two regression-guard tests for the schema's existing
69+
python/rust acceptance plus a go-rejection pin; the F22
70+
closure pin (rust manifest accepted at step 2_3); a python
71+
acceptance pin; an unknown-language fail-closed pin; a
72+
go-fail-closed pin.
73+
74+
#### What this release does NOT do
75+
76+
- Does NOT modify the schema validator (`Manifest.from_dict`
77+
whitelist unchanged from v0.11.2)
78+
- Does NOT add new CASM-V error codes
79+
- Does NOT change checker semantics
80+
- Does NOT advance Phase G11.x roadmap; it closes the audit
81+
register entry for F22 and nothing more
82+
- Does NOT extend the README or SECURITY.md beyond what
83+
v0.11.2 already documented (the documented surface was
84+
always cross-language; this release fixes the substrate to
85+
match)
86+
87+
---
88+
2289
## [0.11.2] - 2026-05-09
2390

2491
### Substrate corrective (at-Tawbah / G11.0.1)

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,20 @@ pip install "furqan-lint[rust,go,onnx-runtime,onnx-profile,gate11]" # all adapt
4141
### Install from a specific commit or tag
4242

4343
```bash
44-
pip install "git+https://github.com/BayyinahEnterprise/furqan-lint.git@v0.11.2"
44+
pip install "git+https://github.com/BayyinahEnterprise/furqan-lint.git@v0.11.3"
4545
```
4646

47-
Replace `v0.11.2` with any tag from the [release history](https://github.com/BayyinahEnterprise/furqan-lint/releases) or `main` for the development tip.
47+
Replace `v0.11.3` with any tag from the [release history](https://github.com/BayyinahEnterprise/furqan-lint/releases) or `main` for the development tip.
4848

4949
### Furqan dependency
5050

51-
furqan-lint requires `furqan>=0.11.0`, the Furqan programming-language tooling. As of 2026-05-03 the PyPI release of `furqan` is at v0.10.1; install v0.11.2 directly from GitHub:
51+
furqan-lint requires `furqan>=0.11.0`, the Furqan programming-language tooling. As of 2026-05-03 the PyPI release of `furqan` is at v0.10.1; install v0.11.3 directly from GitHub:
5252

5353
```bash
54-
pip install "git+https://github.com/BayyinahEnterprise/furqan-programming-language.git@v0.11.2"
54+
pip install "git+https://github.com/BayyinahEnterprise/furqan-programming-language.git@v0.11.3"
5555
```
5656

57-
This GitHub-pin step will not be necessary once `furqan` v0.11.2 is published to PyPI.
57+
This GitHub-pin step will not be necessary once `furqan` v0.11.3 is published to PyPI.
5858

5959
### Rust support (opt-in)
6060

@@ -511,7 +511,7 @@ jobs:
511511
runs-on: ubuntu-latest
512512
steps:
513513
- uses: actions/checkout@v4
514-
- uses: BayyinahEnterprise/furqan-lint@v0.11.2
514+
- uses: BayyinahEnterprise/furqan-lint@v0.11.3
515515
with:
516516
path: src/
517517
```
@@ -531,7 +531,7 @@ Python files:
531531
# .pre-commit-config.yaml
532532
repos:
533533
- repo: https://github.com/BayyinahEnterprise/furqan-lint
534-
rev: v0.11.2
534+
rev: v0.11.3
535535
hooks:
536536
- id: furqan-lint
537537
```
@@ -566,7 +566,7 @@ repos:
566566
- id: mypy
567567
568568
- repo: https://github.com/BayyinahEnterprise/furqan-lint
569-
rev: v0.11.2
569+
rev: v0.11.3
570570
hooks:
571571
- id: furqan-lint
572572
```
@@ -640,7 +640,7 @@ MARAD example.py
640640
Per-version closure ledgers are in
641641
[CHANGELOG.md](CHANGELOG.md). This README previously
642642
mirrored closures from v0.2.0 through v0.11.0; that mirror
643-
was retired in v0.11.2 (Phase G10.5 al-Mubin) because
643+
was retired in v0.11.3 (Phase G10.5 al-Mubin) because
644644
CHANGELOG.md is the canonical closure ledger and the
645645
README mirror was repeatedly drifting out of sync. The
646646
framework section 10.2 retirement procedure was followed:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "furqan-lint"
7-
version = "0.11.2"
7+
version = "0.11.3"
88
description = "Structural-honesty checks for Python, powered by Furqan"
99
readme = "README.md"
1010
requires-python = ">=3.10"

src/furqan_lint/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""furqan-lint: structural-honesty checks for Python."""
22

3-
__version__ = "0.11.2"
3+
__version__ = "0.11.3"
44

55
# Explicit public surface declaration. The implicit surface (anything
66
# not starting with an underscore at module level) is fragile: any

src/furqan_lint/gate11/verification.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,27 @@ def step2_3_check_version_and_language(self, manifest: Manifest) -> None:
139139
f"unsupported casm_version {manifest.casm_version!r}",
140140
)
141141
language = manifest.module_identity.get("language")
142-
if language != "python":
142+
# Phase G11.0.2 (v0.11.3) F22 corrective: align this
143+
# dispatch whitelist with Manifest.from_dict's whitelist.
144+
# Pre-v0.11.3 the schema accepted ('python', 'rust') but
145+
# the verifier dispatch site rejected anything other than
146+
# 'python', so a rust manifest reaching this step raised
147+
# CASM-V-001 -- the dispatch surface contradicted the
148+
# documented schema surface. The gate11-rust-smoke-test
149+
# CI job had been red since v0.11.0 (PR #20) for this
150+
# reason. v0.11.3 closes the gap by accepting rust here
151+
# too. Future Go (Phase G11.2) and ONNX (Phase G11.3)
152+
# additions will extend this whitelist when their
153+
# verifiers ship; until then, manifests with those
154+
# languages still fail-closed at this step with a clear
155+
# CASM-V-001 error rather than silently passing into a
156+
# missing verifier.
157+
if language not in ("python", "rust"):
143158
raise CasmVerificationError(
144159
"CASM-V-001",
145-
f"v1.0 supports only language='python'; got {language!r}",
160+
f"v1.0 supports language in (python, rust); "
161+
f"got {language!r}. Go support ships in Phase "
162+
f"G11.2; ONNX in Phase G11.3.",
146163
)
147164

148165
# Step 4
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
"""Phase G11.0.2 (v0.11.3) F22 corrective pinning tests.
2+
3+
Pre-v0.11.3 the verifier's dispatch site
4+
(``verification.step2_3_check_version_and_language``) rejected
5+
any manifest whose ``language`` was not exactly ``"python"``,
6+
even though ``Manifest.from_dict`` accepted both
7+
``("python", "rust")`` from v0.11.0 onwards. The result was
8+
that the ``gate11-rust-smoke-test`` CI job had been red since
9+
PR #20 (v0.11.0): a rust manifest passed the schema validator
10+
but was rejected at the dispatch site with CASM-V-001. The
11+
dispatch surface contradicted the documented schema surface.
12+
13+
v0.11.3 aligns the dispatch whitelist with the schema
14+
whitelist. After this corrective, the rust smoke test goes
15+
green for the first time since v0.11.0.
16+
17+
These tests pin the corrective. They are intentionally
18+
narrow: only python and rust are exercised, because those are
19+
the two language substrates that have shipped verifiers as of
20+
v0.11.3. Go (Phase G11.2) and ONNX (Phase G11.3) are out of
21+
scope for this patch.
22+
"""
23+
24+
# ruff: noqa: E402
25+
26+
from __future__ import annotations
27+
28+
import pytest
29+
30+
pytest.importorskip("rfc8785")
31+
32+
from furqan_lint.gate11.manifest_schema import (
33+
CasmSchemaError,
34+
Manifest,
35+
)
36+
from furqan_lint.gate11.verification import (
37+
CasmVerificationError,
38+
TrustConfig,
39+
Verifier,
40+
)
41+
42+
43+
def _baseline_manifest_dict(language: str) -> dict:
44+
"""Build a minimal Manifest dict in the requested language."""
45+
return {
46+
"casm_version": "1.0",
47+
"module_identity": {
48+
"language": language,
49+
"module_path": "x",
50+
"module_root_hash": "sha256:" + "0" * 64,
51+
},
52+
"public_surface": {
53+
"names": [],
54+
"extraction_method": (f"placeholder.{language}-public-surface@v1.0"),
55+
"extraction_substrate": "test",
56+
},
57+
"chain": {
58+
"previous_manifest_hash": None,
59+
"chain_position": 1,
60+
},
61+
"linter_substrate_attestation": {
62+
"linter_name": "furqan-lint",
63+
"linter_version": "0.11.3",
64+
"checker_set_hash": "sha256:" + "1" * 64,
65+
},
66+
"trust_root": {"trust_root_id": "public-sigstore"},
67+
"issued_at": "2026-05-09T00:00:00Z",
68+
}
69+
70+
71+
# ---------------------------------------------------------------
72+
# Schema-side: Manifest.from_dict accepts python and rust
73+
# (this was already true from v0.11.0; pin it as regression guard)
74+
# ---------------------------------------------------------------
75+
76+
77+
def test_schema_accepts_language_python() -> None:
78+
Manifest.from_dict(_baseline_manifest_dict("python"))
79+
80+
81+
def test_schema_accepts_language_rust() -> None:
82+
Manifest.from_dict(_baseline_manifest_dict("rust"))
83+
84+
85+
def test_schema_rejects_language_go() -> None:
86+
"""Go support ships in Phase G11.2; until then schema rejects it.
87+
88+
This pin is removed when Phase G11.2 ships (v0.12.0+).
89+
"""
90+
bad = _baseline_manifest_dict("go")
91+
with pytest.raises(CasmSchemaError) as exc:
92+
Manifest.from_dict(bad)
93+
assert exc.value.code == "CASM-V-001"
94+
95+
96+
# ---------------------------------------------------------------
97+
# Dispatch-site: step2_3 accepts python and rust (THE F22 FIX)
98+
# ---------------------------------------------------------------
99+
100+
101+
def test_step2_3_accepts_python_manifest() -> None:
102+
verifier = Verifier(trust_config=TrustConfig())
103+
manifest = Manifest.from_dict(_baseline_manifest_dict("python"))
104+
# No exception raised -> dispatch accepts.
105+
verifier.step2_3_check_version_and_language(manifest)
106+
107+
108+
def test_f22_step2_3_accepts_rust_manifest() -> None:
109+
"""F22 CLOSURE: rust manifest no longer rejected at step 2_3.
110+
111+
Pre-v0.11.3 this raised CasmVerificationError(CASM-V-001,
112+
'v1.0 supports only language=python; got rust'), causing
113+
gate11-rust-smoke-test CI to be red since v0.11.0. v0.11.3
114+
aligns the dispatch whitelist with the schema whitelist
115+
(both accept python/rust).
116+
"""
117+
verifier = Verifier(trust_config=TrustConfig())
118+
manifest = Manifest.from_dict(_baseline_manifest_dict("rust"))
119+
verifier.step2_3_check_version_and_language(manifest)
120+
121+
122+
def test_step2_3_rejects_unknown_language_with_supported_list() -> None:
123+
"""Unsupported languages still fail-closed at the dispatch site
124+
with CasmVerificationError(CASM-V-001) and the supported list
125+
enumerated in the error message. Future Phase G11.2 (Go) and
126+
G11.3 (ONNX) will extend the whitelist when their verifiers
127+
ship.
128+
"""
129+
# We bypass schema (which would reject 'haskell') and forge
130+
# the language field directly to exercise the verifier-side
131+
# defense.
132+
valid = Manifest.from_dict(_baseline_manifest_dict("python"))
133+
valid.module_identity["language"] = "haskell"
134+
135+
verifier = Verifier(trust_config=TrustConfig())
136+
with pytest.raises(CasmVerificationError) as exc:
137+
verifier.step2_3_check_version_and_language(valid)
138+
assert exc.value.code == "CASM-V-001"
139+
# The error message must enumerate the supported list so an
140+
# operator hitting this knows how to adjust.
141+
msg = str(exc.value).lower()
142+
assert "python" in msg
143+
assert "rust" in msg
144+
145+
146+
def test_step2_3_rejects_go_pre_g11_2() -> None:
147+
"""Go is not yet supported (Phase G11.2 / v0.12.0). The
148+
dispatch site fails-closed with CASM-V-001 + a clear error
149+
message naming the future phase.
150+
"""
151+
valid = Manifest.from_dict(_baseline_manifest_dict("python"))
152+
valid.module_identity["language"] = "go"
153+
154+
verifier = Verifier(trust_config=TrustConfig())
155+
with pytest.raises(CasmVerificationError) as exc:
156+
verifier.step2_3_check_version_and_language(valid)
157+
assert exc.value.code == "CASM-V-001"
158+
# Error message names the phase that will add Go.
159+
assert "G11.2" in str(exc.value)

tests/test_release_sweep_extension.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def test_positive_current_repo_sweeps_clean(tmp_path: Path) -> None:
3535
and all README pins refreshed must sweep clean.
3636
"""
3737
findings = sweep(REPO_ROOT)
38-
# The repo is at v0.11.2 with the T04a sweep applied; no
38+
# The repo is at v0.11.3 with the T04a sweep applied; no
3939
# stale pins should remain.
4040
assert findings == [], f"current repo expected to sweep clean but got: {findings}"
4141

@@ -45,7 +45,7 @@ def test_negative_stale_install_pin_fires(tmp_path: Path) -> None:
4545
readme = repo / "README.md"
4646
text = readme.read_text(encoding="utf-8")
4747
text = text.replace(
48-
'pip install "git+https://github.com/BayyinahEnterprise/' 'furqan-lint.git@v0.11.2"',
48+
'pip install "git+https://github.com/BayyinahEnterprise/' 'furqan-lint.git@v0.11.3"',
4949
'pip install "git+https://github.com/BayyinahEnterprise/' 'furqan-lint.git@v0.4.0"',
5050
)
5151
readme.write_text(text, encoding="utf-8")
@@ -58,7 +58,7 @@ def test_negative_stale_github_action_fires(tmp_path: Path) -> None:
5858
readme = repo / "README.md"
5959
text = readme.read_text(encoding="utf-8")
6060
text = text.replace(
61-
"uses: BayyinahEnterprise/furqan-lint@v0.11.2",
61+
"uses: BayyinahEnterprise/furqan-lint@v0.11.3",
6262
"uses: BayyinahEnterprise/furqan-lint@v0.4.0",
6363
)
6464
readme.write_text(text, encoding="utf-8")
@@ -70,10 +70,10 @@ def test_negative_stale_precommit_rev_fires(tmp_path: Path) -> None:
7070
repo = _build_repo_clone(tmp_path)
7171
readme = repo / "README.md"
7272
text = readme.read_text(encoding="utf-8")
73-
# First refresh case: substitute one of the v0.11.2 rev examples
73+
# First refresh case: substitute one of the v0.11.3 rev examples
7474
# back to a stale v0.5.0 to exercise the regex.
7575
text = text.replace(
76-
" rev: v0.11.2\n hooks:\n - id: furqan-lint",
76+
" rev: v0.11.3\n hooks:\n - id: furqan-lint",
7777
" rev: v0.5.0\n hooks:\n - id: furqan-lint",
7878
1,
7979
)

0 commit comments

Comments
 (0)