qig-stack 0.1.0__py3-none-any.whl
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/__init__.py +51 -0
- qig_stack/contract.json +146 -0
- qig_stack/doctor.py +530 -0
- qig_stack-0.1.0.dist-info/METADATA +96 -0
- qig_stack-0.1.0.dist-info/RECORD +7 -0
- qig_stack-0.1.0.dist-info/WHEEL +4 -0
- qig_stack-0.1.0.dist-info/entry_points.txt +2 -0
qig_stack/__init__.py
ADDED
|
@@ -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
|
+
)
|
qig_stack/contract.json
ADDED
|
@@ -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
|
+
}
|
qig_stack/doctor.py
ADDED
|
@@ -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,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,7 @@
|
|
|
1
|
+
qig_stack/__init__.py,sha256=_Uoofejm1xurIsYsNkXyknkokK47pnXwcEqbhDKOo-U,1422
|
|
2
|
+
qig_stack/doctor.py,sha256=N5di8W3g7C-kJfjVq5Dv1i_B-nSG19YhBBOHc_kZUtw,20324
|
|
3
|
+
qig_stack/contract.json,sha256=67e94EN8PS_IJzbxQvReSDbxD2K-jA73VjBqDmsuQT4,9504
|
|
4
|
+
qig_stack-0.1.0.dist-info/METADATA,sha256=NgCW66dVKT00ja094nGqbyuyQ7zduj5yzbotB5_W4Zo,4539
|
|
5
|
+
qig_stack-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
6
|
+
qig_stack-0.1.0.dist-info/entry_points.txt,sha256=ShiiObo3EgrCZqJzLCzj3nueixQPu7x_SWlGq4VxMCw,53
|
|
7
|
+
qig_stack-0.1.0.dist-info/RECORD,,
|