From 1643414853524f6bc7df957fdc7b1068cb657f2a Mon Sep 17 00:00:00 2001 From: "Jeremy H. Carroll" Date: Mon, 2 Nov 2026 15:31:26 -0500 Subject: [PATCH] Bundle runtime resources and verify installed distributions in CI --- .github/workflows/ci.yml | 48 ++++++++++++++++++++++++++++ IMPORT_GUIDE.md | 13 +++++++- README.md | 11 +++++-- contracts/__init__.py | 1 + docs/DISTRIBUTION.md | 38 +++++++++++++++++++++++ normal_forms/README.md | 6 ++-- normal_forms/contract_paths.py | 7 ++++- pyproject.toml | 32 +++++++++++++------ scripts/installed_smoke.py | 57 ++++++++++++++++++++++++++++++++++ web/__init__.py | 1 + 10 files changed, 198 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 contracts/__init__.py create mode 100644 docs/DISTRIBUTION.md create mode 100644 scripts/installed_smoke.py create mode 100644 web/__init__.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8a0a150 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: tests-and-distribution + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + tests: + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + python: ['3.10', '3.14'] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7 + with: + python-version: ${{ matrix.python }} + - run: python -m pip install -e ".[dev,web]" + - run: python -m normal_forms.catalog + - run: python -m pytest tests web/test_api_smoke.py -q + + distribution: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7 + with: + python-version: '3.14' + - run: python -m pip install build + # By default build creates an sdist, then builds the wheel from that sdist. + - run: python -m build + - name: Install and exercise wheel outside the checkout + shell: bash + run: | + python -m venv "$RUNNER_TEMP/wheel-env" + wheel=("$GITHUB_WORKSPACE"/dist/*.whl) + "$RUNNER_TEMP/wheel-env/bin/python" -m pip install "${wheel[0]}[web]" + cd "$RUNNER_TEMP" + "$RUNNER_TEMP/wheel-env/bin/python" -I "$GITHUB_WORKSPACE/scripts/installed_smoke.py" + "$RUNNER_TEMP/wheel-env/bin/python" -I -m normal_forms.catalog diff --git a/IMPORT_GUIDE.md b/IMPORT_GUIDE.md index 62b8233..1d583c0 100644 --- a/IMPORT_GUIDE.md +++ b/IMPORT_GUIDE.md @@ -26,7 +26,9 @@ This guide provides external software engineers, database architects, and distri ### 2.1 Clone and Install in Development Mode -The core evaluation engines depend on the Python Standard Library and `pyyaml`. Optional web dependencies (`fastapi`, `uvicorn`) are provided for the companion workbench interface: +The core evaluation engines depend on the Python Standard Library, `pyyaml`, and +Pydantic 2. Optional web dependencies are provided through the `web` extra. +Wheels include the contract resources and Studio templates/static assets. ```bash # 1. Clone the repository @@ -58,6 +60,11 @@ pytest tests/ -v ## 3. Four Concrete Integration Blueprints +For the bounded streaming profile and Python/SQLite input semantics, read +[Executable scope and regression evidence](docs/CORRECTNESS_SCOPE.md). +For source-distribution and isolated wheel checks, read +[Distribution verification](docs/DISTRIBUTION.md). + Below are four complete, syntax-checked, runnable Python blueprints demonstrating how to integrate `simplex-normal-forms` into production application architectures. --- @@ -406,6 +413,10 @@ if __name__ == "__main__": This blueprint demonstrates wrapping AI retrieval-augmented generation (RAG) context compression and Model Context Protocol (MCP) tool pipelines with `@coda_mur_projection`. It enforces that prompt context truncation and attribute erasure emit a cryptographic SHA-256 Merkle `LossLedger`, verifies exact additive token conservation ($\text{retained\_tokens} + \text{truncated\_tokens} == \text{source\_tokens}$), and asserts the epistemic authority firewall (`certified_implies_authorized: false`). +Here, tokens are deterministic whitespace-separated units from +`tokenize_whitespace`, not provider/model tokenizer counts. The budget alone +does not establish that a prompt fits a particular model's context window. + ```python """Blueprint 4: AI Agent LossLedger in LangChain / LlamaIndex / MCP Workflows.""" from typing import Any, List, Mapping diff --git a/README.md b/README.md index a288ff5..d18a1f3 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ simplex-normal-forms/ │ └── research_forum/ # 6-document Research Forum & Commercial Strategy Dossier (SimpleCache vs IVM) ├── images/ # 112 per-contract visual SVG diagrams + architectural overview SVGs ├── wiki/ # Complete mirror of the GitHub Wiki (one markdown page per normal form) -├── tests/ # Automated verification suite (136 passing unit, property, and API tests) +├── tests/ # Unit, replay, scalar-equivalence, and API verification ├── pyproject.toml # Standard PEP 517/518 build definition for zero-friction pip install ├── render.yaml # Infrastructure-as-code blueprint for smplxnf.anomalon.com ├── IMPORT_GUIDE.md # Developer adoption guide with 4 runnable integration blueprints @@ -156,6 +156,8 @@ Explore the **[GitHub Wiki](https://github.com/dbccpage/simplex-normal-forms/wik - **[Relational Geometrodynamics & Physics Substrate](https://github.com/dbccpage/simplex-normal-forms/wiki/Relational-Geometrodynamics-and-Physics-Substrate)** ([local](wiki/Relational-Geometrodynamics-and-Physics-Substrate.md)) - **[MVP Selection & Architecture Specification](docs/MVP_SELECTION.md)** (Render deployment and workbench design) - **[External Developer Adoption & System Import Guide](IMPORT_GUIDE.md)** (4 runnable production integration blueprints) +- **[Executable Scope & Regression Evidence](docs/CORRECTNESS_SCOPE.md)** (streaming profile and Python/SQLite semantics) +- **[Distribution Verification](docs/DISTRIBUTION.md)** (bundled resources, isolated installation, and CI) - **[Master Plan of Plans (NF-IR Architecture)](docs/plans/plan_of_plans.md)** ([Plan 1: Micro-Kernel](docs/plans/plan_01_wdp_obnf_micro_kernel.md) • [Plan 2: Subsumption & DCE](docs/plans/plan_02_subsumption_lattice.md) • [Plan 3: Operadic Wiring](docs/plans/plan_03_operadic_wiring_compiler.md) • [Plan 4: Multi-Target Lowering](docs/plans/plan_04_multi_target_lowering.md)) - **[SimpleCache Research Forum & Commercial Strategy Dossier](docs/research_forum/README.md)** ([Executive Whitepaper](docs/research_forum/01_executive_whitepaper.md) • [Competitive Analysis](docs/research_forum/02_competitive_analysis.md) • [5 Commercial Upgrades](docs/research_forum/03_commercial_upgrades.md) • [Cross-Program Foundations](docs/research_forum/04_cross_program_foundations.md) • [Forum Proceedings](docs/research_forum/05_research_forum_proceedings.md)) @@ -163,7 +165,12 @@ Explore the **[GitHub Wiki](https://github.com/dbccpage/simplex-normal-forms/wik ## 6. Complete Normal Form Contract Register (112 Contracts) -112 contract files (`index.yaml` lists 25 active and 86 draft contracts). Every contract includes a dedicated Wiki page and visual SVG diagram: +112 contract files. The index lists 25 active and 87 draft entries, with +`nf.cohomology.acnf` also listed as deprecated. These lifecycle labels are +distinct from executable verification: contract metadata currently labels 35 +executable, 75 verification-absent, and two unspecified. Catalog validation +checks document structure. Every contract includes a dedicated Wiki page and +visual SVG diagram. | Contract YAML | Short Name | Full Name | Family | Status | Theory | Verification | Wiki & SVG Diagram | | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | diff --git a/contracts/__init__.py b/contracts/__init__.py new file mode 100644 index 0000000..1b0f1a4 --- /dev/null +++ b/contracts/__init__.py @@ -0,0 +1 @@ +"""Contract resources, installed as normal_forms.resources without moving sources.""" diff --git a/docs/DISTRIBUTION.md b/docs/DISTRIBUTION.md new file mode 100644 index 0000000..6229597 --- /dev/null +++ b/docs/DISTRIBUTION.md @@ -0,0 +1,38 @@ +# Distribution verification + +`contracts/` remains the source owner for YAML, SQL, and JSON resources. The +package-directory mapping installs these files under `normal_forms/resources/`. +The library resolves this installed directory first and uses the checkout's +`contracts/` directory during source development. `web` ships templates and +static assets; install the `web` extra to run Studio. Changes to the package +list or runtime resource extensions must also update `pyproject.toml`. + +Build a source distribution and then a wheel from that source distribution: + +```sh +python -m pip install build +python -m build +``` + +Install the generated wheel with its web extra into a new virtual environment. +From a directory outside this checkout, run that environment's interpreter: + +```sh +python -I /absolute/path/to/simplex-normal-forms/scripts/installed_smoke.py +python -I -m normal_forms.catalog +``` + +The smoke check requires a virtual environment and asserts that the library, +web app, and contract resources resolve inside its installed package. It +validates all 112 contracts, loads JSON fixtures, installs the SQL schema, serves +the HTML/JS/CSS assets, and exercises the four API workbenches. This prevents a +checkout or editable install from concealing missing distribution files. + +`.github/workflows/ci.yml` runs the suite and catalog on Linux and Windows with +Python 3.10 and 3.14, and runs the isolated distribution check on Linux. The +workflow has read-only repository permissions and contains no deployment or +publication step. Build checks establish installation behavior; they do not +establish live service availability or general mathematical correctness. + +The original NF-IR plan documents record historical design intentions. Current +implementation boundaries are documented in [CORRECTNESS_SCOPE.md](CORRECTNESS_SCOPE.md). diff --git a/normal_forms/README.md b/normal_forms/README.md index c64c846..b5aa9c6 100644 --- a/normal_forms/README.md +++ b/normal_forms/README.md @@ -2,10 +2,12 @@ Executable validators, schema inventory, evaluators, and examples live here. Their YAML contracts, SQL schemas, and JSON example data remain in -`../yaml/nf`; `contract_paths.py` declares that resource home. +`../contracts` in the checkout and ship as `normal_forms.resources` in a wheel. +`contract_paths.py` selects that resource home without depending on the current +working directory. The four descriptive `*_example.py` modules are executable examples. Exact -mathematical predicates live in the canonical `domain.normal_forms` modules; +mathematical predicates live in the `normal_forms.domain` modules; infrastructure modules provide only filesystem, SQLite, and YAML adapters. AuthorityEffect: None. diff --git a/normal_forms/contract_paths.py b/normal_forms/contract_paths.py index 94bbb58..fcf3112 100644 --- a/normal_forms/contract_paths.py +++ b/normal_forms/contract_paths.py @@ -7,7 +7,12 @@ from pathlib import Path -NORMAL_FORMS_DIR = Path(__file__).resolve().parent.parent / "contracts" +_PACKAGE_DIR = Path(__file__).resolve().parent +_BUNDLED_CONTRACTS = _PACKAGE_DIR / "resources" +NORMAL_FORMS_DIR = ( + _BUNDLED_CONTRACTS if _BUNDLED_CONTRACTS.is_dir() + else _PACKAGE_DIR.parent / "contracts" +) CONTRACTS_DIR = NORMAL_FORMS_DIR INDEX_PATH = CONTRACTS_DIR / "index.yaml" _GOLDEN = NORMAL_FORMS_DIR / "_golden_nfs" diff --git a/pyproject.toml b/pyproject.toml index a337d0b..0948e75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=61.0"] +requires = ["setuptools>=77.0"] build-backend = "setuptools.build_meta" [project] @@ -8,7 +8,7 @@ version = "0.1.0" description = "Boundary-Certified Data Architecture, Relational Normalization, Cochain Retraction & NF-IR Compiler" readme = "README.md" requires-python = ">=3.10" -license = { text = "MIT" } +license = "MIT" authors = [ { name = "Simplex Normal Forms Authors" } ] @@ -16,7 +16,6 @@ classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Intended Audience :: Science/Research", - "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -28,10 +27,7 @@ classifiers = [ ] dependencies = [ "pyyaml>=6.0", - "fastapi>=0.100.0", - "uvicorn>=0.23.0", - "jinja2>=3.1.2", - "httpx>=0.24.0", + "pydantic>=2.0", ] [project.optional-dependencies] @@ -39,6 +35,7 @@ dev = [ "pytest>=8.0.0", "hypothesis>=6.0.0", "httpx>=0.24.0", + "build>=1.2", ] web = [ "fastapi>=0.100.0", @@ -47,6 +44,21 @@ web = [ "httpx>=0.24.0", ] -[tool.setuptools.packages.find] -where = ["."] -include = ["normal_forms*"] +[tool.setuptools] +packages = [ + "normal_forms", "normal_forms.bqnf", "normal_forms.domain", + "normal_forms.rop", "normal_forms.tower", "normal_forms._golden_nfs", + "normal_forms.resources", "web", +] + +# Keep contracts/ as the source owner; install its bytes inside the library. +[tool.setuptools.package-dir] +"normal_forms.resources" = "contracts" + +[tool.setuptools.package-data] +"normal_forms.resources" = [ + "*.yaml", "*.sql", "*.json", "*.md", + "_golden_nfs/*.yaml", "_golden_nfs/*.sql", "bqnf/*.yaml", "data/*.json", +] +"normal_forms.domain" = ["*.yaml"] +web = ["templates/*.html", "static/*.js", "static/*.css"] diff --git a/scripts/installed_smoke.py b/scripts/installed_smoke.py new file mode 100644 index 0000000..503d566 --- /dev/null +++ b/scripts/installed_smoke.py @@ -0,0 +1,57 @@ +"""Run with a wheel-installed virtualenv Python using -I, outside the checkout.""" + +from pathlib import Path +import json +import sqlite3 +import sys + +from fastapi.testclient import TestClient + +import normal_forms +import web +from normal_forms.catalog import check_catalog, _contract_paths +from normal_forms.contract_paths import CONTRACTS_DIR +from normal_forms.schema_sql import install_schema +from web.app import app, PRESETS, BQNF_PRESETS, COMPILER_PRESETS, AI_PRESETS + + +def main(): + assert sys.prefix != sys.base_prefix, "Use a separate virtualenv to verify the installed wheel." + for module in (normal_forms, web): + assert Path(module.__file__).resolve().is_relative_to(Path(sys.prefix).resolve()), module.__file__ + assert CONTRACTS_DIR.is_relative_to(Path(normal_forms.__file__).resolve().parent) + assert len(_contract_paths(CONTRACTS_DIR)) == 112 + assert check_catalog() == [] + for fixture in (CONTRACTS_DIR / "data").glob("*.json"): + json.loads(fixture.read_text(encoding="utf-8")) + assert (CONTRACTS_DIR / "data" / "cdc_events.json").is_file() + with sqlite3.connect(":memory:") as conn: + install_schema(conn) + assert conn.execute("SELECT count(*) FROM sqlite_master WHERE type='table'").fetchone()[0] > 0 + with TestClient(app) as client: + for path in ("/healthz", "/", "/static/app.js", "/static/style.css"): + assert client.get(path).status_code == 200, path + for endpoint, preset in ( + ("relational/normalize", PRESETS["ecommerce"]), + ("bqnf/simulate", BQNF_PRESETS["financial_ledger"]), + ("compiler/lower", COMPILER_PRESETS["employee_bcnf"]), + ("ai/lossledger", AI_PRESETS["rag_research_context"]), + ): + result = client.post("/api/" + endpoint, json=preset) + assert result.status_code == 200, result.text + data = result.json() + if endpoint == "relational/normalize": + assert data["chase"]["is_implied"] + elif endpoint == "bqnf/simulate": + assert data["final_cache"]["North"]["total_sales"] == 280 + assert data["final_cache"]["North"]["sale_count"] == 4 + elif endpoint == "compiler/lower": + assert data["sqlite_verification"]["verified"] + else: + assert data["token_accounting"]["token_conservation_commutes"] + assert data["loss_ledger_integrity_verified"] + print("Installed wheel: 112 contracts, fixtures, SQL schema, assets, and four API workbenches passed.") + + +if __name__ == "__main__": + main() diff --git a/web/__init__.py b/web/__init__.py new file mode 100644 index 0000000..7c3f4bd --- /dev/null +++ b/web/__init__.py @@ -0,0 +1 @@ +"""Optional Simplex Normal Forms Studio web application."""