Structural-honesty checks for Python, powered by Furqan
Project description
furqan-lint
Structural-honesty checks for Python, powered by Furqan.
furqan-lint translates Python source into the Furqan AST and runs the
subset of Furqan checkers whose semantics carry across language boundaries
into idiomatic Python.
Four core Python checks ship today:
- D24 (all-paths-return) every control-flow path through a typed function reaches a return statement.
- D11 (status-coverage) when a function returns
Optional[X], every caller either propagates the optionality or explicitly handlesNone. A caller that silently collapsesOptional[X]into a non-optional return type is the structural equivalent of dropping theIncompletearm of Furqan'sIntegrity | Incompleteunion. - return_none_mismatch a function declaring
-> str(or any non-Optional type) that returnsNoneon some path is flagged as a type mismatch. Closes the D24 return-None blind spot. - additive_only invoked via
furqan-lint diff old.py new.py, compares two versions of a module's public surface and fires on any removed name. Adding a public name is silent.
Adapter-specific checks ship under the optional extras documented below: [rust] adds R3 + D24 + D11; [go] adds D24 + D11; [onnx] adds D24-onnx + opset_compliance + D11-onnx; [onnx-runtime] adds numpy_divergence; [onnx-profile] adds score_validity ADVISORY; [gate11] adds the Sigstore-CASM Gate 11 verifier (v0.10.0+; an additive-only contract on the public surface, cryptographically witnessed via Sigstore Rekor).
Install
pip install furqan-lint
This installs the latest release from PyPI. Requires Python 3.10+ and furqan>=0.11.0.
Optional adapters
pip install "furqan-lint[rust]" # tree-sitter Rust adapter
pip install "furqan-lint[go]" # Go adapter (requires Go 1.22+ toolchain at install time)
pip install "furqan-lint[onnx]" # ONNX graph-only checks (D24-onnx + opset-compliance + D11-onnx shape/type)
pip install "furqan-lint[onnx-runtime]" # ONNX + numpy-vs-ONNX divergence (v0.9.3+; brings in onnxruntime + numpy)
pip install "furqan-lint[onnx-profile]" # ONNX + score-validity ADVISORY (v0.9.4+; brings in onnx_tool)
pip install "furqan-lint[gate11]" # Sigstore-CASM Gate 11 (v0.10.0+; brings in sigstore + rfc8785)
pip install "furqan-lint[gate11-rust]" # Sigstore-CASM Gate 11 Rust extension (v0.11.0+; brings in sigstore + rfc8785 + tree-sitter-rust)
pip install "furqan-lint[rust,go,onnx-runtime,onnx-profile,gate11]" # all adapters with full ONNX checks plus Gate 11
Install from a specific commit or tag
pip install "git+https://github.com/BayyinahEnterprise/furqan-lint.git@v0.11.4"
Replace v0.11.4 with any tag from the release history or main for the development tip.
Furqan dependency
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.4 directly from GitHub:
pip install "git+https://github.com/BayyinahEnterprise/furqan-programming-language.git@v0.11.4"
This GitHub-pin step will not be necessary once furqan v0.11.4 is published to PyPI.
Rust support (opt-in)
As of v0.7.0, furqan-lint can lint .rs files. Rust support is
behind an opt-in extra so the Python-only install path is unchanged:
pip install "furqan-lint[rust]"
This pulls in tree-sitter and tree-sitter-rust (PyPI ships
ARM64 and x86_64 wheels for both; no source build required).
As of v0.7.2, .rs files run three checkers: R3 (ring-close,
zero-return on annotated functions, via upstream
furqan.checker.check_ring_close), D24 (all-paths-return), and
D11 (status-coverage). The D11 producer predicate recognises
both Option<T> and Result<T, E> returns; a caller that calls
a may-fail helper without propagating the union is flagged.
The planned analogue of return_none_mismatch was dropped per
the v0.7.2 prompt-grounding self-check, which empirically
demonstrated that the firing condition is unreachable on any
compilable Rust source (rustc rejects fn f() -> i32 { None }
at compile time before furqan-lint sees the file). Trait objects,
lifetimes, macro expansion, closure return-type checks, and
Cargo workspace traversal remain out of scope.
Edition is read from the nearest ancestor Cargo.toml's
[package].edition field (one of "2018", "2021", "2024"); if
no Cargo.toml is found or the field is malformed, edition
defaults to "2021". The current implementation does not branch on edition.
Go support (opt-in)
As of v0.8.1, furqan-lint can lint .go files (Go diff in v0.8.1; goast emits qualified method names as of v0.8.2). Go support is
behind an opt-in extra so the Python-only install path is unchanged:
pip install "furqan-lint[go]"
The Go toolchain (1.21+) is required at install time so the
PEP 517 build hook can compile the bundled goast binary; it
is not required at runtime.
As of v0.8.1, .go files run two checkers: D24 (all-paths-
return) and D11 (status-coverage with the (T, error) firing
shape). The cross-language _is_may_fail_producer predicate
(Shape B) recognizes the (T, error) return convention; a
caller that calls a may-fail helper without propagating the
union is flagged. R3 (zero-return) is documented not-applicable
to Go: the Go compiler rejects all firing shapes at compile
time.
Additive-only diff is supported for .go files: furqan-lint diff old.go new.go extracts uppercase-initial public names
from each file via goast and reports any names that were
present in old but not in new. The diagnostic prose is
language-aware: Go users see var Name = <new> re-export hints
rather than Python alias syntax. Cross-language diffs (e.g.
foo.py vs bar.go) return exit 2 with a "Cross-language
diff not supported" message.
ONNX support (opt-in)
As of v0.9.0, furqan-lint can lint .onnx model files. ONNX
support is behind an opt-in extra so the Python-only install
path is unchanged:
pip install "furqan-lint[onnx]"
This pulls in onnx>=1.14,<1.19. The upper bound is load-bearing:
the ONNX op registry retroactively adds operators across onnx
package releases, so an unpinned upper bound would silently
change what counts as e.g. opset 11. No onnxruntime dependency:
lint-time checks operate on the graph structure, not on inference.
.onnx files run three structural checks (current as of v0.9.1):
- D24-onnx (all-paths-emit). Every declared output in
graph.outputmust be reachable from some node in the graph (or be a graph input passed through). The structural shape mirrors a function with a missing return statement. - opset-compliance. Every node's
op_typemust exist in the declared opset, looked up viaonnx.defs.get_schema(..., max_inclusive_version=opset_version)against the pinnedonnx>=1.14,<1.19registry. - D11-onnx (shape-coverage, v0.9.1). Run
onnx.shape_inference.infer_shapes(model_proto, strict_mode=True); if it raisesInferenceError, parse the per-op message and emit oneshape_coveragediagnostic per offender. Strict-mode silent-passes ondim_param(symbolic) and emptydim_value(dynamic) shapes; this is documented as thedynamic_shape_silent_passfour-place limit.
ONNX is structurally a different substrate from Python / Rust /
Go source code. Nodes are not functions; edges are not return
statements; ValueInfo is not a type signature. The ONNX adapter
ships a parallel diagnostic family inspired by the Furqan
structural-honesty primitives, not new instances of the existing
check_d24 / check_status_coverage checkers operating on a
unified IR. The diagnostic spirit is shared (surface claims must
match substrate dataflow); the implementation is its own
package with its own runner.
The additive-only diff covers graph.input and graph.output
ValueInfo entries only. graph.value_info (intermediate tensors)
and graph.initializer (parameter tensors) are explicitly out
of scope; including them would create false positives on every
model retraining.
ONNX numpy_reference convention for NeuroGolf-shape models
The v0.9.3 numpy-vs-ONNX divergence checker discovers a
numpy_reference callable from a sibling _build.py per
the NeuroGolf-specific four-place documented limit
numpy_divergence_neurogolf_convention. The convention
assumes ARC-AGI grids encoded as (1, 10, H, W) one-hot
tensors (10 channels, one per cell color).
Two canonical patterns satisfy the convention. Both produce
zero divergence findings on a well-formed model; pick the one
that matches your build pipeline. Worked examples live at
tests/fixtures/onnx/numpy_reference_examples/.
Pattern A: pre-one-hot input. The build pipeline encodes
the raw ARC-AGI grid into (1, 10, H, W) before invoking
both the ONNX model and the numpy_reference. The reference
function accepts the already-encoded tensor:
def numpy_reference(grid):
import numpy as np
# grid is already (1, 10, H, W) one-hot.
return np.array(grid, dtype=np.float32)
The companion .json task file stores probe grids in the
encoded (1, 10, H, W) shape under train[i]["input"].
Pattern B: raw grid + local encoding. The build pipeline
keeps the ARC-AGI grid as a raw rank-2 integer grid; the
numpy_reference encodes it locally to match the ONNX
model's expected (1, 10, H, W) input shape:
def numpy_reference(grid):
import numpy as np
arr = np.array(grid, dtype=np.int64)
h, w = arr.shape
one_hot = np.zeros((1, 10, h, w), dtype=np.float32)
for c in range(10):
one_hot[0, c, :, :] = (arr == c).astype(np.float32)
return one_hot
The companion .json task file stores probe grids in the
raw rank-2 form (the standard ARC-AGI format).
Both patterns are validated by tests under
tests/test_onnx_neurogolf_adapter_examples.py; future
onnx / onnxruntime / numpy version changes that
break the convention surface as test failures rather than
stale documentation.
Sigstore-CASM Gate 11 -- normative invariants
Phase G11.A (al-Fatiha) ships SAFETY_INVARIANTS.md at the
repository root as the foundational invariants document for the
Sigstore-CASM Gate 11 substrate. The file is normative for all
subsequent Phase G11.x implementations and contains the seven
canonical cryptographic and protocol invariants, the four
mandatory disclosures inherited from the Sigstore threat model,
and the foundation-status disclosure (Section 8) for the
empirical foundation of Strategy 1 (Canonical-First
Architecture).
The empirical foundation behind Strategy 1 is currently at
candidate-finding status pending independent replication; the
seven canonical invariants are NOT magnitude or prevention
claims but cryptographic and protocol specifications. Full
treatment of the strategy mappings lives in Bayyinah at-Tartib v1.0 (second revision May 7 2026); empirical
methodology lives in Bayyinah al-Munasabat v1.0 (second
revision May 7 2026).
Contributors and reviewers preparing Phase G11.0 through Phase
G11.12 patches MUST read SAFETY_INVARIANTS.md before
submitting changes that touch src/furqan_lint/gate11/ or any
Phase G11.x test fixtures.
Sigstore-CASM Gate 11 (opt-in)
As of v0.10.0, furqan-lint can sign and verify a Compositional Additive-only Surface Manifest (CASM) for any Python module via Sigstore-keyed transparency. Gate 11 (Kiraman Katibin, "the noble scribes" who keep an unforgeable record of additive-only public surface) is behind an opt-in extra so the default install path is unchanged:
pip install "furqan-lint[gate11]"
This pulls in sigstore>=3.0.0,<4 and rfc8785>=0.1.4,<0.2.
The [gate11] extra is independent of [onnx] /
[onnx-runtime] / [onnx-profile]; it adds no inference
dependencies.
The CLI grows two opt-in entry points and one flag:
furqan-lint manifest init <module.py> # OIDC sign; emit .furqan.manifest.sigstore
furqan-lint manifest verify <module.py> # 9-step CASM-V verification
furqan-lint manifest update <module.py> # additive-only re-sign; refuses removals
furqan-lint check --gate11 <path> # run normal checks + verify any CASM bundles found
furqan-lint check without --gate11 produces the same
diagnostics as in v0.9.4. Gate 11 only activates when the user
opts in via the flag or the manifest subcommand.
Wire format. Each manifest is a JSON document carrying
casm_version: "1.0", language: "python",
module_root_hash (BOM-stripped, LF-normalized, UTF-8 SHA-256),
public_surface.names (ASCII-sorted entries of kind
function / class / constant, each with a canonical
signature_fingerprint), tooling.linter_version,
tooling.checker_set_hash, and chain_pointer.previous_bundle_hash
for chain integrity. The whole document is canonicalized via
RFC 8785 (JSON Canonical Form) before being signed. Bundles
ship as <module>.furqan.manifest.sigstore.
Verification. manifest verify runs the 9-step flow
documented under the CASM-V-NNN error namespace:
- Parse bundle (CASM-V-010 on JSON failure).
- Check
casm_version == "1.0"(CASM-V-001). - Check
language == "python"(CASM-V-001). - Load Sigstore trust root via TUF (CASM-V-020 ADVISORY on refresh failure with cache fallback; CASM-V-021 if no cache).
- Re-canonicalize the manifest (RFC 8785).
- Verify Sigstore signature against the canonical bytes (CASM-V-030..034 by failure mode).
- Compare
module_root_hashto the on-disk module (CASM-V-040 on mismatch). - Compare
public_surface.namesto the live extraction; if the live module uses dynamic__all__, the result is indeterminate (CASM-V-INDETERMINATE) rather than a false pass. - Check
chain_pointerintegrity against the previous bundle when supplied (CASM-V-060 on hard mismatch; CASM-V-061 ADVISORY when no previous bundle is locally accessible).
manifest update enforces the additive-only contract: any name
removal between previous and proposed manifest is rejected
(CASM-V-050); any signature change on a retained name is
rejected (CASM-V-051). Additions are accepted and re-signed.
Gate 11 scope and disclosures
Gate 11 inherits the Sigstore threat model documented in Newman et al., Sigstore: Software Signing for Everybody (ACM CCS 2022). The four residual disclosures are recorded here in the Shape A documented-limit form so downstream Relying Parties can calibrate trust.
- (N1) Short-window OIDC-identity compromise. A signature binds to the OIDC identity that requested the Fulcio certificate. If the signing identity's OIDC token is compromised within the certificate validity window (typically 10 minutes), an attacker can produce a CASM bundle that verifies cleanly. Mitigation lives outside furqan-lint (hardware-backed OIDC, short-lived tokens, identity-provider audit logging). Severity: HIGH but out-of-scope for the lint itself; documented per Newman 2022 section 6.
- (N2) Typosquatting at the publish boundary. Sigstore proves "an entity controlling identity X signed bytes Y at time T". It does not prove that identity X is the legitimate maintainer of the package the consumer thinks they are installing. Relying Parties must pin both the package name and the expected signing identity; furqan-lint surfaces the identity in the verification result so a CI policy can assert it.
- (N3) Rekor entry queryability and privacy. Rekor is a public append-only log. Manifest entries (including the module-root hash and the public-surface name list) are published unencrypted and become enumerable by third parties. Codebases that are themselves confidential or under embargo should not sign their CASM bundles to the public Rekor instance; the staging instance or a private transparency service is the right substrate. This is recorded as Shape A scope statement F7 below.
- (N4) Log-retention horizon. Rekor's public-instance retention policy is operational, not contractual. Verification past a multi-year horizon may require local mirroring of the relevant log entries. furqan-lint does not mirror Rekor on the user's behalf.
SCITT vocabulary
Gate 11 uses the IETF SCITT (Supply Chain Integrity,
Transparency, and Trust) architectural vocabulary throughout the
verification module and CHANGELOG entries: the Issuer is the
OIDC-identified signer; the Transparency Service is Rekor;
the Relying Party is the consumer running manifest verify
(or check --gate11); the Auditor is any third party who
replays the chain integrity check. The mapping is documented
inline in src/furqan_lint/gate11/verification.py.
Shape A scope statements
Two limits are documented as Shape A (acknowledged residual, not patched in v0.10.0) rather than as defects:
- F4. Linter-substrate trust is recursive. The
tooling.checker_set_hashfield records the integrity of the checker code that produced the manifest. It does not prove that the checker code itself is honest; verifying that requires either signing furqan-lint's own release artifacts with Gate 11 (G11.5 carry-forward) or an independent reproducible build. v0.10.0 ships the field; closing the recursion is a later round. - F7. Rekor entries leak public-surface shape. Per N3 above, the Rekor log publishes the manifest's hash and the public name list. A confidential codebase MUST NOT sign its CASM bundles to the public Rekor instance. v0.10.0 documents the failure mode; private-transparency-service routing is a later round.
The prompt asked these to surface as findings; v0.10.0 records them as Shape A scope statements (acknowledged in design, deferred by scope) rather than as defects (signaled to be fixed this round) because the underlying substrate is the Sigstore public infrastructure, not furqan-lint code.
Sigstore-CASM Gate 11 (Rust extension)
As of v0.11.0, Phase G11.1 (as-Saffat) extends Sigstore-CASM Gate 11 to Rust source files. Bundles are produced via the existing Python sigstore-python pipeline; FFI to sigstore-rs is deferred to v1.5 per Yusuf Horizon discipline.
pip install "furqan-lint[gate11-rust]"
The CLI surface dispatches on file extension:
furqan-lint manifest init src/lib.rs # OIDC sign; emit src/lib.rs.furqan.manifest.sigstore
furqan-lint manifest verify src/lib.rs.furqan.manifest.sigstore \
--expected-identity '<pattern>' \
--expected-issuer 'https://token.actions.githubusercontent.com'
furqan-lint manifest update src/lib.rs # additive-only re-sign; refuses removals
furqan-lint check --gate11 src/ # walk .rs files; verify any adjacent bundle
The Rust manifest declares module_identity.language = "rust"
and public_surface.extraction_method = "tree-sitter.rust-public-surface@v1.0". Public-surface
entries are emitted for top-level pub fn, pub struct,
pub enum, pub trait, pub type, pub const,
pub static, and pub use re-exports.
Identity policy enforcement (audit C-1 + M-6 corrective)
v0.11.0 closes the Phase G11.0 v0.10.0 audit gap where
furqan-lint manifest verify accepted any valid Sigstore
signature regardless of OIDC identity. The verifier now
requires an explicit Identity policy:
--expected-identity <pattern>: required for production verification. The pattern is matched against the signing certificate's Subject Alternative Name (SAN) extension via sigstore-python'sIdentitypolicy class.--expected-issuer <issuer>: optional OIDC issuer constraint (e.g.,https://accounts.google.com,https://token.actions.githubusercontent.com).--allow-any-identity: explicit opt-in toUnsafeNoOp()policy. Use of this flag in production CI is itself an audit signal (Sulayman-Naml ADVISORY pattern); the refuse-without-policy default is the substrate-side enforcement of Newman 2022 N2 (typosquatting at the publish boundary).
Failure modes:
CASM-V-035(no Identity policy supplied): default refuse-without-policy. Pass--expected-identity <pattern>or explicitly--allow-any-identity.CASM-V-032(identity policy mismatch): the signing certificate's SAN does not match the supplied pattern.CASM-V-036(identity extraction failure): the signing certificate is malformed or lacks the SAN extension. Replaces the v0.10.0 string sentinel"<unknown OIDC identity>".
These error codes plus the v0.10.0 codes (CASM-V-001 through CASM-V-061) form the universal CASM-V namespace declared in SAFETY_INVARIANTS.md Invariant 6. Phase G11.4 (Tasdiq al-Bayan) will exercise all codes against bundles produced from at least the Python and Rust substrates.
Documented limits (v1.0 scope, deferred to v1.5)
- Method-level signing: impl-block methods are not part of
the v1.0 public-surface fingerprint (consistent with the
existing
furqan_lint.rust_adapter.extract_public_namesdocumented limit). - Lifetime-aware canonical types: lifetimes are stripped
during fingerprinting so that
f<'a>(s: &'a str)andf<'b>(s: &'b str)produce identical fingerprints. - Trait-object semantic equivalence:
Box<dyn Trait + 'a>is fingerprinted asBox<dyn Trait>(lifetime stripped; bounds beyond first identifier elided). - Macro-expansion-aware signing: macro calls are signed at the source level, NOT after expansion.
- Glob re-exports:
pub use foo::*cannot be statically resolved; the verifier surfacesCASM-V-INDETERMINATErather than a false pass per Phase G11.A Invariant 6 step 8.
These are documented limits in the Sigstore-CASM substrate;
improvement is a v1.5 horizon item tracked in
PHASE_G11_1_CARRY_FORWARD.md.
Usage
furqan-lint check path/to/file.py
furqan-lint check path/to/directory/
furqan-lint diff old_version.py new_version.py
furqan-lint version
CI Integration
Two ways to wire furqan-lint into your workflow.
GitHub Action
Three lines in your workflow file run the structural checks on every push or pull request:
# .github/workflows/furqan-lint.yml
name: Furqan Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: BayyinahEnterprise/furqan-lint@v0.11.4
with:
path: src/
Inputs (all optional):
path-- file or directory to check. Default:.python-version-- Python version to use. Default:3.12furqan-lint-version-- pinned version to install. Default: install latest frommain.
Pre-Commit Hook
Run the same checks locally on every git commit against staged
Python files:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/BayyinahEnterprise/furqan-lint
rev: v0.11.4
hooks:
- id: furqan-lint
Then pre-commit install. Failures block the commit.
Using with Other Tools
furqan-lint is complementary to ruff and mypy. Each catches a different class of issue:
| Tool | Catches | Overlap with furqan-lint |
|---|---|---|
| ruff | Style, unused imports, complexity, common bug patterns, formatting (replaces black + isort + flake8 + pyupgrade) | None |
| mypy | Type errors, some missing returns | Partial overlap on D24 and return-none. mypy does NOT catch Optional collapse (D11) or API-breaking changes (additive-only). |
| furqan-lint | Missing return paths, Optional collapse, return-None mismatch, API-breaking changes, zero-return functions | See mypy column |
Recommended .pre-commit-config.yaml for Your Project
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
- id: mypy
- repo: https://github.com/BayyinahEnterprise/furqan-lint
rev: v0.11.4
hooks:
- id: furqan-lint
Then pre-commit install. Run order: ruff (lint + format) -> mypy
(types) -> furqan-lint (structural honesty). Each layer catches what
the previous layers do not.
Contributing to furqan-lint
git clone https://github.com/BayyinahEnterprise/furqan-lint.git
cd furqan-lint
pip install -e ".[dev]"
pre-commit install
# Run all tools manually
ruff check .
ruff format --check .
mypy
pytest -q
# Run by test category
pytest -m unit # fast, in-process, no subprocess
pytest -m integration # CLI and pipeline tests
pytest -m "not slow" # skip slow tests
pytest -m "not network" # skip network-dependent tests
The furqan-lint check src/ self-check runs as part of pre-commit;
the tool that catches drift in other people's code must not have
drift in its own.
Example
# example.py
from typing import Optional
def find_record(id: int) -> Optional[dict]:
if id <= 0:
return None
return {"id": id}
def get_name(id: int) -> str:
record = find_record(id)
if record is not None:
return record["name"]
# Missing else: falls through with no return
$ furqan-lint check example.py
MARAD example.py
3 violation(s):
[all_paths_return] Function 'get_name' at line 8 declares
-> str but not every control-flow path reaches a return
statement.
[status_coverage] Function 'get_name' at line 8 calls
'find_record' (returns Optional[dict]) but declares -> str.
The Optional is silently collapsed.
[return_none_mismatch] Function 'get_name' at line 8
declares -> str but returns None on at least one path.
Closure history
Per-version closure ledgers are in CHANGELOG.md. This README previously mirrored closures from v0.2.0 through v0.11.0; that mirror was retired in v0.11.4 (Phase G10.5 al-Mubin) because CHANGELOG.md is the canonical closure ledger and the README mirror was repeatedly drifting out of sync. The framework section 10.2 retirement procedure was followed: the existing prose was deleted and a single pointer section took its place.
Remaining limitations
Each limitation here has a fixture in tests/fixtures/documented_limits/
and a test in tests/test_documented_limits.py pinning the current
behaviour, so any change (in either direction) is intentional rather
than silent.
self.method()calls. The adapter resolvesself.foo()to the bare method namefoo, the same as a plainfoo()call. This is not a bug today but will need revisiting if the adapter ever stores qualified call paths.- Checker coverage. Only four of Furqan's ten checkers run on Python. The rest depend on Python-specific conventions (scope declarations, layer annotations, calibration bounds, dependency mapping) that standard Python does not provide.
- Return-type expression inference.
return_none_mismatchonly catches theNoneliteral. A-> intfunction returning"hello"is not caught. - Exhaustive
matchrecognition. Each case body wraps as a maybe-runsIfStmt, so D24 cannot recognise a structurally exhaustivematch(with acase _:arm) as guaranteed coverage. Future work could splice the catch-all case into the priorIfStmt'selse_body. - Aliased
Optional/Unionimports.from typing import Optional as MyOpt; -> MyOpt[X]is treated as a non-Optional return type. The same gap applies toUnion:from typing import Union as U; -> U[X, None]andfrom somelib import Union; -> Union[X, None]both bypass the bare-name andtyping./t.matchers. The matcher recognises the bareOptional/Unionnames and the qualifiedtyping./t.forms only; arbitrary aliases and same-named imports from non-typingmodules need symbol-table tracking (parse imports, build alias map, resolve before matching), which is deferred to a future phase. Workaround: use the bare or qualified form, or rename the import toimport typing as t. - Local classes inside any function or method body. A class
defined inside a function body or a method body has its methods
silently dropped. The v0.3.2 nested-class fix added recursive
descent through top-level
ClassDef->ClassDef(soOuter.Inner.methodis collected); it does NOT extend throughFunctionDef->ClassDefregardless of whether theFunctionDefis at module scope or inside anotherClassDef. The argument for the asymmetry: a local class is a private implementation detail (often a closure-like return value), not part of the module's public contract that D24 andreturn_none_mismatchexist to enforce. If a real-world regression demonstrates otherwise, extend the function walker to descend into localClassDefbodies and call_collect_class_methods.
Rust adapter (current as of v0.8.3)
Each Rust limit has a fixture in
tests/fixtures/rust/documented_limits/ and a pinning test in
tests/test_rust_correctness.py.
- Trait-object return types. Functions returning
Box<dyn Trait>are translated to aTypePaththat ignores the trait-object payload. Trait-object polymorphism is out of scope; a future a future checker would be the right place to revisit. Pinned astests/fixtures/rust/documented_limits/trait_object_return.rs. - Lifetime-affected return types. Functions with explicit
lifetime parameters (
fn f<'a>(...) -> &'a str) have their lifetimes stripped during translation; the return type is treated as-> &'a strliterally (no lifetime semantics). D24's path-coverage logic is unaffected; a future borrow-pattern checker would need lifetime preservation. Pinned astests/fixtures/rust/documented_limits/lifetime_param_return.rs. - Closures with annotated return types.
closure_expressionnodes are skipped for D24, D11, AND R3 in v0.7.1. The outer function is checked normally; the closure body is opaque. A future phase may revisit when there is a concrete user-reported false negative. Pinned astests/fixtures/rust/documented_limits/closure_with_annotated_return.rs. extract_public_namesomits impl-block methods. The Rust additive-only diff path's name extractor walks only top-level CST root children; methods defined insideimpl Type { ... }blocks are intentionally not collected in v0.8.3. Asymmetric with goast (which emits qualified method names likeCounter.incrementas of v0.8.2). Pinned astests/fixtures/rust/documented_limits/impl_methods_omitted.rs(added in v0.8.3). Resolution path: registered as a v0.8.4 candidate.panic!()(or any diverging macro) used as a tail expression with no;. The translator synthesizes aReturnStmt(opaque)for any tail expression per the v0.7.0 R1 rule, so R3 (zero-return) does not fire onfn f() -> i32 { panic!() }. Adding a fix would require either a hardcoded diverging-macro allowlist (brittle) or cross-file type inference of the macro's expansion type (out of scope). A future phase may revisit if the Rust ecosystem standardizes a#[diverging]attribute. Pinned astests/fixtures/rust/documented_limits/r3_panic_as_tail_expression.rs.
Go adapter (current as of v0.8.2)
Each Go limit has a fixture in
tests/fixtures/go/documented_limits/ and a pinning test in
tests/test_go_documented_limits.py (or, for the older
translator-level limits, in tests/test_go_translator.py).
- 3+-element return signatures. Translate to opaque
TypePath("<multi-return>"). D24 and D11 see the function but cannot reason about the individual arms. Pinned astests/fixtures/go/documented_limits/multi_return_three_or_more.go. - 2-element non-error tuple returns. Translate to opaque
TypePath("(T, U)"). D11's may-fail predicate does NOT fire on these; only(T, error)shapes are recognized as may-fail per locked decision 4. Pinned astests/fixtures/go/documented_limits/two_element_non_error_tuple.go. forandfor-rangebodies. Wrap as may-runs-0-or-N opaque IfStmt. D24 cannot prove that aforbody that always returns guarantees coverage. Pinned astests/fixtures/go/documented_limits/for_statement_opaque.go.switchbodies. Wrap as may-runs-0-or-N opaque IfStmt; case-arm returns are invisible to D24. Pinned astests/fixtures/go/documented_limits/switch_statement_opaque.go.selectbodies. Wrap as may-runs-0-or-N opaque IfStmt. Pinned astests/fixtures/go/documented_limits/select_statement_opaque.go.deferstatements. Wrap as opaque; the deferred call's effect on control flow (panic recovery, resource cleanup) is not modeled. Pinned astests/fixtures/go/documented_limits/defer_statement_opaque.go.- Interface method dispatch. Calls through interface
receivers are not specially modeled; the receiver type is
opaque to the adapter. Pinned as
tests/fixtures/go/documented_limits/interface_method_dispatch.go. - Generic type parameters. Syntactically allowed in
signatures but their constraints are ignored. Pinned as
tests/fixtures/go/documented_limits/generic_type_parameters.go. - R3 not-applicable. The Go compiler rejects all R3 firing
shapes (zero-return on annotated functions) at compile time;
the only compilable nearest-edge case is a named-return with
bare
return, which the translator sees as having a return statement. Pinned astests/fixtures/go/documented_limits/r3_compile_rejected.go(added in v0.8.1).
ONNX adapter (current as of v0.9.4)
Each ONNX limit has a fixture in
tests/fixtures/onnx/documented_limits/ and a pinning test in
tests/test_onnx_correctness.py,
tests/test_onnx_public_surface_additive.py, or
tests/test_onnx_shape_coverage.py.
-
score_validity is opt-in via [onnx-profile] (v0.9.4). The v0.9.4 score-validity ADVISORY checker wraps
onnx_tool.model_profile()to surface profiler-coverage gaps (e.g., the cont45 TopK-without-axis crash). It runs only when the[onnx-profile]extra is installed (which brings inonnx_tool); otherwise it silent-passes. ADVISORY findings exit 0 (the model is structurally valid; the failure is in the deployment-side profiler). Pinned astests/fixtures/onnx/documented_limits/score_validity_optin_extra.py. -
numpy_divergence requires NeuroGolf-convention sidecars (v0.9.3). The numpy-vs-ONNX divergence checker is opt-in by reference presence: it only runs when (a) the
[onnx-runtime]extra is installed, (b) a sibling<basename>_build.pyexists exporting top-level callablenumpy_reference, AND (c) a sibling<basename>.jsonexists in ARC-AGI task format withtrain[*]['input']. Generic ONNX users with no NeuroGolf-shaped sidecars see silent-pass on the divergence checker. General-purpose reference-discovery conventions (decorator-based annotation) and probe-grid formats are a v0.9.5+ extension. Pinned astests/fixtures/onnx/documented_limits/numpy_divergence_neurogolf_convention.py. -
Dynamic shape silent-pass. Strict-mode shape inference silent-passes on
dim_param(symbolic batch / sequence dims like"batch") and emptydim_value(dynamic shapes). v0.9.1 takes the position that this is the right default because ONNX models withdim_paramare typically deployment-time signature shapes that bind concrete values at runtime; a future release may revisit if a concrete user-reported false negative motivates a stricter mode. Pinned astests/fixtures/onnx/documented_limits/dynamic_shape_silent_pass.py. -
graph.value_infoandgraph.initializernot in additive contract. The additive-only diff (furqan-lint diff old.onnx new.onnx) coversgraph.inputandgraph.outputValueInfo only. Intermediate tensors and initializers are out of scope per Decision 5 of the v0.9.0 prompt; including them would create false positives on every model retraining. Pinned astests/fixtures/onnx/documented_limits/intermediates_excluded.py. -
ONNX op-registry pin window
>=1.14,<1.19. Op registry version is pinned to prevent silent semantics drift acrossonnxpackage upgrades (the ONNX op registry retroactively adds operators across releases). Consumers requiring a newer registry must wait for a furqan-lint patch release that bumps the pin. Pinned astests/fixtures/onnx/documented_limits/registry_pin_window.py.
Retired in v0.9.1
- D11-onnx deferred to v0.9.1 (former
shape_coverage_deferreddocumented limit). v0.9.1 ships D11-onnx via strict-mode shape inference, so the deferral entry is no longer load-bearing. The companion v0.9.0 pinning testtest_onnx_d11_deferred_v0_9_0_passesis also deleted in v0.9.1 commit 4 per the delete-plus-add discipline (round-30 MED-1 closure). The v0.9.1 firing testtest_d11_onnx_fires_on_shape_mismatchreplaces it.
License
Apache-2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file furqan_lint-0.11.4.tar.gz.
File metadata
- Download URL: furqan_lint-0.11.4.tar.gz
- Upload date:
- Size: 279.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6f1f0a8a49eb1a4554e5e2da962d61d6fb63cfa34176f1c12037d4ab4b43e4f9
|
|
| MD5 |
f4cfbfba893e6cd2080c7af91db3f2bf
|
|
| BLAKE2b-256 |
caf0958e5d63c65bb14e17e497f9cd5744721560794f1cd0ef316a5657c2d0bc
|
Provenance
The following attestation bundles were made for furqan_lint-0.11.4.tar.gz:
Publisher:
release.yml on BayyinahEnterprise/furqan-lint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
furqan_lint-0.11.4.tar.gz -
Subject digest:
6f1f0a8a49eb1a4554e5e2da962d61d6fb63cfa34176f1c12037d4ab4b43e4f9 - Sigstore transparency entry: 1480762453
- Sigstore integration time:
-
Permalink:
BayyinahEnterprise/furqan-lint@718b68e5a8098094f9787240726615a8d239b66f -
Branch / Tag:
refs/tags/v0.11.4 - Owner: https://github.com/BayyinahEnterprise
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@718b68e5a8098094f9787240726615a8d239b66f -
Trigger Event:
push
-
Statement type:
File details
Details for the file furqan_lint-0.11.4-py3-none-any.whl.
File metadata
- Download URL: furqan_lint-0.11.4-py3-none-any.whl
- Upload date:
- Size: 2.1 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d4e29892eb36da22a8241bb20c9210e9ddc2a78785ac8bef894aeada37e219a
|
|
| MD5 |
c71c3fbd81d87128b1f045f12c04db1f
|
|
| BLAKE2b-256 |
c45ca1cd8abaa3d9c85e6636d2880861a33962923ea32a1f9d138b88fcb288d3
|
Provenance
The following attestation bundles were made for furqan_lint-0.11.4-py3-none-any.whl:
Publisher:
release.yml on BayyinahEnterprise/furqan-lint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
furqan_lint-0.11.4-py3-none-any.whl -
Subject digest:
9d4e29892eb36da22a8241bb20c9210e9ddc2a78785ac8bef894aeada37e219a - Sigstore transparency entry: 1480762544
- Sigstore integration time:
-
Permalink:
BayyinahEnterprise/furqan-lint@718b68e5a8098094f9787240726615a8d239b66f -
Branch / Tag:
refs/tags/v0.11.4 - Owner: https://github.com/BayyinahEnterprise
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@718b68e5a8098094f9787240726615a8d239b66f -
Trigger Event:
push
-
Statement type: