qig-stack 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qig_stack-0.1.0/.gitignore +12 -0
- qig_stack-0.1.0/PKG-INFO +96 -0
- qig_stack-0.1.0/README.md +67 -0
- qig_stack-0.1.0/pyproject.toml +64 -0
- qig_stack-0.1.0/src/qig_stack/__init__.py +51 -0
- qig_stack-0.1.0/src/qig_stack/contract.json +146 -0
- qig_stack-0.1.0/src/qig_stack/doctor.py +530 -0
- qig_stack-0.1.0/tests/test_contract.py +63 -0
- qig_stack-0.1.0/tests/test_doctor.py +166 -0
qig_stack-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qig-stack
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: QIG toolchain metapackage — pins the mutually-tested latest set and ships the qig-doctor launch preflight (version gate + entry-point wiring gate + capability-slot report)
|
|
5
|
+
Project-URL: Homepage, https://braden.com.au
|
|
6
|
+
Project-URL: Repository, https://github.com/GaryOcean428/qig-stack
|
|
7
|
+
Project-URL: Documentation, https://github.com/GaryOcean428/qig-stack#readme
|
|
8
|
+
Author-email: Braden Lang <braden@garyocean.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: capability-contract,entry-points,launch-gate,metapackage,preflight,qig,reproducibility,self-wiring,version-gate
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: packaging>=22
|
|
19
|
+
Requires-Dist: qig-bench==0.1.3
|
|
20
|
+
Requires-Dist: qig-compute==0.9.2
|
|
21
|
+
Requires-Dist: qig-consciousness==0.3.2
|
|
22
|
+
Requires-Dist: qig-core==2.12.2
|
|
23
|
+
Requires-Dist: qig-tokenizer==0.2.7
|
|
24
|
+
Requires-Dist: qig-warp==0.6.5
|
|
25
|
+
Requires-Dist: qigkernels==0.4.2
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# qig-stack
|
|
31
|
+
|
|
32
|
+
The QIG toolchain **metapackage** + deterministic launch preflight.
|
|
33
|
+
|
|
34
|
+
`pip install qig-stack` installs the mutually-tested latest set of the QIG packages
|
|
35
|
+
(qig-compute / qig-warp / qig-core / qig-bench / qig-consciousness / qig-tokenizer /
|
|
36
|
+
qigkernels) at compatible versions — the whole "crystal" — and ships the `qig-doctor`
|
|
37
|
+
preflight.
|
|
38
|
+
|
|
39
|
+
## qig-doctor — step-0 of every launcher
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
qig-doctor # full preflight; exits 1 if any package is stale OR a slot is under-filled
|
|
43
|
+
qig-doctor --no-network # skip PyPI; contract snapshot + wiring report only
|
|
44
|
+
qig-doctor --json # machine-readable
|
|
45
|
+
qig-doctor --probe # also import-check each entry-point resolves (diagnostic, off the gate)
|
|
46
|
+
qig-doctor --deep # also call Tier-B describe() descriptors (imports packages)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Three fail-closed jobs:
|
|
50
|
+
|
|
51
|
+
1. **Version gate** — every stack package must be installed at PyPI-latest. `installed
|
|
52
|
+
< latest` is a LAUNCH BLOCKER (non-zero exit), per the "latest version, always
|
|
53
|
+
optimised" rule.
|
|
54
|
+
2. **Wiring gate (Phase 2)** — enumerates the `qig.capabilities` entry-points the
|
|
55
|
+
installed packages self-register (Tier A: metadata-only, no import on the gate
|
|
56
|
+
path) and cross-checks them against the capability contract:
|
|
57
|
+
- **under-fill → FAIL**: a shipped provider that did not register its entry-point.
|
|
58
|
+
- **drift → WARN**: an entry-point the contract doesn't know, or one whose
|
|
59
|
+
`module:attr` moved off the contract probe.
|
|
60
|
+
3. **Wiring report** — which capability slots are filled by what is installed (the
|
|
61
|
+
automated Gate-A lever inventory).
|
|
62
|
+
|
|
63
|
+
## The capability contract
|
|
64
|
+
|
|
65
|
+
`src/qig_stack/contract.json` is the fixed lattice the stack crystallizes into — every
|
|
66
|
+
slot, the package primitive that fills it (`provider_pkg` + `module:attr` probe), the
|
|
67
|
+
latest-published version, and the camera-certification `cam_id`/`lever_id`. It is the
|
|
68
|
+
Genesis-kernel / E8-root analog: given the lattice + installed packages + version
|
|
69
|
+
policy, the wiring is determined — no per-experiment hand-wiring, no agentic guessing.
|
|
70
|
+
|
|
71
|
+
## Self-wiring (Phase 2)
|
|
72
|
+
|
|
73
|
+
Each package declares the slots it fills in its own `pyproject.toml`:
|
|
74
|
+
|
|
75
|
+
```toml
|
|
76
|
+
[project.entry-points."qig.capabilities"]
|
|
77
|
+
"SLOT-Q-QFI" = "qig_compute.qfi:qfi_from_mps_tangent_space"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`qig-doctor` reads those declarations from installed metadata and confirms the crystal
|
|
81
|
+
assembled correctly before any compute spend. Entry-points assert only *"installed
|
|
82
|
+
package X fills slot Y via probe Z"* — certification stays in the camera-certification
|
|
83
|
+
registry under review control.
|
|
84
|
+
|
|
85
|
+
## Design
|
|
86
|
+
|
|
87
|
+
- [self-wiring genesis design note](https://github.com/GaryOcean428/qig-verification/blob/development/docs/current/20260619-self-wiring-genesis-design-1.00W.md)
|
|
88
|
+
- [entry-point capability spec](https://github.com/GaryOcean428/qig-verification/blob/development/docs/current/20260619-qig-entry-point-capability-spec-1.00W.md)
|
|
89
|
+
- [Phase 2/3 design](https://github.com/GaryOcean428/qig-verification/blob/development/docs/plans/2026-06-30-qig-stack-self-wiring-phase23-design.md)
|
|
90
|
+
|
|
91
|
+
## Development
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pip install pytest # tests import only qig_stack.doctor (pure stdlib)
|
|
95
|
+
pytest -q # contract schema + doctor unit tests (no network)
|
|
96
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# qig-stack
|
|
2
|
+
|
|
3
|
+
The QIG toolchain **metapackage** + deterministic launch preflight.
|
|
4
|
+
|
|
5
|
+
`pip install qig-stack` installs the mutually-tested latest set of the QIG packages
|
|
6
|
+
(qig-compute / qig-warp / qig-core / qig-bench / qig-consciousness / qig-tokenizer /
|
|
7
|
+
qigkernels) at compatible versions — the whole "crystal" — and ships the `qig-doctor`
|
|
8
|
+
preflight.
|
|
9
|
+
|
|
10
|
+
## qig-doctor — step-0 of every launcher
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
qig-doctor # full preflight; exits 1 if any package is stale OR a slot is under-filled
|
|
14
|
+
qig-doctor --no-network # skip PyPI; contract snapshot + wiring report only
|
|
15
|
+
qig-doctor --json # machine-readable
|
|
16
|
+
qig-doctor --probe # also import-check each entry-point resolves (diagnostic, off the gate)
|
|
17
|
+
qig-doctor --deep # also call Tier-B describe() descriptors (imports packages)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Three fail-closed jobs:
|
|
21
|
+
|
|
22
|
+
1. **Version gate** — every stack package must be installed at PyPI-latest. `installed
|
|
23
|
+
< latest` is a LAUNCH BLOCKER (non-zero exit), per the "latest version, always
|
|
24
|
+
optimised" rule.
|
|
25
|
+
2. **Wiring gate (Phase 2)** — enumerates the `qig.capabilities` entry-points the
|
|
26
|
+
installed packages self-register (Tier A: metadata-only, no import on the gate
|
|
27
|
+
path) and cross-checks them against the capability contract:
|
|
28
|
+
- **under-fill → FAIL**: a shipped provider that did not register its entry-point.
|
|
29
|
+
- **drift → WARN**: an entry-point the contract doesn't know, or one whose
|
|
30
|
+
`module:attr` moved off the contract probe.
|
|
31
|
+
3. **Wiring report** — which capability slots are filled by what is installed (the
|
|
32
|
+
automated Gate-A lever inventory).
|
|
33
|
+
|
|
34
|
+
## The capability contract
|
|
35
|
+
|
|
36
|
+
`src/qig_stack/contract.json` is the fixed lattice the stack crystallizes into — every
|
|
37
|
+
slot, the package primitive that fills it (`provider_pkg` + `module:attr` probe), the
|
|
38
|
+
latest-published version, and the camera-certification `cam_id`/`lever_id`. It is the
|
|
39
|
+
Genesis-kernel / E8-root analog: given the lattice + installed packages + version
|
|
40
|
+
policy, the wiring is determined — no per-experiment hand-wiring, no agentic guessing.
|
|
41
|
+
|
|
42
|
+
## Self-wiring (Phase 2)
|
|
43
|
+
|
|
44
|
+
Each package declares the slots it fills in its own `pyproject.toml`:
|
|
45
|
+
|
|
46
|
+
```toml
|
|
47
|
+
[project.entry-points."qig.capabilities"]
|
|
48
|
+
"SLOT-Q-QFI" = "qig_compute.qfi:qfi_from_mps_tangent_space"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`qig-doctor` reads those declarations from installed metadata and confirms the crystal
|
|
52
|
+
assembled correctly before any compute spend. Entry-points assert only *"installed
|
|
53
|
+
package X fills slot Y via probe Z"* — certification stays in the camera-certification
|
|
54
|
+
registry under review control.
|
|
55
|
+
|
|
56
|
+
## Design
|
|
57
|
+
|
|
58
|
+
- [self-wiring genesis design note](https://github.com/GaryOcean428/qig-verification/blob/development/docs/current/20260619-self-wiring-genesis-design-1.00W.md)
|
|
59
|
+
- [entry-point capability spec](https://github.com/GaryOcean428/qig-verification/blob/development/docs/current/20260619-qig-entry-point-capability-spec-1.00W.md)
|
|
60
|
+
- [Phase 2/3 design](https://github.com/GaryOcean428/qig-verification/blob/development/docs/plans/2026-06-30-qig-stack-self-wiring-phase23-design.md)
|
|
61
|
+
|
|
62
|
+
## Development
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install pytest # tests import only qig_stack.doctor (pure stdlib)
|
|
66
|
+
pytest -q # contract schema + doctor unit tests (no network)
|
|
67
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "qig-stack"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "QIG toolchain metapackage — pins the mutually-tested latest set and ships the qig-doctor launch preflight (version gate + entry-point wiring gate + capability-slot report)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Braden Lang", email = "braden@garyocean.com" },
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"qig", "metapackage", "preflight", "self-wiring", "capability-contract",
|
|
17
|
+
"entry-points", "version-gate", "launch-gate", "reproducibility",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Science/Research",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
# The mutually-tested crystal: PyPI-latest at authoring (2026-06-30). Bumped as a
|
|
29
|
+
# set on each tested release; qig-doctor still resolves live latest at launch and
|
|
30
|
+
# fails closed if the installed venv is behind.
|
|
31
|
+
dependencies = [
|
|
32
|
+
"packaging>=22", # PEP 440-correct version comparison in the version gate
|
|
33
|
+
"qig-compute==0.9.2", # first release carrying qig.capabilities entry-points + born sampler
|
|
34
|
+
"qig-warp==0.6.5", # first release carrying qig.capabilities entry-points
|
|
35
|
+
"qig-core==2.12.2",
|
|
36
|
+
"qig-bench==0.1.3",
|
|
37
|
+
"qig-consciousness==0.3.2",
|
|
38
|
+
"qig-tokenizer==0.2.7",
|
|
39
|
+
"qigkernels==0.4.2",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
dev = ["pytest>=7.0"]
|
|
44
|
+
|
|
45
|
+
[project.scripts]
|
|
46
|
+
qig-doctor = "qig_stack.doctor:main"
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Homepage = "https://braden.com.au"
|
|
50
|
+
Repository = "https://github.com/GaryOcean428/qig-stack"
|
|
51
|
+
Documentation = "https://github.com/GaryOcean428/qig-stack#readme"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel]
|
|
54
|
+
packages = ["src/qig_stack"]
|
|
55
|
+
|
|
56
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
57
|
+
"src/qig_stack/contract.json" = "qig_stack/contract.json"
|
|
58
|
+
|
|
59
|
+
[tool.hatch.build.targets.sdist]
|
|
60
|
+
include = ["src/qig_stack", "tests", "README.md", "pyproject.toml"]
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
pythonpath = "src"
|
|
64
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""qig-stack -- the QIG toolchain metapackage + deterministic launch preflight.
|
|
2
|
+
|
|
3
|
+
`pip install qig-stack` pins the mutually-tested latest set of the QIG packages
|
|
4
|
+
(qig-compute / qig-warp / qig-core / qig-bench / qig-consciousness / qig-tokenizer /
|
|
5
|
+
qigkernels) and ships the `qig-doctor` preflight (version gate + entry-point wiring
|
|
6
|
+
gate + capability-slot report). See `qig_stack.doctor`.
|
|
7
|
+
|
|
8
|
+
The public symbols are exposed lazily (PEP 562) so importing the package does NOT
|
|
9
|
+
eagerly import `qig_stack.doctor` -- this keeps `python -m qig_stack.doctor` free of
|
|
10
|
+
the runpy "found in sys.modules" warning.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
|
|
19
|
+
_LAZY = frozenset(
|
|
20
|
+
{
|
|
21
|
+
"main",
|
|
22
|
+
"build_report",
|
|
23
|
+
"is_behind",
|
|
24
|
+
"enumerate_capability_entrypoints",
|
|
25
|
+
"check_entrypoint_wiring",
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = ["__version__", *sorted(_LAZY)]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def __getattr__(name: str):
|
|
33
|
+
if name in _LAZY:
|
|
34
|
+
from qig_stack import doctor
|
|
35
|
+
|
|
36
|
+
return getattr(doctor, name)
|
|
37
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def __dir__() -> list[str]:
|
|
41
|
+
return sorted(__all__)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if TYPE_CHECKING: # static-analysis visibility without an eager runtime import
|
|
45
|
+
from qig_stack.doctor import (
|
|
46
|
+
build_report,
|
|
47
|
+
check_entrypoint_wiring,
|
|
48
|
+
enumerate_capability_entrypoints,
|
|
49
|
+
is_behind,
|
|
50
|
+
main,
|
|
51
|
+
)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
{
|
|
2
|
+
"doc": "QIG Stack Capability Contract",
|
|
3
|
+
"version": "1.01W",
|
|
4
|
+
"date": "2026-06-30",
|
|
5
|
+
"supersedes": "qig-verification:docs/current/20260619-qig-stack-capability-contract-1.00W.json (Phase-1 authored record)",
|
|
6
|
+
"principle": "Genesis-kernel deterministic self-assembly (vex-agent Core-8 / E8-root analog). The stack crystallizes into this FIXED lattice of capability slots; each slot is filled by a package primitive at latest-published version, cited to a camera-certification cert. With Phase 2 (entry-point self-registration) live, the wiring is DERIVED from installed metadata, not hand-authored.",
|
|
7
|
+
"authority": "PI directive 2026-06-19 (Phase 1) + 2026-06-30 (Phase 2/3 + born-sampler promotion); standing latest-version / step-0 lever-inventory rule (qig-verification/CLAUDE.md)",
|
|
8
|
+
"cert_registry": "qig-verification:docs/current/20260619-camera-certification-registry-1.00W.json",
|
|
9
|
+
"design_note": "qig-verification:docs/current/20260619-self-wiring-genesis-design-1.00W.md",
|
|
10
|
+
"entry_point_spec": "qig-verification:docs/current/20260619-qig-entry-point-capability-spec-1.00W.md",
|
|
11
|
+
"phase23_design": "qig-verification:docs/plans/2026-06-30-qig-stack-self-wiring-phase23-design.md",
|
|
12
|
+
"preflight": "qig_stack.doctor (console script: qig-doctor)",
|
|
13
|
+
"entry_point_group": "qig.capabilities",
|
|
14
|
+
"version_policy": {
|
|
15
|
+
"rule": "installed == latest-published (PyPI) at every launch; installed < latest is a LAUNCH BLOCKER (fail-closed, non-zero exit).",
|
|
16
|
+
"pin_exception": "Frozen reproducibility scripts keep pinned era-versions for bit-for-bit replay; this is never a reason to run a NEW launcher behind latest.",
|
|
17
|
+
"stack_packages_latest_at_authoring": {
|
|
18
|
+
"qig-compute": "0.9.2",
|
|
19
|
+
"qig-warp": "0.6.5",
|
|
20
|
+
"qig-core": "2.12.2",
|
|
21
|
+
"qig-bench": "0.1.3",
|
|
22
|
+
"qig-consciousness": "0.3.2",
|
|
23
|
+
"qig-tokenizer": "0.2.7",
|
|
24
|
+
"qigkernels": "0.4.2"
|
|
25
|
+
},
|
|
26
|
+
"note_on_latest": "These are a SNAPSHOT for reference only. qig_stack.doctor resolves latest live from PyPI at launch; do not treat this block as the source of truth -- the live query is. The qig-stack metapackage pins this exact set in pyproject.toml as the mutually-tested crystal."
|
|
27
|
+
},
|
|
28
|
+
"slot_status_legend": {
|
|
29
|
+
"CERTIFIED": "matched-cell equivalence certified once; cite the cam_id, never re-run",
|
|
30
|
+
"REFERENCE": "the exact reference path a cheap camera is certified against",
|
|
31
|
+
"PENDING": "uncertified cheap path; one-time cert must land before its results count",
|
|
32
|
+
"LEVER": "an efficiency lever (no measurement channel of its own)",
|
|
33
|
+
"CONDITIONAL": "usable only behind a controlled recheck at a calibration anchor"
|
|
34
|
+
},
|
|
35
|
+
"entry_point_gates": {
|
|
36
|
+
"under_fill": "FAIL (non-zero exit): a slot with provider_pkg set and shipped != false but no installed qig.capabilities entry-point (stale / mis-built / un-republished package).",
|
|
37
|
+
"drift": "WARN (non-fatal): an installed entry-point whose slot_id is not in the contract, or whose module:attr value differs from the slot probe.",
|
|
38
|
+
"exempt": "Slots with provider_pkg: null (engine-local, e.g. SLOT-GROUND-STATE/TeNPy) or shipped: false are exempt from under_fill."
|
|
39
|
+
},
|
|
40
|
+
"slots": [
|
|
41
|
+
{
|
|
42
|
+
"slot_id": "SLOT-GROUND-STATE",
|
|
43
|
+
"role": "prepare the lattice ground state psi that both channels read",
|
|
44
|
+
"channel": "state",
|
|
45
|
+
"provided_by": "qig-verification (engine) + TeNPy DMRG-complex",
|
|
46
|
+
"provider_pkg": null,
|
|
47
|
+
"primitive": "DMRG/MPS ground-state preparation vs ED",
|
|
48
|
+
"probe": "tenpy",
|
|
49
|
+
"cam_id": "CAM-STATE-DMRG-VS-ED",
|
|
50
|
+
"cert_status": "CERTIFIED",
|
|
51
|
+
"feasible_at": "L>=6 via MPS/DMRG"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"slot_id": "SLOT-Q-QFI",
|
|
55
|
+
"role": "quantum Fisher information (Q channel) -- exact, tensor-contractible",
|
|
56
|
+
"channel": "Q",
|
|
57
|
+
"provided_by": "qig-compute",
|
|
58
|
+
"provider_pkg": "qig-compute",
|
|
59
|
+
"primitive": "qfi.qfi_from_mps_tangent_space(psi_mps, L)",
|
|
60
|
+
"probe": "qig_compute.qfi:qfi_from_mps_tangent_space",
|
|
61
|
+
"cam_id": "CAM-QFI-MPS-TANGENT",
|
|
62
|
+
"cert_status": "CERTIFIED",
|
|
63
|
+
"feasible_at": "all L (O(chi^3) contraction, no sampling, zero error bar)",
|
|
64
|
+
"alternatives": ["CAM-QFI-DENSE-STREAMING", "CAM-QFI-BITFLIP", "CAM-QFI-SPARSE-NN", "CAM-QFI-SITE-PRUNE", "CAM-QFI-SITELOCAL"]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"slot_id": "SLOT-C-SUSCEPTIBILITY-REF",
|
|
68
|
+
"role": "classical / phase-blind Bhattacharyya fidelity-susceptibility (C channel) EXACT reference",
|
|
69
|
+
"channel": "C",
|
|
70
|
+
"provided_by": "qig-compute",
|
|
71
|
+
"provider_pkg": "qig-compute",
|
|
72
|
+
"primitive": "qfi.classical_fidelity_susceptibility(p_plus, p_minus, eps)",
|
|
73
|
+
"probe": "qig_compute.qfi:classical_fidelity_susceptibility",
|
|
74
|
+
"cam_id": "CAM-C-DENSE-REF",
|
|
75
|
+
"cert_status": "REFERENCE",
|
|
76
|
+
"feasible_at": "L<=5 ONLY (dense Born p over 2^N states; L=6 -> ~1.1 TB, infeasible)"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"slot_id": "SLOT-C-BORN-SAMPLER",
|
|
80
|
+
"role": "MPS Born-sampler -- the L=6-feasible C path (CERTIFIED via EXP-122 2026-06-19; PROMOTED to qig-compute 2026-06-30)",
|
|
81
|
+
"channel": "C",
|
|
82
|
+
"provided_by": "qig-compute (promoted from EXP-122 launcher born_C_score, byte-identical numerics)",
|
|
83
|
+
"provider_pkg": "qig-compute",
|
|
84
|
+
"primitive": "sampling.born_classical_fisher_score(psi_base, psi_p, psi_m, eps, M, seed): x ~ p(x) from MPS perfect (Ferris-Vidal) sampling, O(N chi^2)/sample; C = E_{x~p_base}[((log p+ - log p-)/2eps)^2] (PHASE-FREE classical-Fisher SCORE, estimator (c)). The Bhattacharyya form (estimator (a)) FAILS the 1/eps^2 variance test -- (c) is the certified estimator.",
|
|
85
|
+
"probe": "qig_compute.sampling:born_classical_fisher_score",
|
|
86
|
+
"cam_id": "CAM-C-BORN-MPS",
|
|
87
|
+
"cert_status": "CERTIFIED",
|
|
88
|
+
"shipped": true,
|
|
89
|
+
"promoted": "2026-06-30 from qig-verification/scripts/exp122_dmrg_complex.py:born_C_score (byte-identical). The frozen launcher keeps its inline copy for bit-for-bit reproducibility of the L=5 cert.",
|
|
90
|
+
"feasible_at": "L=6 (CERTIFIED at L=5: EXP-122 L=5 cert results/exp122/20260619_exp122_L5_born_cert.json @566b245 -- M=150k Delta=0.000373 bias PASS, sigma_D=0.00332 < D_sep/3=0.00473 variance PASS)."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"slot_id": "SLOT-SCREENING",
|
|
94
|
+
"role": "site pruning / screening lever (Yukawa shell cutoff)",
|
|
95
|
+
"channel": "lever",
|
|
96
|
+
"provided_by": "qig-warp",
|
|
97
|
+
"provider_pkg": "qig-warp",
|
|
98
|
+
"primitive": "prune_sites / screening_cutoff",
|
|
99
|
+
"probe": "qig_warp.screening:prune_sites",
|
|
100
|
+
"cam_id": "CAM-QFI-SITE-PRUNE",
|
|
101
|
+
"cert_status": "CERTIFIED"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"slot_id": "SLOT-EARLY-STOP",
|
|
105
|
+
"role": "convergence early-stop across the chi sub-ladder (8/16/24/32)",
|
|
106
|
+
"channel": "lever",
|
|
107
|
+
"provided_by": "qig-warp",
|
|
108
|
+
"provider_pkg": "qig-warp",
|
|
109
|
+
"primitive": "convergence.check_ci_stabilized / find_early_stop_point",
|
|
110
|
+
"probe": "qig_warp.convergence:check_ci_stabilized",
|
|
111
|
+
"lever_id": "LEV-CONVERGENCE-EARLYSTOP",
|
|
112
|
+
"cert_status": "LEVER"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"slot_id": "SLOT-RUNTIME-PRICING",
|
|
116
|
+
"role": "spend preflight before heavy launches",
|
|
117
|
+
"channel": "lever",
|
|
118
|
+
"provided_by": "qig-warp + chi-ladder pace-check",
|
|
119
|
+
"provider_pkg": "qig-warp",
|
|
120
|
+
"primitive": "bridge.predict_runtime (J-scaling) / chi-ladder pace-check",
|
|
121
|
+
"probe": "qig_warp.bridge:predict_runtime",
|
|
122
|
+
"lever_id": "LEV-CHI-PACE-CHECK",
|
|
123
|
+
"cert_status": "LEVER",
|
|
124
|
+
"caveat": "predict_runtime is J-scaling and is WRONG for L/chi-BOUND jobs at fixed J (EXP-122). For those, use the chi-ladder pace-check (LEV-CHI-PACE-CHECK)."
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"slot_id": "SLOT-BLAS-PIN",
|
|
128
|
+
"role": "deterministic BLAS threading (API, not raw env-vars)",
|
|
129
|
+
"channel": "lever",
|
|
130
|
+
"provided_by": "qig-compute",
|
|
131
|
+
"provider_pkg": "qig-compute",
|
|
132
|
+
"primitive": "pin_blas_threads / single_thread_blas",
|
|
133
|
+
"probe": "qig_compute.blas:pin_blas_threads",
|
|
134
|
+
"lever_id": "LEV-BLAS-PIN",
|
|
135
|
+
"cert_status": "LEVER"
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
"how_to_use": "Step-0 of every launcher: run `qig-doctor` (from the qig-stack package; qig-verification/scripts/qig_doctor.py is a back-compat shim). It (1) fails closed if any stack package is behind PyPI-latest, (2) fails closed on under-filled slots (a shipped provider that did not self-register its entry-point), and (3) enumerates which slots are filled by what is installed -- the automated Gate-A lever inventory. An experiment then cites the cam_id/lever_id for each slot it uses and never re-runs a matched-cell gate.",
|
|
139
|
+
"roadmap": {
|
|
140
|
+
"phase_1": "DONE -- fixed lattice (this contract) + qig_doctor.py fail-closed version preflight (qig-verification).",
|
|
141
|
+
"phase_2": "DONE 2026-06-30 -- each package self-registers its slots via [project.entry-points.'qig.capabilities'] (qig-compute, qig-warp); qig_stack.doctor derives wiring from entry-points (Tier-A metadata-only) with under-fill (FAIL) + drift (WARN) gates.",
|
|
142
|
+
"phase_3": "DONE 2026-06-30 -- qig-stack metapackage pins the mutually-tested latest set, houses the doctor + this contract, ships the qig-doctor console script; CI runs contract schema + doctor unit tests (no network).",
|
|
143
|
+
"born_sampler_promotion": "DONE 2026-06-30 -- SLOT-C-BORN-SAMPLER shipped:false -> true; born_C_score promoted to qig_compute.sampling.born_classical_fisher_score (byte-identical), gains an entry-point. CAM-C-BORN-MPS stays CERTIFIED.",
|
|
144
|
+
"activation": "entry-points go live in the installed crystal on each package's next PyPI release; cut qig-compute / qig-warp / qig-stack releases to activate (PI greenlight)."
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""qig_stack.doctor -- deterministic launch preflight (Genesis-kernel self-assembly check).
|
|
3
|
+
|
|
4
|
+
Step-0 of every launcher. Three jobs, all fail-closed:
|
|
5
|
+
|
|
6
|
+
1. VERSION GATE -- every stack package must be installed at PyPI-latest.
|
|
7
|
+
installed < latest is a LAUNCH BLOCKER (non-zero exit), per
|
|
8
|
+
qig-verification/CLAUDE.md "Packages: latest version, always optimised".
|
|
9
|
+
|
|
10
|
+
2. WIRING GATE (Phase 2) -- enumerate the `qig.capabilities` entry-points the
|
|
11
|
+
installed packages self-register (Tier A: metadata-only, NO import on the
|
|
12
|
+
fail-closed path) and cross-check them against the capability contract:
|
|
13
|
+
- UNDER-FILL -> FAIL (non-zero exit): a slot whose provider package is
|
|
14
|
+
shipped but did not register its entry-point (stale / mis-built package).
|
|
15
|
+
- DRIFT -> WARN: an installed entry-point the contract does not know, or
|
|
16
|
+
one whose module:attr moved off the contract probe.
|
|
17
|
+
ACTIVATION GRACE: if NO `qig.capabilities` entry-point is installed at all,
|
|
18
|
+
the feature is not yet activated in this venv (the providers have not
|
|
19
|
+
republished with entry-points) -- the gate reports PENDING and PASSES rather
|
|
20
|
+
than false-blocking every launch. Once any provider registers, the gate is
|
|
21
|
+
strict: a missing expected slot is then a real UNDER-FILL.
|
|
22
|
+
|
|
23
|
+
3. WIRING REPORT -- which contract slots are filled by what is installed (the
|
|
24
|
+
automated Gate-A lever inventory).
|
|
25
|
+
|
|
26
|
+
The contract (bundled as `qig_stack/contract.json`) is the fixed lattice the
|
|
27
|
+
stack crystallizes into; this preflight checks the crystal formed correctly
|
|
28
|
+
before any compute spend.
|
|
29
|
+
|
|
30
|
+
Usage:
|
|
31
|
+
qig-doctor # full preflight, exits 1 if stale OR under-filled
|
|
32
|
+
qig-doctor --no-network # skip PyPI (contract snapshot + wiring only)
|
|
33
|
+
qig-doctor --json # machine-readable report
|
|
34
|
+
qig-doctor --probe # also import-check each entry-point resolves (off the gate)
|
|
35
|
+
qig-doctor --deep # also call Tier-B describe() descriptors (imports packages)
|
|
36
|
+
qig-doctor --contract PATH # override the bundled contract (testing)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import argparse
|
|
42
|
+
import importlib
|
|
43
|
+
import importlib.metadata as importlib_metadata
|
|
44
|
+
import json
|
|
45
|
+
import os
|
|
46
|
+
import sys
|
|
47
|
+
import urllib.error
|
|
48
|
+
import urllib.request
|
|
49
|
+
from dataclasses import asdict, dataclass, field
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
|
|
52
|
+
try: # PEP 440-correct version comparison; `packaging` is a declared dependency.
|
|
53
|
+
from packaging.version import InvalidVersion, Version
|
|
54
|
+
|
|
55
|
+
_HAVE_PACKAGING = True
|
|
56
|
+
except ImportError: # pragma: no cover - defensive; packaging is in [project.dependencies]
|
|
57
|
+
_HAVE_PACKAGING = False
|
|
58
|
+
|
|
59
|
+
CAPABILITY_GROUP = "qig.capabilities"
|
|
60
|
+
PYPI_TIMEOUT_S = 10
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _contract_path(override: str | Path | None = None) -> Path:
|
|
64
|
+
"""Locate the capability contract: explicit override > env var > bundled copy."""
|
|
65
|
+
if override:
|
|
66
|
+
return Path(override)
|
|
67
|
+
env = os.environ.get("QIG_STACK_CONTRACT")
|
|
68
|
+
if env:
|
|
69
|
+
return Path(env)
|
|
70
|
+
return Path(__file__).resolve().parent / "contract.json"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def latest_pypi_version(package: str, timeout: float = PYPI_TIMEOUT_S) -> str | None:
|
|
74
|
+
"""Return the latest published version on PyPI, or None if unreachable.
|
|
75
|
+
|
|
76
|
+
Raises _PyPIAbsent on a definitive HTTP 404 (package/version genuinely not on
|
|
77
|
+
PyPI -- yanked or renamed), so the caller can distinguish that from a transient
|
|
78
|
+
network blip (which returns None -> fail-open).
|
|
79
|
+
"""
|
|
80
|
+
url = f"https://pypi.org/pypi/{package}/json"
|
|
81
|
+
try:
|
|
82
|
+
with urllib.request.urlopen(url, timeout=timeout) as resp:
|
|
83
|
+
payload = json.load(resp)
|
|
84
|
+
except urllib.error.HTTPError as exc:
|
|
85
|
+
if exc.code == 404:
|
|
86
|
+
raise _PyPIAbsent(package) from exc
|
|
87
|
+
return None
|
|
88
|
+
except (urllib.error.URLError, TimeoutError, ValueError):
|
|
89
|
+
return None
|
|
90
|
+
info = payload.get("info", {})
|
|
91
|
+
version = info.get("version")
|
|
92
|
+
return version if isinstance(version, str) else None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class _PyPIAbsent(Exception):
|
|
96
|
+
"""Definitive 'no such package/version on PyPI' (HTTP 404)."""
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def installed_version(package: str) -> str | None:
|
|
100
|
+
try:
|
|
101
|
+
return importlib_metadata.version(package)
|
|
102
|
+
except importlib_metadata.PackageNotFoundError:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _parse_version(text: str) -> tuple[int, ...]:
|
|
107
|
+
"""Zero-padding-agnostic release tuple (fallback only; drops local/pre/dev tails)."""
|
|
108
|
+
release = text.split("+", 1)[0]
|
|
109
|
+
parts: list[int] = []
|
|
110
|
+
for chunk in release.split("."):
|
|
111
|
+
digits = "".join(ch for ch in chunk if ch.isdigit())
|
|
112
|
+
if digits == chunk and digits: # pure numeric release component
|
|
113
|
+
parts.append(int(digits))
|
|
114
|
+
else: # a pre/post/dev tail (e.g. '1rc2', 'dev0') ends the release tuple
|
|
115
|
+
break
|
|
116
|
+
return tuple(parts)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def is_behind(installed: str, latest: str) -> bool:
|
|
120
|
+
"""True iff `installed` is an older version than `latest` (PEP 440-correct)."""
|
|
121
|
+
if _HAVE_PACKAGING:
|
|
122
|
+
try:
|
|
123
|
+
return Version(installed) < Version(latest)
|
|
124
|
+
except InvalidVersion: # pragma: no cover - non-PEP440 string
|
|
125
|
+
pass
|
|
126
|
+
a, b = _parse_version(installed), _parse_version(latest)
|
|
127
|
+
n = max(len(a), len(b))
|
|
128
|
+
a += (0,) * (n - len(a))
|
|
129
|
+
b += (0,) * (n - len(b))
|
|
130
|
+
return a < b
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# ───────────────────────── Phase-2: entry-point self-registration ─────────────────────────
|
|
134
|
+
def enumerate_capability_entrypoints() -> dict[str, str]:
|
|
135
|
+
"""Tier A (CANONICAL): name -> "module:attr" for the qig.capabilities group.
|
|
136
|
+
|
|
137
|
+
Metadata-only -- enumerates DECLARATIONS without importing any package, so it
|
|
138
|
+
can never run package code and cannot inject non-determinism into a launch.
|
|
139
|
+
Requires the Python 3.10+ selectable entry_points(group=) API (requires-python>=3.10).
|
|
140
|
+
"""
|
|
141
|
+
return {ep.name: ep.value for ep in importlib_metadata.entry_points(group=CAPABILITY_GROUP)}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@dataclass
|
|
145
|
+
class WiringIssue:
|
|
146
|
+
slot_id: str
|
|
147
|
+
kind: str # UNDER_FILL | DRIFT_UNKNOWN | DRIFT_MOVED
|
|
148
|
+
detail: str
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def check_entrypoint_wiring(
|
|
152
|
+
slots: list[dict], declared: dict[str, str]
|
|
153
|
+
) -> tuple[list[WiringIssue], list[WiringIssue]]:
|
|
154
|
+
"""Cross-check declared qig.capabilities entry-points against the contract lattice.
|
|
155
|
+
|
|
156
|
+
Returns (underfill, drift). Pure function -- `declared` is injected so this is
|
|
157
|
+
unit-testable without a live environment.
|
|
158
|
+
- UNDER_FILL (FAIL): a slot with provider_pkg set and shipped != false but no
|
|
159
|
+
declared entry-point -- ONLY when the feature is activated (declared is
|
|
160
|
+
non-empty). An empty `declared` means the feature is not yet live in this
|
|
161
|
+
venv (providers not republished) -> no under-fill (see build_report's
|
|
162
|
+
wiring_activation = PENDING).
|
|
163
|
+
- DRIFT (WARN): a declared entry-point whose slot_id is not in the contract
|
|
164
|
+
(DRIFT_UNKNOWN), or whose value differs from the slot probe (DRIFT_MOVED).
|
|
165
|
+
"""
|
|
166
|
+
underfill: list[WiringIssue] = []
|
|
167
|
+
drift: list[WiringIssue] = []
|
|
168
|
+
by_id = {s["slot_id"]: s for s in slots}
|
|
169
|
+
group_active = bool(declared)
|
|
170
|
+
|
|
171
|
+
if group_active:
|
|
172
|
+
for slot in slots:
|
|
173
|
+
provider = slot.get("provider_pkg")
|
|
174
|
+
shipped = slot.get("shipped", True)
|
|
175
|
+
expected = provider is not None and shipped is not False
|
|
176
|
+
if expected and slot["slot_id"] not in declared:
|
|
177
|
+
underfill.append(
|
|
178
|
+
WiringIssue(
|
|
179
|
+
slot["slot_id"],
|
|
180
|
+
"UNDER_FILL",
|
|
181
|
+
f"{provider} ships this slot but registered no '{CAPABILITY_GROUP}' "
|
|
182
|
+
f"entry-point (probe {slot.get('probe')!r}); stale or mis-built package.",
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
for name, value in declared.items():
|
|
187
|
+
slot = by_id.get(name)
|
|
188
|
+
if slot is None:
|
|
189
|
+
drift.append(
|
|
190
|
+
WiringIssue(name, "DRIFT_UNKNOWN", f"entry-point '{name}={value}' is not a contract slot_id")
|
|
191
|
+
)
|
|
192
|
+
elif slot.get("probe") and value != slot["probe"]:
|
|
193
|
+
drift.append(
|
|
194
|
+
WiringIssue(
|
|
195
|
+
name,
|
|
196
|
+
"DRIFT_MOVED",
|
|
197
|
+
f"entry-point value {value!r} != contract probe {slot['probe']!r}",
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
return underfill, drift
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _expected_slot_ids(slots: list[dict]) -> list[str]:
|
|
205
|
+
return [
|
|
206
|
+
s["slot_id"]
|
|
207
|
+
for s in slots
|
|
208
|
+
if s.get("provider_pkg") is not None and s.get("shipped", True) is not False
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@dataclass
|
|
213
|
+
class PackageCheck:
|
|
214
|
+
package: str
|
|
215
|
+
installed: str | None
|
|
216
|
+
latest: str | None
|
|
217
|
+
status: str # OK | STALE | MISSING | LATEST_UNKNOWN | PYPI_ABSENT
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@dataclass
|
|
221
|
+
class SlotCheck:
|
|
222
|
+
slot_id: str
|
|
223
|
+
cite: str
|
|
224
|
+
cert_status: str
|
|
225
|
+
provided_by: str
|
|
226
|
+
filled: str # FILLED | MISSING | UNSHIPPED | PKG_ABSENT
|
|
227
|
+
entrypoint: str # DECLARED | ABSENT | DRIFT | EXEMPT | UNSHIPPED | PENDING
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@dataclass
|
|
231
|
+
class Report:
|
|
232
|
+
contract: str
|
|
233
|
+
packages: list[PackageCheck] = field(default_factory=list)
|
|
234
|
+
slots: list[SlotCheck] = field(default_factory=list)
|
|
235
|
+
entrypoint_underfill: list[WiringIssue] = field(default_factory=list)
|
|
236
|
+
entrypoint_drift: list[WiringIssue] = field(default_factory=list)
|
|
237
|
+
wiring_activation: str = "N/A" # ACTIVE | PENDING | N/A
|
|
238
|
+
version_gate_passed: bool = True
|
|
239
|
+
wiring_gate_passed: bool = True
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def check_packages(latest_versions: dict[str, str], use_network: bool) -> list[PackageCheck]:
|
|
243
|
+
checks: list[PackageCheck] = []
|
|
244
|
+
for package in sorted(latest_versions):
|
|
245
|
+
installed = installed_version(package)
|
|
246
|
+
if use_network:
|
|
247
|
+
try:
|
|
248
|
+
latest = latest_pypi_version(package)
|
|
249
|
+
except _PyPIAbsent:
|
|
250
|
+
checks.append(PackageCheck(package, installed, None, "PYPI_ABSENT"))
|
|
251
|
+
continue
|
|
252
|
+
else:
|
|
253
|
+
latest = latest_versions[package]
|
|
254
|
+
if installed is None:
|
|
255
|
+
status = "MISSING"
|
|
256
|
+
elif latest is None:
|
|
257
|
+
status = "LATEST_UNKNOWN"
|
|
258
|
+
elif is_behind(installed, latest):
|
|
259
|
+
status = "STALE"
|
|
260
|
+
else:
|
|
261
|
+
status = "OK"
|
|
262
|
+
checks.append(PackageCheck(package, installed, latest, status))
|
|
263
|
+
return checks
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _module_importable(probe: str | None) -> bool:
|
|
267
|
+
if not probe:
|
|
268
|
+
return False
|
|
269
|
+
top = probe.partition(":")[0].split(".")[0]
|
|
270
|
+
try:
|
|
271
|
+
importlib.import_module(top)
|
|
272
|
+
return True
|
|
273
|
+
except ImportError:
|
|
274
|
+
return False
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _probe_filled(probe: str | None) -> bool:
|
|
278
|
+
"""A slot is filled if its probe imports. `module:attr` also checks the attr."""
|
|
279
|
+
if not probe:
|
|
280
|
+
return False
|
|
281
|
+
module_name, _, attr = probe.partition(":")
|
|
282
|
+
try:
|
|
283
|
+
module = importlib.import_module(module_name)
|
|
284
|
+
except ImportError:
|
|
285
|
+
return False
|
|
286
|
+
if attr:
|
|
287
|
+
return hasattr(module, attr)
|
|
288
|
+
return True
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def check_slots(slots: list[dict], declared: dict[str, str]) -> list[SlotCheck]:
|
|
292
|
+
checks: list[SlotCheck] = []
|
|
293
|
+
group_active = bool(declared)
|
|
294
|
+
for slot in slots:
|
|
295
|
+
cite = slot.get("cam_id") or slot.get("lever_id") or "(uncited)"
|
|
296
|
+
provided_by = slot.get("provided_by", "")
|
|
297
|
+
probe = slot.get("probe")
|
|
298
|
+
provider = slot.get("provider_pkg")
|
|
299
|
+
shipped = slot.get("shipped", True)
|
|
300
|
+
|
|
301
|
+
# UNSHIPPED is a PACKAGE/wiring status, orthogonal to cert_status.
|
|
302
|
+
if shipped is False or (slot.get("cert_status") == "PENDING" and probe is None):
|
|
303
|
+
filled = "UNSHIPPED"
|
|
304
|
+
elif _probe_filled(probe):
|
|
305
|
+
filled = "FILLED"
|
|
306
|
+
elif _module_importable(probe):
|
|
307
|
+
filled = "MISSING" # package present, symbol gone
|
|
308
|
+
else:
|
|
309
|
+
filled = "PKG_ABSENT"
|
|
310
|
+
|
|
311
|
+
# entry-point dimension (Phase 2)
|
|
312
|
+
if provider is None:
|
|
313
|
+
entrypoint = "EXEMPT"
|
|
314
|
+
elif shipped is False:
|
|
315
|
+
entrypoint = "UNSHIPPED"
|
|
316
|
+
elif slot["slot_id"] in declared:
|
|
317
|
+
entrypoint = "DRIFT" if (probe and declared[slot["slot_id"]] != probe) else "DECLARED"
|
|
318
|
+
elif group_active:
|
|
319
|
+
entrypoint = "ABSENT"
|
|
320
|
+
else:
|
|
321
|
+
entrypoint = "PENDING" # feature not yet activated in this venv
|
|
322
|
+
|
|
323
|
+
checks.append(
|
|
324
|
+
SlotCheck(
|
|
325
|
+
slot_id=slot["slot_id"],
|
|
326
|
+
cite=cite,
|
|
327
|
+
cert_status=slot.get("cert_status", ""),
|
|
328
|
+
provided_by=provided_by,
|
|
329
|
+
filled=filled,
|
|
330
|
+
entrypoint=entrypoint,
|
|
331
|
+
)
|
|
332
|
+
)
|
|
333
|
+
return checks
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class ContractError(Exception):
|
|
337
|
+
"""The capability contract is missing, unreadable, or malformed."""
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _load_contract(contract_path: Path) -> dict:
|
|
341
|
+
try:
|
|
342
|
+
data = json.loads(Path(contract_path).read_text())
|
|
343
|
+
except FileNotFoundError as exc:
|
|
344
|
+
raise ContractError(f"contract not found at {contract_path}") from exc
|
|
345
|
+
except json.JSONDecodeError as exc:
|
|
346
|
+
raise ContractError(f"contract at {contract_path} is not valid JSON: {exc}") from exc
|
|
347
|
+
if "slots" not in data or "version_policy" not in data:
|
|
348
|
+
raise ContractError(
|
|
349
|
+
f"contract at {contract_path} missing required keys ('slots', 'version_policy')"
|
|
350
|
+
)
|
|
351
|
+
try:
|
|
352
|
+
data["version_policy"]["stack_packages_latest_at_authoring"]
|
|
353
|
+
except (KeyError, TypeError) as exc:
|
|
354
|
+
raise ContractError(
|
|
355
|
+
f"contract at {contract_path} missing version_policy.stack_packages_latest_at_authoring"
|
|
356
|
+
) from exc
|
|
357
|
+
return data
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def build_report(
|
|
361
|
+
contract_path: Path,
|
|
362
|
+
use_network: bool,
|
|
363
|
+
declared: dict[str, str] | None = None,
|
|
364
|
+
) -> Report:
|
|
365
|
+
contract = _load_contract(Path(contract_path))
|
|
366
|
+
latest_versions = contract["version_policy"]["stack_packages_latest_at_authoring"]
|
|
367
|
+
slots = contract["slots"]
|
|
368
|
+
if declared is None:
|
|
369
|
+
declared = enumerate_capability_entrypoints()
|
|
370
|
+
|
|
371
|
+
report = Report(contract=str(contract_path))
|
|
372
|
+
report.packages = check_packages(latest_versions, use_network)
|
|
373
|
+
report.slots = check_slots(slots, declared)
|
|
374
|
+
report.entrypoint_underfill, report.entrypoint_drift = check_entrypoint_wiring(slots, declared)
|
|
375
|
+
|
|
376
|
+
if not _expected_slot_ids(slots):
|
|
377
|
+
report.wiring_activation = "N/A"
|
|
378
|
+
elif declared:
|
|
379
|
+
report.wiring_activation = "ACTIVE"
|
|
380
|
+
else:
|
|
381
|
+
report.wiring_activation = "PENDING"
|
|
382
|
+
|
|
383
|
+
report.version_gate_passed = all(
|
|
384
|
+
pkg.status in ("OK", "LATEST_UNKNOWN", "PYPI_ABSENT") for pkg in report.packages
|
|
385
|
+
)
|
|
386
|
+
report.wiring_gate_passed = not report.entrypoint_underfill
|
|
387
|
+
return report
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def probe_entrypoints(declared: dict[str, str]) -> dict[str, bool]:
|
|
391
|
+
"""Optional `--probe`: import-check that each entry-point module:attr resolves.
|
|
392
|
+
|
|
393
|
+
This IMPORTS packages, so it is OFF the fail-closed gate -- a diagnostic only.
|
|
394
|
+
"""
|
|
395
|
+
return {name: _probe_filled(value) for name, value in declared.items()}
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def deep_descriptors() -> list[dict]:
|
|
399
|
+
"""Optional Tier-B `--deep`: call any `_descriptor` describe() entry-points.
|
|
400
|
+
|
|
401
|
+
IMPORTS + EXECUTES package code; FORBIDDEN on the fail-closed gate, behind --deep only.
|
|
402
|
+
"""
|
|
403
|
+
out: list[dict] = []
|
|
404
|
+
for ep in importlib_metadata.entry_points(group=CAPABILITY_GROUP):
|
|
405
|
+
if ep.name != "_descriptor":
|
|
406
|
+
continue
|
|
407
|
+
try:
|
|
408
|
+
result = ep.load()()
|
|
409
|
+
if isinstance(result, list):
|
|
410
|
+
out.extend(result)
|
|
411
|
+
except Exception as exc: # noqa: BLE001 - diagnostic, never gates
|
|
412
|
+
out.append({"error": f"{ep.value}: {exc}"})
|
|
413
|
+
return out
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
_GLYPH = {
|
|
417
|
+
"OK": "ok ",
|
|
418
|
+
"STALE": "STALE",
|
|
419
|
+
"MISSING": "MISSING",
|
|
420
|
+
"LATEST_UNKNOWN": "lat?",
|
|
421
|
+
"PYPI_ABSENT": "404!",
|
|
422
|
+
"FILLED": "filled",
|
|
423
|
+
"UNSHIPPED": "UNSHIPPED",
|
|
424
|
+
"PKG_ABSENT": "pkg-absent",
|
|
425
|
+
"DECLARED": "ep-ok",
|
|
426
|
+
"ABSENT": "ep-ABSENT",
|
|
427
|
+
"DRIFT": "ep-DRIFT",
|
|
428
|
+
"EXEMPT": "exempt",
|
|
429
|
+
"PENDING": "ep-pending",
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def print_report(report: Report, use_network: bool) -> None:
|
|
434
|
+
print(f"qig-doctor -- preflight against {report.contract}")
|
|
435
|
+
print(f" version source: {'PyPI (live)' if use_network else 'contract snapshot'}\n")
|
|
436
|
+
print(" VERSION GATE (installed must equal latest-published):")
|
|
437
|
+
for pkg in report.packages:
|
|
438
|
+
print(
|
|
439
|
+
f" [{_GLYPH[pkg.status]:>6}] {pkg.package:<18}"
|
|
440
|
+
f" installed={pkg.installed or '-':<28} latest={pkg.latest or '-'}"
|
|
441
|
+
)
|
|
442
|
+
print(f"\n WIRING REPORT (slots -> installed / entry-point) [activation: {report.wiring_activation}]:")
|
|
443
|
+
for slot in report.slots:
|
|
444
|
+
print(
|
|
445
|
+
f" [{_GLYPH[slot.filled]:>10}|{_GLYPH.get(slot.entrypoint, slot.entrypoint):>10}]"
|
|
446
|
+
f" {slot.slot_id:<26} {slot.cite:<22} ({slot.cert_status}) via {slot.provided_by}"
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
if report.entrypoint_drift:
|
|
450
|
+
print("\n WIRING DRIFT (WARN -- contract bump may be needed):")
|
|
451
|
+
for issue in report.entrypoint_drift:
|
|
452
|
+
print(f" [{issue.kind}] {issue.slot_id}: {issue.detail}")
|
|
453
|
+
|
|
454
|
+
absent = [p.package for p in report.packages if p.status == "PYPI_ABSENT"]
|
|
455
|
+
if absent:
|
|
456
|
+
print(f"\n NOTE: PyPI returned 404 for: {', '.join(absent)} (yanked/renamed? -- could not verify latest)")
|
|
457
|
+
|
|
458
|
+
stale = [p.package for p in report.packages if p.status in ("STALE", "MISSING")]
|
|
459
|
+
print()
|
|
460
|
+
if report.version_gate_passed:
|
|
461
|
+
print(" VERSION GATE: PASS")
|
|
462
|
+
else:
|
|
463
|
+
print(f" VERSION GATE: FAIL -- stale/missing: {', '.join(stale)}")
|
|
464
|
+
print(" -> LAUNCH BLOCKED. Update to latest published before launching:")
|
|
465
|
+
print(f" uv pip install -U {' '.join(stale)}")
|
|
466
|
+
|
|
467
|
+
if report.wiring_activation == "PENDING":
|
|
468
|
+
print(" WIRING GATE: PENDING ACTIVATION -- no qig.capabilities entry-points installed")
|
|
469
|
+
print(" (providers not yet republished with entry-points; not a blocker).")
|
|
470
|
+
elif report.wiring_gate_passed:
|
|
471
|
+
print(" WIRING GATE: PASS")
|
|
472
|
+
else:
|
|
473
|
+
under = ", ".join(i.slot_id for i in report.entrypoint_underfill)
|
|
474
|
+
print(f" WIRING GATE: FAIL -- under-filled slots: {under}")
|
|
475
|
+
print(" -> LAUNCH BLOCKED. A shipped provider did not self-register its")
|
|
476
|
+
print(" qig.capabilities entry-point (stale / mis-built package).")
|
|
477
|
+
for issue in report.entrypoint_underfill:
|
|
478
|
+
print(f" - {issue.slot_id}: {issue.detail}")
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def main(argv: list[str] | None = None) -> int:
|
|
482
|
+
parser = argparse.ArgumentParser(
|
|
483
|
+
description="qig launch preflight (version gate + entry-point wiring gate + report)"
|
|
484
|
+
)
|
|
485
|
+
parser.add_argument(
|
|
486
|
+
"--no-network", action="store_true", help="skip PyPI; use contract snapshot"
|
|
487
|
+
)
|
|
488
|
+
parser.add_argument("--json", action="store_true", help="emit machine-readable JSON")
|
|
489
|
+
parser.add_argument(
|
|
490
|
+
"--probe", action="store_true", help="import-check each entry-point resolves (off the gate)"
|
|
491
|
+
)
|
|
492
|
+
parser.add_argument(
|
|
493
|
+
"--deep", action="store_true", help="call Tier-B describe() descriptors (imports packages)"
|
|
494
|
+
)
|
|
495
|
+
parser.add_argument("--contract", default=None, help="override the bundled contract path")
|
|
496
|
+
args = parser.parse_args(argv)
|
|
497
|
+
|
|
498
|
+
use_network = not args.no_network
|
|
499
|
+
try:
|
|
500
|
+
report = build_report(_contract_path(args.contract), use_network)
|
|
501
|
+
except ContractError as exc:
|
|
502
|
+
sys.stderr.write(f"qig-doctor: {exc}\n -> LAUNCH BLOCKED (preflight fail-closed).\n")
|
|
503
|
+
return 1
|
|
504
|
+
|
|
505
|
+
extras: dict[str, object] = {}
|
|
506
|
+
if args.probe:
|
|
507
|
+
extras["entrypoint_probe"] = probe_entrypoints(enumerate_capability_entrypoints())
|
|
508
|
+
if args.deep:
|
|
509
|
+
extras["descriptors"] = deep_descriptors()
|
|
510
|
+
|
|
511
|
+
if args.json:
|
|
512
|
+
payload = asdict(report)
|
|
513
|
+
payload.update(extras)
|
|
514
|
+
print(json.dumps(payload, indent=2))
|
|
515
|
+
else:
|
|
516
|
+
print_report(report, use_network)
|
|
517
|
+
if "entrypoint_probe" in extras:
|
|
518
|
+
print("\n ENTRY-POINT PROBE (import resolve-check):")
|
|
519
|
+
for name, ok in extras["entrypoint_probe"].items(): # type: ignore[union-attr]
|
|
520
|
+
print(f" [{'ok ' if ok else 'FAIL':>4}] {name}")
|
|
521
|
+
if "descriptors" in extras:
|
|
522
|
+
print("\n TIER-B DESCRIPTORS (--deep):")
|
|
523
|
+
for d in extras["descriptors"]: # type: ignore[union-attr]
|
|
524
|
+
print(f" {d}")
|
|
525
|
+
|
|
526
|
+
return 0 if (report.version_gate_passed and report.wiring_gate_passed) else 1
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
if __name__ == "__main__":
|
|
530
|
+
sys.exit(main())
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Schema tests for the bundled QIG Stack Capability Contract (no network)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
CONTRACT = Path(__file__).resolve().parent.parent / "src" / "qig_stack" / "contract.json"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _load() -> dict:
|
|
12
|
+
return json.loads(CONTRACT.read_text())
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_contract_present():
|
|
16
|
+
assert CONTRACT.exists(), "bundled contract.json missing from src/qig_stack/"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_contract_top_level_schema():
|
|
20
|
+
data = _load()
|
|
21
|
+
for key in ("doc", "version", "version_policy", "slots", "entry_point_group"):
|
|
22
|
+
assert key in data, f"contract missing top-level key: {key}"
|
|
23
|
+
assert data["entry_point_group"] == "qig.capabilities"
|
|
24
|
+
latest = data["version_policy"]["stack_packages_latest_at_authoring"]
|
|
25
|
+
for pkg in ("qig-compute", "qig-warp", "qig-core", "qig-bench"):
|
|
26
|
+
assert pkg in latest, f"version snapshot missing {pkg}"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_slots_well_formed():
|
|
30
|
+
data = _load()
|
|
31
|
+
seen: set[str] = set()
|
|
32
|
+
for slot in data["slots"]:
|
|
33
|
+
sid = slot["slot_id"]
|
|
34
|
+
assert sid not in seen, f"duplicate slot_id {sid}"
|
|
35
|
+
seen.add(sid)
|
|
36
|
+
assert "cert_status" in slot
|
|
37
|
+
assert slot.get("cam_id") or slot.get("lever_id"), f"{sid} cites no cam/lever id"
|
|
38
|
+
# provider_pkg is mandatory (may be null for engine-local slots)
|
|
39
|
+
assert "provider_pkg" in slot, f"{sid} missing provider_pkg"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_provider_slots_have_full_module_attr_probe():
|
|
43
|
+
"""Every PyPI-provided, shipped slot must carry a module:attr probe == its entry-point."""
|
|
44
|
+
data = _load()
|
|
45
|
+
for slot in data["slots"]:
|
|
46
|
+
if slot.get("provider_pkg") and slot.get("shipped", True) is not False:
|
|
47
|
+
probe = slot.get("probe", "")
|
|
48
|
+
assert ":" in probe, f"{slot['slot_id']} probe {probe!r} is not module:attr"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_born_sampler_promoted():
|
|
52
|
+
data = _load()
|
|
53
|
+
born = next(s for s in data["slots"] if s["slot_id"] == "SLOT-C-BORN-SAMPLER")
|
|
54
|
+
assert born["shipped"] is True, "born sampler should be shipped after promotion"
|
|
55
|
+
assert born["provider_pkg"] == "qig-compute"
|
|
56
|
+
assert born["probe"] == "qig_compute.sampling:born_classical_fisher_score"
|
|
57
|
+
assert born["cert_status"] == "CERTIFIED"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_ground_state_is_engine_local():
|
|
61
|
+
data = _load()
|
|
62
|
+
gs = next(s for s in data["slots"] if s["slot_id"] == "SLOT-GROUND-STATE")
|
|
63
|
+
assert gs["provider_pkg"] is None, "ground-state slot is engine-local (no PyPI entry-point)"
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Unit tests for qig_stack.doctor -- version compare, slot fill, entry-point cross-check.
|
|
2
|
+
|
|
3
|
+
Network-free: the entry-point wiring is exercised by INJECTING a `declared` dict, so
|
|
4
|
+
these tests do not depend on what is installed in the environment.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
from qig_stack import doctor
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _contract_slots() -> list[dict]:
|
|
15
|
+
return json.loads(doctor._contract_path().read_text())["slots"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _all_expected_declared() -> dict[str, str]:
|
|
19
|
+
"""The fully-wired crystal: every shipped, PyPI-provided slot declared at its probe."""
|
|
20
|
+
return {
|
|
21
|
+
s["slot_id"]: s["probe"]
|
|
22
|
+
for s in _contract_slots()
|
|
23
|
+
if s.get("provider_pkg") and s.get("shipped", True) is not False
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ───────────────────────── version gate (PEP 440-correct) ─────────────────────────
|
|
28
|
+
def test_version_comparison_logic():
|
|
29
|
+
assert doctor.is_behind("0.6.2", "0.9.1") is True
|
|
30
|
+
assert doctor.is_behind("0.9.1", "0.9.1") is False
|
|
31
|
+
assert doctor.is_behind("0.9.2", "0.9.1") is False
|
|
32
|
+
assert doctor.is_behind("0.2.0", "0.2.7") is True
|
|
33
|
+
assert doctor.is_behind("2.9.0", "2.12.2") is True # multi-digit minor
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_version_comparison_prerelease_and_local():
|
|
37
|
+
# a dev/rc build is BEHIND its final release (the false-PASS the review caught)
|
|
38
|
+
assert doctor.is_behind("0.9.1.dev3", "0.9.1") is True
|
|
39
|
+
assert doctor.is_behind("0.9.1rc1", "0.9.1") is True
|
|
40
|
+
assert doctor.is_behind("0.9.2.dev8+g083281", "0.9.1") is False # dev of a newer release
|
|
41
|
+
# a post-release is AHEAD of its base release
|
|
42
|
+
assert doctor.is_behind("1.0.0.post1", "1.0.0") is False
|
|
43
|
+
# trailing-zero normalisation must not false-FAIL
|
|
44
|
+
assert doctor.is_behind("1.0", "1.0.0") is False
|
|
45
|
+
assert doctor.is_behind("2.12", "2.12.0") is False
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ───────────────────────── entry-point wiring gate (activation grace) ─────────────────────────
|
|
49
|
+
def test_wiring_clean_when_fully_declared():
|
|
50
|
+
slots = _contract_slots()
|
|
51
|
+
underfill, drift = doctor.check_entrypoint_wiring(slots, _all_expected_declared())
|
|
52
|
+
assert underfill == []
|
|
53
|
+
assert drift == []
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_wiring_pending_when_nothing_declared():
|
|
57
|
+
"""Empty group = feature not yet activated -> NO under-fill (grace, not a false-FAIL)."""
|
|
58
|
+
slots = _contract_slots()
|
|
59
|
+
underfill, drift = doctor.check_entrypoint_wiring(slots, {})
|
|
60
|
+
assert underfill == []
|
|
61
|
+
assert drift == []
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_wiring_underfill_on_partial_activation():
|
|
65
|
+
"""Once ANY provider registers, a missing expected slot is a real UNDER_FILL."""
|
|
66
|
+
slots = _contract_slots()
|
|
67
|
+
declared = _all_expected_declared()
|
|
68
|
+
missing = declared.pop("SLOT-Q-QFI") # qig-compute registered the rest but not this
|
|
69
|
+
assert missing
|
|
70
|
+
underfill, _ = doctor.check_entrypoint_wiring(slots, declared)
|
|
71
|
+
assert {i.slot_id for i in underfill} == {"SLOT-Q-QFI"}
|
|
72
|
+
assert all(i.kind == "UNDER_FILL" for i in underfill)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_wiring_exempts_engine_local_on_partial_activation():
|
|
76
|
+
slots = _contract_slots()
|
|
77
|
+
declared = _all_expected_declared()
|
|
78
|
+
declared.pop("SLOT-BLAS-PIN") # force partial activation
|
|
79
|
+
underfill, _ = doctor.check_entrypoint_wiring(slots, declared)
|
|
80
|
+
ids = {i.slot_id for i in underfill}
|
|
81
|
+
assert "SLOT-GROUND-STATE" not in ids # provider_pkg=null is never under-fill
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_wiring_drift_unknown_slot():
|
|
85
|
+
slots = _contract_slots()
|
|
86
|
+
declared = _all_expected_declared()
|
|
87
|
+
declared["SLOT-MADE-UP"] = "qig_compute.nowhere:ghost"
|
|
88
|
+
_, drift = doctor.check_entrypoint_wiring(slots, declared)
|
|
89
|
+
assert any(i.kind == "DRIFT_UNKNOWN" and i.slot_id == "SLOT-MADE-UP" for i in drift)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_wiring_drift_moved_value():
|
|
93
|
+
slots = _contract_slots()
|
|
94
|
+
declared = _all_expected_declared()
|
|
95
|
+
declared["SLOT-Q-QFI"] = "qig_compute.somewhere_else:qfi_from_mps_tangent_space"
|
|
96
|
+
_, drift = doctor.check_entrypoint_wiring(slots, declared)
|
|
97
|
+
assert any(i.kind == "DRIFT_MOVED" and i.slot_id == "SLOT-Q-QFI" for i in drift)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# ───────────────────────── report assembly (offline) ─────────────────────────
|
|
101
|
+
def test_build_report_offline_fully_wired():
|
|
102
|
+
report = doctor.build_report(
|
|
103
|
+
doctor._contract_path(), use_network=False, declared=_all_expected_declared()
|
|
104
|
+
)
|
|
105
|
+
assert report.packages, "no package checks produced"
|
|
106
|
+
assert report.slots, "no slot checks produced"
|
|
107
|
+
assert report.wiring_activation == "ACTIVE"
|
|
108
|
+
assert report.wiring_gate_passed is True
|
|
109
|
+
born = next(s for s in report.slots if s.slot_id == "SLOT-C-BORN-SAMPLER")
|
|
110
|
+
assert born.entrypoint == "DECLARED"
|
|
111
|
+
gs = next(s for s in report.slots if s.slot_id == "SLOT-GROUND-STATE")
|
|
112
|
+
assert gs.entrypoint == "EXEMPT"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_build_report_pending_passes_wiring():
|
|
116
|
+
"""Un-activated venv (no entry-points) -> PENDING, gate PASSES (no false block)."""
|
|
117
|
+
report = doctor.build_report(doctor._contract_path(), use_network=False, declared={})
|
|
118
|
+
assert report.wiring_activation == "PENDING"
|
|
119
|
+
assert report.wiring_gate_passed is True
|
|
120
|
+
assert report.entrypoint_underfill == []
|
|
121
|
+
born = next(s for s in report.slots if s.slot_id == "SLOT-C-BORN-SAMPLER")
|
|
122
|
+
assert born.entrypoint == "PENDING"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_build_report_partial_activation_fails_wiring():
|
|
126
|
+
declared = _all_expected_declared()
|
|
127
|
+
declared.pop("SLOT-SCREENING")
|
|
128
|
+
report = doctor.build_report(doctor._contract_path(), use_network=False, declared=declared)
|
|
129
|
+
assert report.wiring_activation == "ACTIVE"
|
|
130
|
+
assert report.wiring_gate_passed is False
|
|
131
|
+
assert {i.slot_id for i in report.entrypoint_underfill} == {"SLOT-SCREENING"}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# ───────────────────────── malformed contract (fail-closed, no traceback) ─────────────────────────
|
|
135
|
+
def test_malformed_contract_raises_contract_error(tmp_path):
|
|
136
|
+
bad = tmp_path / "broken.json"
|
|
137
|
+
bad.write_text("{ not valid json ")
|
|
138
|
+
import pytest
|
|
139
|
+
|
|
140
|
+
with pytest.raises(doctor.ContractError):
|
|
141
|
+
doctor.build_report(bad, use_network=False, declared={})
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_missing_keys_contract_raises_contract_error(tmp_path):
|
|
145
|
+
bad = tmp_path / "incomplete.json"
|
|
146
|
+
bad.write_text(json.dumps({"doc": "x", "slots": []})) # no version_policy
|
|
147
|
+
import pytest
|
|
148
|
+
|
|
149
|
+
with pytest.raises(doctor.ContractError):
|
|
150
|
+
doctor.build_report(bad, use_network=False, declared={})
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_main_offline_runs(capsys):
|
|
154
|
+
rc = doctor.main(["--no-network", "--contract", str(doctor._contract_path())])
|
|
155
|
+
out = capsys.readouterr().out
|
|
156
|
+
assert "VERSION GATE" in out and "WIRING" in out
|
|
157
|
+
assert rc in (0, 1)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_main_malformed_contract_returns_1(tmp_path, capsys):
|
|
161
|
+
bad = tmp_path / "broken.json"
|
|
162
|
+
bad.write_text("nonsense")
|
|
163
|
+
rc = doctor.main(["--no-network", "--contract", str(bad)])
|
|
164
|
+
err = capsys.readouterr().err
|
|
165
|
+
assert rc == 1
|
|
166
|
+
assert "LAUNCH BLOCKED" in err
|