From a40324ab426448cf9874764ce9807699cc4047e4 Mon Sep 17 00:00:00 2001 From: "Jeremy H. Carroll" Date: Mon, 2 Nov 2026 17:12:40 -0500 Subject: [PATCH 1/2] Reconcile deployment status and fix lossy preset browser parsing --- .github/workflows/ci.yml | 1 + IMPORT_GUIDE.md | 8 +- README.md | 9 +- docs/DEPLOYMENT.md | 94 ++++++++++ docs/MVP_SELECTION.md | 12 +- docs/PROJECT_STATUS.md | 103 +++++++++++ docs/plans/plan_01_wdp_obnf_micro_kernel.md | 6 + docs/plans/plan_02_subsumption_lattice.md | 6 + .../plans/plan_03_operadic_wiring_compiler.md | 6 + docs/plans/plan_04_multi_target_lowering.md | 6 + docs/plans/plan_of_plans.md | 8 +- .../2026-11-02-alternate-domain-http.json | 19 ++ docs/verification/2026-11-02-browser.md | 23 +++ .../2026-11-02-corrected-domain-http.json | 19 ++ .../2026-11-02-corrected-render-http.json | 20 +++ docs/verification/2026-11-02-local-http.json | 166 ++++++++++++++++++ docs/verification/2026-11-02-public-http.json | 19 ++ .../2026-11-02-render-candidate-http.json | 20 +++ scripts/http_smoke.py | 143 +++++++++++++++ tests/test_frontend.cjs | 52 ++++++ web/static/app.js | 7 +- wiki/Home.md | 5 + wiki/Project-Status.md | 60 +++++++ wiki/_Sidebar.md | 1 + 24 files changed, 796 insertions(+), 17 deletions(-) create mode 100644 docs/DEPLOYMENT.md create mode 100644 docs/PROJECT_STATUS.md create mode 100644 docs/verification/2026-11-02-alternate-domain-http.json create mode 100644 docs/verification/2026-11-02-browser.md create mode 100644 docs/verification/2026-11-02-corrected-domain-http.json create mode 100644 docs/verification/2026-11-02-corrected-render-http.json create mode 100644 docs/verification/2026-11-02-local-http.json create mode 100644 docs/verification/2026-11-02-public-http.json create mode 100644 docs/verification/2026-11-02-render-candidate-http.json create mode 100644 scripts/http_smoke.py create mode 100644 tests/test_frontend.cjs create mode 100644 wiki/Project-Status.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a0a150..0fc1401 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,7 @@ jobs: - 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 + - run: node tests/test_frontend.cjs distribution: runs-on: ubuntu-latest diff --git a/IMPORT_GUIDE.md b/IMPORT_GUIDE.md index 1d583c0..58df01a 100644 --- a/IMPORT_GUIDE.md +++ b/IMPORT_GUIDE.md @@ -573,7 +573,7 @@ def check_strict_auditing_nf(schema: RelationalSchema) -> Tuple[bool, Optional[s ## 5. Deployment Guide: Companion Web Interface -The repository includes a companion web application, **Simplex-NF Studio ("The Normal Form Workbench")**, configured for production deployment at `smplxnf.anomalon.com` on Render (`onrender.com`). +The repository includes a companion web application, **Simplex-NF Studio ("The Normal Form Workbench")**, with a Render blueprint targeting `smplxnf.anomalon.com`. Configuration is not deployment evidence: see [current status](docs/PROJECT_STATUS.md) and the [deployment runbook](docs/DEPLOYMENT.md). ### 5.1 Local Development Server @@ -621,9 +621,9 @@ services: 2. Select **New > Blueprint** and link the repository. Render will automatically parse `render.yaml`. 3. Configure the custom domain in your DNS provider: - Record Type: `CNAME` - - Host: `smplxnf.anomalon.com` - - Value: `smplxnf-studio.onrender.com` -4. Render provisions and renews TLS/SSL certificates automatically via Let's Encrypt. + - Host: `smplxnf` within the `anomalon.com` zone + - Value: the actual assigned Render hostname (do not infer it from the service name) +4. Verify the custom domain and certificate in Render, then run `python scripts/http_smoke.py https://smplxnf.anomalon.com`. The [runbook](docs/DEPLOYMENT.md) covers the user-operated DNS handoff and browser checks. --- diff --git a/README.md b/README.md index d18a1f3..f0f6055 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ **Boundary-Certified Data Architecture, Relational Normalization, Cochain Retraction & Representation Transport (`CODA_MUR`)** **Epistemic Standard:** Landau Rigor / Claim Discipline Compliant (`AuthorityEffect: None`, `certified_implies_authorized: false`) -[![Live Demo: smplxnf.anomalon.com](https://img.shields.io/badge/Live_Demo-smplxnf.anomalon.com-0284c7)](https://smplxnf.anomalon.com) +[![Deployment status](https://img.shields.io/badge/Deployment-verification_pending-amber)](docs/PROJECT_STATUS.md) [![Deployment: Render](https://img.shields.io/badge/Deploy-onrender.com-46e3b7)](https://render.com) [![Adoption Guide: IMPORT_GUIDE.md](https://img.shields.io/badge/Adoption_Guide-IMPORT__GUIDE.md-10b981)](IMPORT_GUIDE.md) [![Architecture Spec: MVP_SELECTION.md](https://img.shields.io/badge/Architecture-MVP__SELECTION.md-8b5cf6)](docs/MVP_SELECTION.md) @@ -19,12 +19,15 @@ Experience the core theoretical and engineering capabilities of `simplex-normal-forms` in real time: -👉 **[Launch Simplex-NF Studio at smplxnf.anomalon.com](https://smplxnf.anomalon.com)** (Hosted on Render via `onrender.com`) +**Target URL:** [smplxnf.anomalon.com](https://smplxnf.anomalon.com). As of 2026-11-02, +the application is merged and locally tested; public DNS/HTTPS verification is +pending. See [project status](docs/PROJECT_STATUS.md) and the +[Render deployment and smoke-test runbook](docs/DEPLOYMENT.md). ### Four Flagship Engineering Workbenches 1. **Relational Synthesis & Tableau Chase Studio (Flagship Landing View):** Interactive attribute closure, candidate key derivation, prime/non-prime attribute partitioning, minimal canonical cover, Bernstein (1976) dependency-preserving 3NF synthesis, Tsou-Fischer BCNF decomposition trees, and step-by-step Tableau Chase verification with spurious tuple counterexample witnesses. 2. **Streaming BQNF Delta Repair Simulator:** Incremental Change Data Capture (CDC) boundary maintenance, signed coboundary delta calculation, priority-queue extremum cochain tracking in $O(\log K)$ without base table scans, and dynamic dyadic-bucket $\epsilon$-quantile sketch rebalancing under skewed streams. -3. **4-Layer NF-IR Compiler Explorer:** Godbolt-style intermediate representation inspector demonstrating multi-pass lowering: Declarative Contract $\to$ Layer 1 Micro-Kernel $\to$ Layer 2 Poset Subsumption & Dead-Check Elimination (DCE) $\to$ Layer 3 Operadic DAG Wiring $\to$ Layer 4 Codegen for SQLite Triggers (`BEFORE INSERT/UPDATE`) and Python `@nf_kernel_guard` decorators. +3. **NF-IR Compiler Explorer:** Functional-dependency lowering into SQLite triggers (`BEFORE INSERT/UPDATE`), Python guards, and counterexample fixtures, with a constructed subsumption comparison. The four-layer library architecture includes operadic wiring; this endpoint does not execute arbitrary wiring DAGs or compile the full contract catalog. 4. **AI Agent LossLedger Gateway:** Context projection and token budget truncation for RAG and Model Context Protocol (MCP) tool pipelines with cryptographic SHA-256 Merkle `LossLedger` generation, exact additive token conservation ($\text{retained} + \text{truncated} == \text{source}$), and strict authority firewall gating (`certified_implies_authorized: false`). 📖 **Read the Full Architectural Specification:** [`docs/MVP_SELECTION.md`](docs/MVP_SELECTION.md) diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 0000000..5f2e3db --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,94 @@ +# Render deployment and Phase 3 verification + +The application and blueprint are implemented. Public deployment remains a +separate gate; see [the dated status](PROJECT_STATUS.md). PRs #17–19 are already +merged. Use the current reviewed `main`, not the old MVP feature branch. + +## Render service + +Inspect the existing [Render project](https://dashboard.render.com/project/prj-dbj5ei942hec73c30pu0) +before creating or syncing `smplxnf-studio` from [render.yaml](../render.yaml). + +| Setting | Blueprint value | +| --- | --- | +| Repository / branch | `https://github.com/dbccpage/simplex-normal-forms` / `main` | +| Runtime / region / plan | Python / Oregon / Starter | +| Build | `pip install -e . && pip install -r web/requirements.txt` | +| Start | `uvicorn web.app:app --host 0.0.0.0 --port $PORT` | +| Health check | `/healthz` | +| Environment | `PYTHONUNBUFFERED=1`, `APP_ENVIRONMENT=production` | +| Custom domain | `smplxnf.anomalon.com` | + +Record the selected Python version and successful deploy's Git SHA. The blueprint +does not pin a Python version; Render's default depends on service creation date +([Render Python version documentation](https://render.com/docs/python-version)). +Record the actual instance plan and associated account commitment before provisioning. + +Run the check below against the **assigned** `https://…onrender.com` origin +first. A service name alone does not establish its assigned hostname. + +## DNS handoff + +The supplied handoff assigns the GoDaddy update to the user. Report the verified +Render hostname, then wait for their confirmation that the record is updated: + +**2026-11-02 update:** The user has already created the canonical CNAME pointing +`smplxnf` to `smplxnf-live.onrender.com`; resolver `1.1.1.1` returned it with TTL +3600. This target returned HTTP 404 on `/healthz`. Confirm the new service's +assigned hostname before requesting another DNS change. The service still needs +`smplxnf.anomalon.com` added in **Settings → Custom Domains**. + +| GoDaddy field | Value | +| --- | --- | +| Type | `CNAME` | +| Name / Host | `smplxnf` (within the `anomalon.com` zone) | +| Value / Target | Actual assigned Render hostname, without `https://` or a path | +| TTL | 600 seconds if available, otherwise provider default | + +Inspect any existing record for this name before replacing it. Leave unrelated +zone records unchanged. Verify the custom domain in Render and wait for its +certificate to become valid. Do not bypass certificate errors. +Provider references: [Render custom domains](https://render.com/docs/custom-domains), +[Render DNS configuration](https://render.com/docs/configure-other-dns), +[GoDaddy CNAME fields](https://www.godaddy.com/help/add-a-cname-record-19236). + +## Repeatable HTTP smoke + +Requires only Python 3.10+ standard library; no application imports or credentials. + +```sh +python scripts/http_smoke.py https://ACTUAL-HOST.onrender.com --output render-http.json +python scripts/http_smoke.py https://smplxnf.anomalon.com --output public-http.json +# Small, bounded concurrency probe: 5 GETs then 10 POSTs across two clients. +python scripts/http_smoke.py https://smplxnf.anomalon.com --workers 2 --repeats 2 --output public-concurrent.json +``` + +The script verifies `/healthz`, the HTML workbench, JavaScript and CSS assets, +and `/api/presets`. It submits full preset objects to: + +| Endpoint | Preset and semantic check | +| --- | --- | +| `/api/relational/normalize` | `ecommerce`: lossless chase; `lossy_decomposition`: spurious tuple witness | +| `/api/bqnf/simulate` | `financial_ledger`: North total 280 and count 4 | +| `/api/compiler/lower` | `employee_bcnf`: verified SQLite, trigger SQL and counterexamples | +| `/api/ai/lossledger` | `rag_research_context`: admission, token conservation and ledger integrity | + +It records HTTP status, elapsed time and response hashes, validates HTTPS +certificates normally, rejects redirects as passing results, and exits nonzero +on failure. If health fails, later checks are explicitly skipped. A DNS failure +provides no TLS verdict. The elapsed times include client/network costs; record +them without claiming an SLA. Source identity must be checked separately in Render. + +## Browser acceptance + +On the public HTTPS origin, execute the default relational preset and the lossy +counterexample preset; inspect the resulting keys, decomposition and chase. +Run streaming with cochain lookup on and off; check logs, region-labelled +buckets and totals against API results. Run compiler lowering and inspect all +three output tabs. Run AI projection and check conservation and ledger output. +Open the quickstart tab, check developer links, inspect browser errors, and +inspect layout at desktop and narrow viewport sizes. Record the URL, deployed +SHA, time, screenshots, observed results and any remaining failures. + +Localhost results establish local behavior only. The deployment gate remains +open until these checks pass on the public custom domain. diff --git a/docs/MVP_SELECTION.md b/docs/MVP_SELECTION.md index f49fd39..a4a8d4c 100644 --- a/docs/MVP_SELECTION.md +++ b/docs/MVP_SELECTION.md @@ -18,7 +18,7 @@ The objective of this specification is to define, evaluate, and architect the si 2. Enable an external developer to clone the repository, install the package via standard tooling, and import the core normal-form logic into their own database, compiler, or AI workflows in under fifteen minutes to support the author's technical resume. ### 1.2 The Production Vision on Render -The application is provisioned as a lightweight, production-ready Web Service on Render bound to the custom hostname `smplxnf.anomalon.com`. Operating under a stateless ASGI process model powered by FastAPI and Uvicorn, the service serves: +The application is designed for a lightweight Web Service on Render bound to the custom hostname `smplxnf.anomalon.com`. Public provisioning and HTTPS verification remain separate gates; see [current status](PROJECT_STATUS.md). Operating under a stateless ASGI process model powered by FastAPI and Uvicorn, the service serves: - A responsive, high-contrast single-page application (SPA) dashboard styled with modern systems-grade typography and Tailwind CSS. - Strongly typed REST endpoints exposing relational synthesis, Tableau Chase verification, streaming CDC delta repair, 4-layer NF-IR compilation to SQLite triggers and Python guards, and AI context compression with Merkle LossLedger generation. - Sub-50 millisecond algorithmic execution latencies for typical schemas and streams, executing purely in-process with zero external database dependencies. @@ -601,7 +601,7 @@ services: ``` #### 5.3.2 Subdomain Routing via CNAME -1. **Render Hostname:** The service receives an internal endpoint: `smplxnf-studio.onrender.com`. +1. **Render Hostname:** Record the actual assigned public `onrender.com` endpoint from the service dashboard. `smplxnf-studio.onrender.com` below is an example, not a verified assignment. 2. **DNS Configuration:** In the DNS control panel for `anomalon.com`, a canonical name record is created: ```text smplxnf.anomalon.com. CNAME smplxnf-studio.onrender.com. @@ -609,7 +609,7 @@ services: 3. **Automated TLS Termination:** Render automatically provisions and renews a Let's Encrypt SSL/TLS certificate for `smplxnf.anomalon.com`. #### 5.3.3 Operational Bounds & Resource Footprint -- **Memory Footprint:** The complete process (FastAPI + Uvicorn + `normal_forms` libraries) consumes approximately 45MB of RAM at idle, fitting comfortably within Render's 512MB entry tier. +- **Memory Footprint:** The original design estimated approximately 45MB at idle. No retained production measurement establishes this figure; measure memory on the deployed instance. - **CPU & Latency Profile:** Core synthesis, Tableau Chase fixed-point calculation, and trigger compilation execute in 5ms to 35ms on standard cloud vCPUs. - **Cold-Start Latency:** Process startup takes approximately 1.2 seconds, ensuring rapid recovery on cold instances. @@ -847,17 +847,17 @@ In compliance with the project Claim Hygiene standard, all architectural evaluat 4. Does not assert that 3NF or BCNF normalization eliminates application-level anomalies outside declared functional dependencies. ### 7.3 Narrowest Defensible Claim -In the declared discrete Python implementation, the relational synthesis algorithms, Chase tableau procedures, BQNF delta accumulation, and 4-layer NF-IR pipeline execute deterministically and pass all 136 automated tests in `simplex-normal-forms/tests/`. +The original MVP handoff reported 136 pytest items (89 unittest methods). Subsequent correctness and packaging work was merged in PRs #18–19, with 232 tests passing in premerge verification and hosted CI passing on merged `da498e5`. These are bounded implementation checks, not certification of every contract or a live deployment. See [current evidence and scope](PROJECT_STATUS.md). ### 7.4 Next Test Needed -Implement the FastAPI presentation prototype in `web/app.py`, deploy the service to Render under `smplxnf.anomalon.com`, and execute automated HTTP end-to-end integration tests measuring sub-200 millisecond response latencies across synthesis, streaming simulation, and compiler endpoints. +The FastAPI presentation prototype is implemented and PRs #17–19 are merged. Inspect/provision the Render service, verify its assigned hostname, hand off the exact CNAME target to the user, then verify custom-domain TLS and all four workbenches. Use [DEPLOYMENT.md](DEPLOYMENT.md) and `scripts/http_smoke.py`; record measured latency rather than treating the original sub-200 ms target as an established result. --- ## 8. Conclusion & Actionable Execution Sequence 1. **Approved Architecture:** Option E (Simplex-NF Studio) is established as the single canonical MVP for `smplxnf.anomalon.com`. -2. **Immediate Execution Phase:** +2. **Original implementation milestones (now implemented; live deployment pending):** - **M2 (Web Prototype & Deployment):** Author `pyproject.toml`, implement `web/app.py`, author `render.yaml`, and build the responsive single-page studio dashboard. - **M3 (Documentation & Import Guide):** Author comprehensive `docs/IMPORT_GUIDE.md` and update `README.md` with complete installation and code usage instructions. - **M4 (Automated Tests & Verification):** Add test suites covering API endpoints, UI rendering, and external package import workflows. diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md new file mode 100644 index 0000000..bd6057d --- /dev/null +++ b/docs/PROJECT_STATUS.md @@ -0,0 +1,103 @@ +# Project status — 2026-11-02 + +Source baseline: `main` at `da498e507ebf6c35cac4355d711e3be30c23a07c`. +Implementation, catalog declarations, and public deployment have separate gates. + +## Implementation and verification + +| Work | Evidence | Status | +| --- | --- | --- | +| MVP selection, four workbenches, import guide, Render blueprint | [PR #17](https://github.com/dbccpage/simplex-normal-forms/pull/17), merged at `489f3e1` | Implemented | +| Streaming and scalar lowering corrections | [PR #18](https://github.com/dbccpage/simplex-normal-forms/pull/18), merged at `d59f890` | Implemented; scope in [CORRECTNESS_SCOPE.md](CORRECTNESS_SCOPE.md) | +| Bundled resources, installed distribution checks, CI | [PR #19](https://github.com/dbccpage/simplex-normal-forms/pull/19), merged at `da498e5` | Implemented | +| Corrected source suite | 232 tests passed on Python 3.12 and 3.14 during premerge verification | Scoped regression evidence | +| Hosted checks on merged `da498e5` | [tests-and-distribution](https://github.com/dbccpage/simplex-normal-forms/actions/runs/41529136846): Windows/Linux × Python 3.10/3.14 and isolated distribution job | Passed | +| Local HTTP smoke, 2026-11-02 22:02 UTC | [Receipt](verification/2026-11-02-local-http.json): 15 checks, two clients, two repetitions of five POST cases | Passed; not a public latency benchmark | +| Initial custom-domain HTTP smoke, 2026-11-02 22:03 UTC | [Receipt](verification/2026-11-02-public-http.json): `/healthz` failed at DNS lookup | Historical failure before DNS correction; TLS not reached | +| Corrected CNAME, 2026-11-02 22:07 UTC | Public resolver `1.1.1.1` returned `smplxnf.anomalon.com → smplxnf-live.onrender.com`, TTL 3600 | DNS record observed; assignment to the intended service not yet verified | +| Supplied Render target, 2026-11-02 22:07 UTC | [Receipt](verification/2026-11-02-corrected-render-http.json): HTTPS `/healthz` returned 404 | Application health check failed | +| Canonical hostname retry, 2026-11-02 22:08 UTC | [Receipt](verification/2026-11-02-corrected-domain-http.json): local resolver still failed | Propagation/cache discrepancy; no TLS verdict for this hostname | +| Render account/service state | Supplied project `prj-dbj5ei942hec73c30pu0`; dashboard required sign-in; no connected Render tool available during inspection | Service existence, deployed SHA, assigned hostname and certificate remain unverified | + +GitHub Pages completing its own deployment does not establish that the FastAPI +application is running on Render. The health response's fixed version `0.1.0` +also does not identify a deployed Git commit. + +The user subsequently supplied a Render project screenshot showing +`owed-workbench` and `smplcache-live` as the two existing Production services, +and a new-web-service URL. These are distinct from the proposed Simplex service. +The earlier accidental `simplxnf` spelling and its failed HTTP/TLS probes are +retained in the receipts as history. The canonical target remains `smplxnf`. + +Local browser checks exercised relational synthesis, streaming, compiler and AI +workbenches. Switching to the lossy decomposition preset exposed a frontend +parser defect: `(A, B), (B, C)` was flattened into one component and displayed a +lossless result. The correction preserves the two components and renders the +spurious-tuple witness; zero-step lossy traces also no longer claim an already +distinguished tableau. `node tests/test_frontend.cjs` checks the actual request +handler and rendering for parenthesized, semicolon and blank component inputs. +These changes require their own review/CI and are not part of baseline `da498e5`. + +## How to read the catalog and wiki + +All **112 contracts** pass the catalog's structural checks. Reading their +`identity` metadata gives: + +| Field | Declared counts | +| --- | --- | +| `status` | 24 active, 87 draft, 1 deprecated | +| `verification_status` | 35 executable, 75 absent, 2 missing | +| `theory_status` | 48 established, 42 proposed, 11 experimental, 9 admitted, 2 missing | + +These counts describe declarations, not independent audits of every verifier. +The index separately lists 25 active and 87 draft entries plus a deprecated +ACNF entry; ACNF occurs in both draft and deprecated lists. Index membership +and per-contract status are different fields and have not been silently aligned. + +Wiki diagrams and generic phrases such as “Certified Output” describe intended +contract behavior; they are not receipts for a successful execution. In +particular, a contract marked `verification_status: absent` supplies no +executable verification by that declaration. Proposed/admitted physics contracts +and research-forum scenarios remain subject to their stated assumptions. Regex +claim-word checks do not establish mathematical correctness, originality, or +the absence of plagiarism. Certification does not confer authority. + +The browser compiler endpoint demonstrates functional-dependency lowering and +a constructed subsumption comparison. It does not execute an arbitrary +four-layer operadic DAG or compile all 112 contracts. The AI workbench accounts +for whitespace-separated tokens, not a model tokenizer or semantic preservation. + +## Reconciliation with the supplied MVP notes + +The user supplied the recent Gemini/Teamwork handoff on 2026-11-02. Its source +text SHA-256 is +`39b3d8f2ce39853808e862145693040f5f2b7767f20cdb4bbb4ffc9dd3d82b37`. +That narrative records milestones 1–4, the original 136 pytest items / 89 +unittest methods, and the creation of PR #17. It is historical implementation +evidence, not proof of a completed Render deployment. Its worker and panel +sign-offs have not been independently reconstructed here. + +The handoff's old checkout path and instruction to merge PR #17 are superseded. +Its four example `preset_id` request bodies do not match the implemented API: +relational, streaming and AI requests need explicit fields, while the compiler +can fall back to defaults and ignore an unknown preset ID. Use the complete +objects returned by `/api/presets`, as [the HTTP smoke script](../scripts/http_smoke.py) +does. The original 136-test count is preserved as history, not current coverage. + +## Next work, in order + +1. Obtain authenticated Render access; inspect the supplied project before + creating a duplicate service. Deploy the current reviewed `main` and record + its exact SHA, service ID, deploy ID, region, plan and assigned hostname. +2. Verify the assigned `onrender.com` origin with the HTTP smoke script. Compare + the **actual** hostname with the user's existing CNAME target + `smplxnf-live.onrender.com`; report any needed correction. +3. Add `smplxnf.anomalon.com` to the service's Custom Domains, verify DNS and TLS; + run the same HTTPS checks and all four browser workflows on the public origin. +4. Record deployment receipts and measured latencies. A bounded concurrency + smoke is not evidence of sustained load capacity or a sub-200 ms SLA. +5. Continue contract-by-contract evidence audits and replace generic wiki + examples with executable witnesses where useful. Do not expand the MVP's + advertised scope ahead of those checks. + +See [DEPLOYMENT.md](DEPLOYMENT.md) for the runnable handoff. diff --git a/docs/plans/plan_01_wdp_obnf_micro_kernel.md b/docs/plans/plan_01_wdp_obnf_micro_kernel.md index ae83ed9..35c3812 100644 --- a/docs/plans/plan_01_wdp_obnf_micro_kernel.md +++ b/docs/plans/plan_01_wdp_obnf_micro_kernel.md @@ -2,6 +2,12 @@ ## 1. Gate Status +**Current status (2026-11-02):** Implemented and merged in PR #12 (`db4150f`). +The approval gates below are retained as the original planning record, not +pending authorization. See [current project status](../PROJECT_STATUS.md). + +### Original planning gates + * **Master Plan of Plans ([`plan_of_plans.md`](./plan_of_plans.md)):** Approved * **Current Gate:** Review & Approve **Plan 1 (`plan_01_wdp_obnf_micro_kernel.md`)** before writing any production code. * **Next Gate After Approval:** Execute Plan 1 (`normal_forms/ir_kernel.py` + `tests/test_ir_kernel.py`) while drafting **Plan 2 (`plan_02_subsumption_lattice.md`)** in parallel; open, review, and merge **PR #12** before executing Plan 2. diff --git a/docs/plans/plan_02_subsumption_lattice.md b/docs/plans/plan_02_subsumption_lattice.md index fdde5cc..66f0f80 100644 --- a/docs/plans/plan_02_subsumption_lattice.md +++ b/docs/plans/plan_02_subsumption_lattice.md @@ -2,6 +2,12 @@ ## 1. Gate Status +**Current status (2026-11-02):** Implemented and merged in PR #13 (`d0e834c`). +The approval gates below are retained as the original planning record, not +pending authorization. See [current project status](../PROJECT_STATUS.md). + +### Original planning gates + * **Master Plan of Plans ([`plan_of_plans.md`](./plan_of_plans.md)):** Approved * **Plan 1 ([`plan_01_wdp_obnf_micro_kernel.md`](./plan_01_wdp_obnf_micro_kernel.md)):** Executed (`normal_forms/ir_kernel.py` + `tests/test_ir_kernel.py` passing) & PR #12 (`https://github.com/dbccpage/simplex-normal-forms/pull/12`) merging to `origin/main`. * **Current Gate:** Review & Approve **Plan 2 (`plan_02_subsumption_lattice.md`)** before writing any Plan 2 production code. diff --git a/docs/plans/plan_03_operadic_wiring_compiler.md b/docs/plans/plan_03_operadic_wiring_compiler.md index d99e84e..2424003 100644 --- a/docs/plans/plan_03_operadic_wiring_compiler.md +++ b/docs/plans/plan_03_operadic_wiring_compiler.md @@ -2,6 +2,12 @@ ## 1. Gate Status +**Current status (2026-11-02):** Implemented and merged in PR #14 (`ce90d22`). +The approval gates below are retained as the original planning record, not +pending authorization. See [current project status](../PROJECT_STATUS.md). + +### Original planning gates + * **Master Plan of Plans ([`plan_of_plans.md`](./plan_of_plans.md)):** Approved * **Plan 1 ([`plan_01_wdp_obnf_micro_kernel.md`](./plan_01_wdp_obnf_micro_kernel.md)):** Merged in **PR #12** (`db4150f`) * **Plan 2 ([`plan_02_subsumption_lattice.md`](./plan_02_subsumption_lattice.md)):** Executed (`normal_forms/ir_subsumption.py` + `tests/test_ir_subsumption.py` passing) & **PR #13** (`https://github.com/dbccpage/simplex-normal-forms/pull/13`) merging to `origin/main`. diff --git a/docs/plans/plan_04_multi_target_lowering.md b/docs/plans/plan_04_multi_target_lowering.md index a4efb1a..89e7663 100644 --- a/docs/plans/plan_04_multi_target_lowering.md +++ b/docs/plans/plan_04_multi_target_lowering.md @@ -2,6 +2,12 @@ ## 1. Gate Status +**Current status (2026-11-02):** Implemented and merged in PR #15 (`dffdb87`). +The approval gates below are retained as the original planning record, not +pending authorization. See [current project status](../PROJECT_STATUS.md). + +### Original planning gates + * **Master Plan of Plans ([`plan_of_plans.md`](./plan_of_plans.md)):** Approved * **Plan 1 ([`plan_01_wdp_obnf_micro_kernel.md`](./plan_01_wdp_obnf_micro_kernel.md)):** Merged in **PR #12** (`db4150f`) * **Plan 2 ([`plan_02_subsumption_lattice.md`](./plan_02_subsumption_lattice.md)):** Merged in **PR #13** (`d0e834c`) diff --git a/docs/plans/plan_of_plans.md b/docs/plans/plan_of_plans.md index 0312aaa..80bf4c2 100644 --- a/docs/plans/plan_of_plans.md +++ b/docs/plans/plan_of_plans.md @@ -1,6 +1,10 @@ -# Master Plan of Plans: Simplex Normal Forms IR (`NF-IR`) — Completed +# Master Plan of Plans: Simplex Normal Forms IR (`NF-IR`) — Historical completion record -## 1. Final Status on `origin/main` @ `dffdb87` +The four-plan implementation was completed at `dffdb87`. The 44-test receipt +below belongs to that milestone. For the later web MVP, correctness fixes, +packaging, CI and deployment gate, see [current project status](../PROJECT_STATUS.md). + +## 1. Historical status at `dffdb87` All 11 pull requests (**PR #5 through PR #15**) are **committed, verified (`Ran 44 tests in 31.448s — OK`), merged, and synced to `origin/main`**: diff --git a/docs/verification/2026-11-02-alternate-domain-http.json b/docs/verification/2026-11-02-alternate-domain-http.json new file mode 100644 index 0000000..8cfaf03 --- /dev/null +++ b/docs/verification/2026-11-02-alternate-domain-http.json @@ -0,0 +1,19 @@ +{ + "checked_at_utc": "2026-11-02T22:06:17.249622+00:00", + "base_url": "https://simplxnf.anomalon.com", + "workers": 1, + "repeats": 1, + "tls_certificate_validation": "enabled", + "checks": [ + { + "method": "GET", + "path": "/healthz", + "passed": false, + "error": "", + "elapsed_ms": 73.5 + } + ], + "scope": "HTTP and semantic smoke only; deployment commit and browser UX require separate verification.", + "passed": false, + "remaining_checks": "skipped: health check failed" +} diff --git a/docs/verification/2026-11-02-browser.md b/docs/verification/2026-11-02-browser.md new file mode 100644 index 0000000..11db0e1 --- /dev/null +++ b/docs/verification/2026-11-02-browser.md @@ -0,0 +1,23 @@ +# Local browser verification — 2026-11-02 + +Origin: `http://127.0.0.1:8765/`, Uvicorn on the working branch based on +`da498e5`. These observations are local, not production acceptance. + +| Workflow | Observed result | +| --- | --- | +| Default relational preset | Key `{item_id, order_id}`; lossless chase with seven steps; generated tables and DDL rendered | +| Streaming default, explicit Simulate click | Three CDC events, four extrema operations, zero fallback scans; North and South buckets rendered, total mass five | +| Compiler default, explicit Compile click | SQLite INSERT/UPDATE triggers rendered; one subsumption pass executed and one elided | +| AI default, explicit Evaluate click | 62 source tokens = 35 retained + 27 truncated; ledger seal marked verified | +| Lossy preset before correction | UI sent one flattened component and incorrectly displayed a lossless join, despite the direct API's correct two-component result | +| Lossy preset after parser correction | UI displayed “Lossy Join (Spurious Tuples)”, “Non-Lossless Decomposition” and the witness `{"A":"a_A","B":"a_B","C":"a_C"}` | + +The zero-step trace also used to assert a distinguished row regardless of the +chase result. The follow-up renderer correction and Node regression verify both +lossy and lossless zero-step messages. `node tests/test_frontend.cjs` passes; +the existing Python suite passes **232 tests in 45.08s** on Python 3.14. + +This pass did not complete mobile viewport, copy-button, developer-link, +console-error, or full custom-input coverage. Repeat the browser acceptance +checklist in `docs/DEPLOYMENT.md` on the live HTTPS site before closing the +deployment gate. diff --git a/docs/verification/2026-11-02-corrected-domain-http.json b/docs/verification/2026-11-02-corrected-domain-http.json new file mode 100644 index 0000000..795246d --- /dev/null +++ b/docs/verification/2026-11-02-corrected-domain-http.json @@ -0,0 +1,19 @@ +{ + "checked_at_utc": "2026-11-02T22:08:43.691595+00:00", + "base_url": "https://smplxnf.anomalon.com", + "workers": 1, + "repeats": 1, + "tls_certificate_validation": "enabled", + "checks": [ + { + "method": "GET", + "path": "/healthz", + "passed": false, + "error": "", + "elapsed_ms": 58.61 + } + ], + "scope": "HTTP and semantic smoke only; deployment commit and browser UX require separate verification.", + "passed": false, + "remaining_checks": "skipped: health check failed" +} diff --git a/docs/verification/2026-11-02-corrected-render-http.json b/docs/verification/2026-11-02-corrected-render-http.json new file mode 100644 index 0000000..e6d5642 --- /dev/null +++ b/docs/verification/2026-11-02-corrected-render-http.json @@ -0,0 +1,20 @@ +{ + "checked_at_utc": "2026-11-02T22:07:08.900825+00:00", + "base_url": "https://smplxnf-live.onrender.com", + "workers": 1, + "repeats": 1, + "tls_certificate_validation": "enabled", + "checks": [ + { + "method": "GET", + "path": "/healthz", + "passed": false, + "error": "HTTP Error 404: Not Found", + "status": 404, + "elapsed_ms": 301.07 + } + ], + "scope": "HTTP and semantic smoke only; deployment commit and browser UX require separate verification.", + "passed": false, + "remaining_checks": "skipped: health check failed" +} diff --git a/docs/verification/2026-11-02-local-http.json b/docs/verification/2026-11-02-local-http.json new file mode 100644 index 0000000..d8c05bf --- /dev/null +++ b/docs/verification/2026-11-02-local-http.json @@ -0,0 +1,166 @@ +{ + "checked_at_utc": "2026-11-02T22:02:18.842115+00:00", + "base_url": "http://127.0.0.1:8765", + "workers": 2, + "repeats": 2, + "tls_certificate_validation": "not applicable (loopback)", + "checks": [ + { + "method": "GET", + "path": "/healthz", + "status": 200, + "final_url": "http://127.0.0.1:8765/healthz", + "response_sha256": "c7e0c2a5ccb41ba909833e6a4e943d254bdb4efd296c3b930c7dcdf45eed9a46", + "passed": true, + "elapsed_ms": 60.5 + }, + { + "method": "GET", + "path": "/", + "status": 200, + "final_url": "http://127.0.0.1:8765/", + "response_sha256": "a4f86bbc503e80fed790367bb01adb2eb969c42259c44940f17dfb6aa73ce67a", + "passed": true, + "elapsed_ms": 10.43 + }, + { + "method": "GET", + "path": "/static/app.js", + "status": 200, + "final_url": "http://127.0.0.1:8765/static/app.js", + "response_sha256": "140a36b0929e725a074e312f44242f0cf19f71df8a3cd3aeca910766afaa53bd", + "passed": true, + "elapsed_ms": 146.62 + }, + { + "method": "GET", + "path": "/static/style.css", + "status": 200, + "final_url": "http://127.0.0.1:8765/static/style.css", + "response_sha256": "de95408663815d3c523d76a49c73f291bae565ab00faa83c96a2ebe86fbbfaa6", + "passed": true, + "elapsed_ms": 28.93 + }, + { + "method": "GET", + "path": "/api/presets", + "status": 200, + "final_url": "http://127.0.0.1:8765/api/presets", + "response_sha256": "09de81e08ac6b20e42bbfcdf0141d87e763f3e037aa30977b37086c89d1d83ab", + "passed": true, + "elapsed_ms": 3.01 + }, + { + "method": "POST", + "path": "/api/relational/normalize", + "request_sha256": "8620280aa10745b8d19bd9cbe96dca19ba6b13e63a3577c04a33ffc412b2706b", + "status": 200, + "final_url": "http://127.0.0.1:8765/api/relational/normalize", + "response_sha256": "792c67faa1854068bcda5c5e037bff728a9ec8a2b425fe67da7e826302e67c1c", + "passed": true, + "elapsed_ms": 92.99, + "preset": "ecommerce" + }, + { + "method": "POST", + "path": "/api/relational/normalize", + "request_sha256": "6d60e85b14af5b7fe8b02df1e0621caa55b18f5ca79cb720aa07afcb20429d5d", + "status": 200, + "final_url": "http://127.0.0.1:8765/api/relational/normalize", + "response_sha256": "effe7ca618c18e5cabac8ad6106093d98f7fe6153917d8efb56d35b76d33c3db", + "passed": true, + "elapsed_ms": 4.21, + "preset": "lossy_decomposition" + }, + { + "method": "POST", + "path": "/api/bqnf/simulate", + "request_sha256": "b129f3da473c83dd477e03e9154bc521c6fa07d1c0c8f9d7a4bbc5d79561d4dc", + "status": 200, + "final_url": "http://127.0.0.1:8765/api/bqnf/simulate", + "response_sha256": "279027ce2a31f451ad1e03fb0e8c0ec63fe3b6e0bdcf1c8e265ca15f07e43c0a", + "passed": true, + "elapsed_ms": 88.71, + "preset": "financial_ledger" + }, + { + "method": "POST", + "path": "/api/compiler/lower", + "request_sha256": "33dac6350d1c2aacfa4744beec696030729e3a6b2521779748dcc441b456bcf5", + "status": 200, + "final_url": "http://127.0.0.1:8765/api/compiler/lower", + "response_sha256": "4e37fefc27234f53411540e9e597e3da4df46fe2dff819f539dbf10d373def5b", + "passed": true, + "elapsed_ms": 32.0, + "preset": "employee_bcnf" + }, + { + "method": "POST", + "path": "/api/ai/lossledger", + "request_sha256": "59baf8400215a7d0fe4c330f1fdbd7ff04de370f3f991c9df6c2c8a1e0908138", + "status": 200, + "final_url": "http://127.0.0.1:8765/api/ai/lossledger", + "response_sha256": "02012e08df212dd1f880e894633aea6cbb4b74893cb9820b29a1a2a98682729a", + "passed": true, + "elapsed_ms": 31.15, + "preset": "rag_research_context" + }, + { + "method": "POST", + "path": "/api/relational/normalize", + "request_sha256": "8620280aa10745b8d19bd9cbe96dca19ba6b13e63a3577c04a33ffc412b2706b", + "status": 200, + "final_url": "http://127.0.0.1:8765/api/relational/normalize", + "response_sha256": "792c67faa1854068bcda5c5e037bff728a9ec8a2b425fe67da7e826302e67c1c", + "passed": true, + "elapsed_ms": 43.74, + "preset": "ecommerce" + }, + { + "method": "POST", + "path": "/api/relational/normalize", + "request_sha256": "6d60e85b14af5b7fe8b02df1e0621caa55b18f5ca79cb720aa07afcb20429d5d", + "status": 200, + "final_url": "http://127.0.0.1:8765/api/relational/normalize", + "response_sha256": "effe7ca618c18e5cabac8ad6106093d98f7fe6153917d8efb56d35b76d33c3db", + "passed": true, + "elapsed_ms": 44.48, + "preset": "lossy_decomposition" + }, + { + "method": "POST", + "path": "/api/bqnf/simulate", + "request_sha256": "b129f3da473c83dd477e03e9154bc521c6fa07d1c0c8f9d7a4bbc5d79561d4dc", + "status": 200, + "final_url": "http://127.0.0.1:8765/api/bqnf/simulate", + "response_sha256": "279027ce2a31f451ad1e03fb0e8c0ec63fe3b6e0bdcf1c8e265ca15f07e43c0a", + "passed": true, + "elapsed_ms": 12.31, + "preset": "financial_ledger" + }, + { + "method": "POST", + "path": "/api/compiler/lower", + "request_sha256": "33dac6350d1c2aacfa4744beec696030729e3a6b2521779748dcc441b456bcf5", + "status": 200, + "final_url": "http://127.0.0.1:8765/api/compiler/lower", + "response_sha256": "4e37fefc27234f53411540e9e597e3da4df46fe2dff819f539dbf10d373def5b", + "passed": true, + "elapsed_ms": 27.06, + "preset": "employee_bcnf" + }, + { + "method": "POST", + "path": "/api/ai/lossledger", + "request_sha256": "59baf8400215a7d0fe4c330f1fdbd7ff04de370f3f991c9df6c2c8a1e0908138", + "status": 200, + "final_url": "http://127.0.0.1:8765/api/ai/lossledger", + "response_sha256": "02012e08df212dd1f880e894633aea6cbb4b74893cb9820b29a1a2a98682729a", + "passed": true, + "elapsed_ms": 15.78, + "preset": "rag_research_context" + } + ], + "scope": "HTTP and semantic smoke only; deployment commit and browser UX require separate verification.", + "passed": true +} diff --git a/docs/verification/2026-11-02-public-http.json b/docs/verification/2026-11-02-public-http.json new file mode 100644 index 0000000..6fcaaf0 --- /dev/null +++ b/docs/verification/2026-11-02-public-http.json @@ -0,0 +1,19 @@ +{ + "checked_at_utc": "2026-11-02T22:03:07.463624+00:00", + "base_url": "https://smplxnf.anomalon.com", + "workers": 1, + "repeats": 1, + "tls_certificate_validation": "enabled", + "checks": [ + { + "method": "GET", + "path": "/healthz", + "passed": false, + "error": "", + "elapsed_ms": 95.37 + } + ], + "scope": "HTTP and semantic smoke only; deployment commit and browser UX require separate verification.", + "passed": false, + "remaining_checks": "skipped: health check failed" +} diff --git a/docs/verification/2026-11-02-render-candidate-http.json b/docs/verification/2026-11-02-render-candidate-http.json new file mode 100644 index 0000000..c923449 --- /dev/null +++ b/docs/verification/2026-11-02-render-candidate-http.json @@ -0,0 +1,20 @@ +{ + "checked_at_utc": "2026-11-02T22:05:47.540449+00:00", + "base_url": "https://simplxnf-live.onrender.com", + "workers": 1, + "repeats": 1, + "tls_certificate_validation": "enabled", + "checks": [ + { + "method": "GET", + "path": "/healthz", + "passed": false, + "error": "HTTP Error 404: Not Found", + "status": 404, + "elapsed_ms": 234.54 + } + ], + "scope": "HTTP and semantic smoke only; deployment commit and browser UX require separate verification.", + "passed": false, + "remaining_checks": "skipped: health check failed" +} diff --git a/scripts/http_smoke.py b/scripts/http_smoke.py new file mode 100644 index 0000000..b038705 --- /dev/null +++ b/scripts/http_smoke.py @@ -0,0 +1,143 @@ +"""Exercise a running Studio over HTTP; no checkout imports or extra packages. + +HTTPS uses normal certificate/hostname validation. HTTP is allowed only for +loopback development. Results describe this bounded run, not a load benchmark. +""" + +import argparse +from concurrent.futures import ThreadPoolExecutor +from datetime import datetime, timezone +import hashlib +import json +from pathlib import Path +import time +from urllib.error import HTTPError, URLError +from urllib.parse import urlsplit +from urllib.request import Request, urlopen + + +CASES = ( + ("relational", "ecommerce", "/api/relational/normalize"), + ("relational", "lossy_decomposition", "/api/relational/normalize"), + ("bqnf", "financial_ledger", "/api/bqnf/simulate"), + ("compiler", "employee_bcnf", "/api/compiler/lower"), + ("ai", "rag_research_context", "/api/ai/lossledger"), +) + + +def request(base, path, payload=None, timeout=30): + encoded = None if payload is None else json.dumps(payload).encode("utf-8") + req = Request(base + path, data=encoded, headers={"Content-Type": "application/json"}) + receipt = {"method": req.get_method(), "path": path} + if encoded is not None: + receipt["request_sha256"] = hashlib.sha256(encoded).hexdigest() + start = time.perf_counter() + try: + with urlopen(req, timeout=timeout) as response: + receipt["status"] = response.status + receipt["final_url"] = response.url + body = response.read() + receipt["response_sha256"] = hashlib.sha256(body).hexdigest() + receipt["passed"] = response.status == 200 and response.url == base + path + if "application/json" in response.headers.get("Content-Type", ""): + data = json.loads(body) + else: + data = body.decode("utf-8") + except (HTTPError, URLError, TimeoutError, OSError, ValueError) as exc: + receipt.update(passed=False, error=str(exc)) + if isinstance(exc, HTTPError): + receipt["status"] = exc.code + data = None + receipt["elapsed_ms"] = round((time.perf_counter() - start) * 1000, 2) + return receipt, data + + +def validate_case(family, name, data): + if family == "relational": + if name == "lossy_decomposition": + return (data["chase"]["is_implied"] is False + and data["chase"]["spurious_tuple_witness"] is not None) + return data["chase"]["is_implied"] is True and bool(data["candidate_keys"]) + if family == "bqnf": + north = data["final_cache"]["North"] + return north["total_sales"] == 280 and north["sale_count"] == 4 + if family == "compiler": + return (data["sqlite_verification"]["verified"] is True + and "CREATE TRIGGER" in data["sqlite_ddl"]["before_insert_trigger_sql"] + and bool(data["synthesized_counterexamples"])) + return (data["admitted"] is True + and data["token_accounting"]["token_conservation_commutes"] is True + and data["loss_ledger_integrity_verified"] is True) + + +def run(base, workers=1, repeats=1, timeout=30): + report = { + "checked_at_utc": datetime.now(timezone.utc).isoformat(), + "base_url": base, "workers": workers, "repeats": repeats, + "tls_certificate_validation": "enabled" if base.startswith("https:") else "not applicable (loopback)", + "checks": [], + "scope": "HTTP and semantic smoke only; deployment commit and browser UX require separate verification.", + } + checks = report["checks"] + health, data = request(base, "/healthz", timeout=timeout) + health["passed"] &= isinstance(data, dict) and data.get("status") == "ok" and data.get("service") == "smplxnf-studio" + checks.append(health) + if not health["passed"]: + report.update(passed=False, remaining_checks="skipped: health check failed") + return report + for path, marker in (("/", "Simplex-NF Studio"), ("/static/app.js", "/api/"), ("/static/style.css", "{")): + receipt, data = request(base, path, timeout=timeout) + receipt["passed"] &= isinstance(data, str) and marker in data + checks.append(receipt) + receipt, presets = request(base, "/api/presets", timeout=timeout) + receipt["passed"] &= isinstance(presets, dict) and all( + isinstance(presets.get(family), dict) and name in presets[family] + for family, name, _ in CASES + ) + checks.append(receipt) + if not receipt["passed"]: + report.update(passed=False, remaining_checks="skipped: preset catalog invalid") + return report + + def exercise(case): + family, name, path = case + receipt, data = request(base, path, presets[family][name], timeout) + receipt["preset"] = name + try: + receipt["passed"] &= validate_case(family, name, data) + except (KeyError, TypeError, ValueError) as exc: + receipt.update(passed=False, error="Unexpected response schema: " + str(exc)) + return receipt + + with ThreadPoolExecutor(max_workers=workers) as pool: + checks.extend(pool.map(exercise, CASES * repeats)) + report["passed"] = all(check["passed"] for check in checks) + return report + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("base_url") + parser.add_argument("--workers", type=int, choices=range(1, 5), default=1) + parser.add_argument("--repeats", type=int, choices=range(1, 11), default=1) + parser.add_argument("--timeout", type=float, default=30) + parser.add_argument("--output", type=Path) + args = parser.parse_args() + base = args.base_url.rstrip("/") + url = urlsplit(base) + if (url.scheme not in ("http", "https") or not url.hostname or url.username or url.password + or url.query or url.fragment or url.path + or (url.scheme == "http" and url.hostname not in ("localhost", "127.0.0.1", "::1"))): + parser.error("Use an HTTPS origin (or an HTTP loopback origin), without credentials, path, or query.") + if args.timeout <= 0: + parser.error("--timeout must be positive") + report = run(base, args.workers, args.repeats, args.timeout) + serialized = json.dumps(report, indent=2) + "\n" + if args.output: + args.output.write_text(serialized, encoding="utf-8") + print(serialized, end="") + return 0 if report["passed"] else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_frontend.cjs b/tests/test_frontend.cjs new file mode 100644 index 0000000..ee8ba11 --- /dev/null +++ b/tests/test_frontend.cjs @@ -0,0 +1,52 @@ +// Regression checks for the actual frontend request handler and chase renderer. +// Node built-ins only; run with: node tests/test_frontend.cjs +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); +const vm = require('node:vm'); + +function element() { + return { value: '', innerHTML: '', classList: { add() {}, remove() {} }, appendChild() {} }; +} +const elements = new Map(); +const getElement = id => { + if (!elements.has(id)) elements.set(id, element()); + return elements.get(id); +}; +let submitted; +const response = { + candidate_keys: [['A', 'C']], prime_attributes: ['A', 'C'], normal_forms: {}, + minimal_cover: [], synthesized_3nf: [], sql_ddl: '', + chase: { is_implied: false, steps: [], steps_count: 0, final_tableau: [], + distinguished_row_index: null, spurious_tuple_witness: { A: 'a_A', B: 'a_B', C: 'a_C' } }, +}; +const context = vm.createContext({ + document: { addEventListener() {}, getElementById: getElement, createElement: element }, + alert(message) { throw new Error(message); }, + fetch: async (url, options) => { + assert.equal(url, '/api/relational/normalize'); + submitted = JSON.parse(options.body); + return { ok: true, json: async () => response }; + }, +}); +vm.runInContext(fs.readFileSync(path.join(__dirname, '../web/static/app.js'), 'utf8'), context); + +(async () => { + getElement('relational-attributes-input').value = 'A, B, C'; + getElement('relational-dependencies-input').value = 'A -> B'; + for (const text of ['(A, B), (B, C)', '(A,B) ,(B,C)', 'A, B; B, C']) { + getElement('relational-components-input').value = text; + await context.executeRelationalNormalize(); + assert.deepEqual(submitted.custom_components, [['A', 'B'], ['B', 'C']]); + assert.match(getElement('relational-chase-status').innerHTML, /Lossy Join/); + assert.match(getElement('chase-witness-box').innerHTML, /a_C/); + assert.match(getElement('chase-steps-tbody').innerHTML, /no distinguished row/); + assert.equal(getElement('btn-run-relational').disabled, false); + } + getElement('relational-components-input').value = ''; + response.chase.is_implied = true; + await context.executeRelationalNormalize(); + assert.equal(submitted.custom_components, null); + assert.match(getElement('chase-steps-tbody').innerHTML, /already distinguished/); + console.log('Frontend component parsing and zero-step chase rendering passed.'); +})().catch(error => { console.error(error); process.exitCode = 1; }); diff --git a/web/static/app.js b/web/static/app.js index 0a7bab3..c7fc256 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -109,7 +109,7 @@ async function executeRelationalNormalize() { let custom_components = null; if (compStr.trim()) { // Parse "(A, B), (B, C)" or "A, B; B, C" - const parts = compStr.replace(/[()]/g, '').split(';').map(x => x.split(',').map(y => y.trim()).filter(Boolean)); + const parts = compStr.replace(/\)\s*,\s*\(/g, ';').replace(/[()]/g, '').split(';').map(x => x.split(',').map(y => y.trim()).filter(Boolean)); if (parts.length > 0 && parts[0].length > 0) { custom_components = parts; } @@ -226,7 +226,10 @@ function renderRelationalResults(data) { const tbody = document.getElementById('chase-steps-tbody'); tbody.innerHTML = ''; if (data.chase.steps.length === 0) { - tbody.innerHTML = 'Initial tableau was already distinguished (0 substitution steps needed).'; + const message = data.chase.is_implied + ? 'Initial tableau was already distinguished (0 substitution steps needed).' + : 'No substitutions applied; no distinguished row was reached.'; + tbody.innerHTML = `${message}`; } else { data.chase.steps.forEach(s => { const tr = document.createElement('tr'); diff --git a/wiki/Home.md b/wiki/Home.md index d915041..9ac0a1a 100644 --- a/wiki/Home.md +++ b/wiki/Home.md @@ -3,6 +3,11 @@ **AuthorityEffect:** `None` (`certified_implies_authorized: false`) **Total Canonical Normal Form Contracts:** **112** (each with its own dedicated Wiki treatise and visual SVG diagram) +**[Current project status and evidence levels](Project-Status):** PRs #17–19 are +merged; public Render deployment remains unverified. Contract pages describe +declarations and intended behavior. Their diagrams and “Certified Output” labels +are not execution receipts; see the status guide before interpreting those claims. + ![Simplex Normal Forms Architecture](images/simplex-normal-forms.svg) --- diff --git a/wiki/Project-Status.md b/wiki/Project-Status.md new file mode 100644 index 0000000..261f414 --- /dev/null +++ b/wiki/Project-Status.md @@ -0,0 +1,60 @@ +# Project status and evidence — 2026-11-02 + +The web MVP, correctness fixes and packaging work are merged. Public Render +deployment is a separate, unfinished gate. + +| Area | Verified state | +| --- | --- | +| MVP | [PR #17](https://github.com/dbccpage/simplex-normal-forms/pull/17) merged at `489f3e1` | +| Correctness | [PR #18](https://github.com/dbccpage/simplex-normal-forms/pull/18) merged at `d59f890`; tracked-field matching, exact quantile membership and per-region repair receipts corrected | +| Packaging / CI | [PR #19](https://github.com/dbccpage/simplex-normal-forms/pull/19) merged at `da498e5`; source and installed-distribution verification | +| Tests | 232 tests passed in premerge Python 3.12/3.14 runs; [merged-main hosted CI](https://github.com/dbccpage/simplex-normal-forms/actions/runs/41529136846) passed | +| Local HTTP | 15 smoke checks passed on 2026-11-02, including two repetitions of five API cases with two clients | +| DNS | User corrected CNAME to `smplxnf → smplxnf-live.onrender.com`; observed through resolver `1.1.1.1` at 22:07 UTC, TTL 3600 | +| Render / HTTPS | Supplied target's `/healthz` returned HTTP 404. The canonical hostname still failed through the local resolver at 22:08 UTC. No successful public workbench or custom-domain TLS check is recorded | + +The original Gemini/Teamwork notes' 136 pytest items / 89 unittest methods and +instructions to merge PR #17 are historical. A Render blueprint is configuration, +not proof that a service was provisioned. The new service must have the custom +domain registered in Render; the CNAME alone does not establish routing or TLS. + +## Catalog evidence levels + +All 112 contracts pass structural catalog checks. Per-contract `identity` +metadata reports 24 active, 87 draft and 1 deprecated; verification status is +35 executable, 75 absent and 2 missing; theory status is 48 established, +42 proposed, 11 experimental, 9 admitted and 2 missing. + +These are declared statuses, not independent verification of every contract. +The catalog index separately lists 25 active and 87 draft entries, plus a +deprecated ACNF entry; ACNF appears in both draft and deprecated lists. + +Generic diagram labels and “Certified Output” tables describe intended contract +behavior. They are not successful-run receipts. In particular, `verification_status: +absent` cannot establish an executable result. Proposed/admitted physics and +research-forum scenarios retain their stated assumptions. Claim-word scans do +not establish mathematical correctness or originality. No metadata status or +certification confers operational authority. + +The streaming API supports the bounded model documented in +[Executable scope](https://github.com/dbccpage/simplex-normal-forms/blob/main/docs/CORRECTNESS_SCOPE.md). +The compiler workbench demonstrates FD lowering and a constructed subsumption +comparison, rather than arbitrary DAG execution or compilation of the entire +catalog. AI token counts use whitespace splitting, not a model tokenizer. + +## Next work + +1. Create/inspect the Simplex service in the supplied Render project, deploy the + reviewed `main`, and record the deployed Git SHA and assigned hostname. +2. Confirm that hostname matches `smplxnf-live.onrender.com`; ask the user to + adjust their CNAME only if needed. Register `smplxnf.anomalon.com` as the + service's custom domain and verify its certificate. +3. Execute live HTTP and browser checks on the assigned Render origin and then + the custom domain. Fetch full request bodies from `/api/presets`; the old + handoff's `preset_id` examples do not match these API request models. +4. Review the follow-up browser fix for the lossy decomposition preset. Its + parenthesized components were being flattened into one component in the UI, + unlike the correct direct API call. Local verification now displays the + lossy witness; this follow-up is not included in baseline `da498e5`. +5. Audit contract evidence and replace generic wiki examples with executable + witnesses. Catalog size does not establish implementation breadth. diff --git a/wiki/_Sidebar.md b/wiki/_Sidebar.md index 3ea69de..f2ed525 100644 --- a/wiki/_Sidebar.md +++ b/wiki/_Sidebar.md @@ -1,5 +1,6 @@ ### 🏛️ Simplex Normal Forms - **[Home (Master Index)](Home)** +- **[Project Status & Evidence](Project-Status)** - **[Theory & Cochain Substrate](Theory-and-Cochain-Substrate)** - **[Observer-Relative Ontology](Observer-Relative-Ontology)** - **[Concrete Worked Examples](Concrete-Examples)** From d2e331aea62c1c2eff3686ab03681c49e8107515 Mon Sep 17 00:00:00 2001 From: "Jeremy H. Carroll" Date: Mon, 2 Nov 2026 17:26:34 -0500 Subject: [PATCH 2/2] Record valid TLS with incorrect SmplCache domain routing --- docs/DEPLOYMENT.md | 8 ++++++++ docs/PROJECT_STATUS.md | 11 +++++++++- .../2026-11-02-domain-service-mismatch.json | 20 +++++++++++++++++++ wiki/Project-Status.md | 10 +++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 docs/verification/2026-11-02-domain-service-mismatch.json diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 5f2e3db..432f7e2 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -38,6 +38,14 @@ Render hostname, then wait for their confirmation that the record is updated: assigned hostname before requesting another DNS change. The service still needs `smplxnf.anomalon.com` added in **Settings → Custom Domains**. +**Latest check, 22:25 UTC:** The certificate is valid, but the custom domain +serves **SmplCache Live** (`service: smplcache-live`, commit `810fc7cf0d99`). +The user's Render screenshot lists `smplcache-live.onrender.com` as the target. +Create/verify the separate Simplex service first, then move this custom-domain +association to it and align DNS with that service's actual hostname. Do not +replace or rename the existing SmplCache application to perform this migration. +See the [application-identity receipt](verification/2026-11-02-domain-service-mismatch.json). + | GoDaddy field | Value | | --- | --- | | Type | `CNAME` | diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md index bd6057d..8e88ad6 100644 --- a/docs/PROJECT_STATUS.md +++ b/docs/PROJECT_STATUS.md @@ -17,7 +17,8 @@ Implementation, catalog declarations, and public deployment have separate gates. | Corrected CNAME, 2026-11-02 22:07 UTC | Public resolver `1.1.1.1` returned `smplxnf.anomalon.com → smplxnf-live.onrender.com`, TTL 3600 | DNS record observed; assignment to the intended service not yet verified | | Supplied Render target, 2026-11-02 22:07 UTC | [Receipt](verification/2026-11-02-corrected-render-http.json): HTTPS `/healthz` returned 404 | Application health check failed | | Canonical hostname retry, 2026-11-02 22:08 UTC | [Receipt](verification/2026-11-02-corrected-domain-http.json): local resolver still failed | Propagation/cache discrepancy; no TLS verdict for this hostname | -| Render account/service state | Supplied project `prj-dbj5ei942hec73c30pu0`; dashboard required sign-in; no connected Render tool available during inspection | Service existence, deployed SHA, assigned hostname and certificate remain unverified | +| Latest custom-domain check, 2026-11-02 22:25 UTC | [Receipt](verification/2026-11-02-domain-service-mismatch.json): valid HTTPS, HTTP 200, health service `smplcache-live`, HTML title SmplCache Live | Wrong application attached to the domain; Studio acceptance failed | +| Render account/service state | User screenshot shows certificate issued and target `smplcache-live.onrender.com`; account inspection still unavailable | Custom domain is attached to the existing SmplCache application; intended Studio service remains unverified | GitHub Pages completing its own deployment does not establish that the FastAPI application is running on Render. The health response's fixed version `0.1.0` @@ -29,6 +30,14 @@ and a new-web-service URL. These are distinct from the proposed Simplex service. The earlier accidental `simplxnf` spelling and its failed HTTP/TLS probes are retained in the receipts as history. The canonical target remains `smplxnf`. +**Latest state:** DNS still points to `smplxnf-live.onrender.com`, but Render +serves SmplCache Live for the custom Host header. The certificate is now valid. +This is a routing/application mismatch, not a successful Studio deployment. +Create/verify the separate service from `dbccpage/simplex-normal-forms`, move +only this custom-domain association from the SmplCache service to it, and +align the CNAME with its assigned hostname. Preserve the existing SmplCache +service and its application. No Studio API POSTs were sent to that application. + Local browser checks exercised relational synthesis, streaming, compiler and AI workbenches. Switching to the lossy decomposition preset exposed a frontend parser defect: `(A, B), (B, C)` was flattened into one component and displayed a diff --git a/docs/verification/2026-11-02-domain-service-mismatch.json b/docs/verification/2026-11-02-domain-service-mismatch.json new file mode 100644 index 0000000..362ec86 --- /dev/null +++ b/docs/verification/2026-11-02-domain-service-mismatch.json @@ -0,0 +1,20 @@ +{ + "checked_at_utc": "2026-11-02T22:25:34.3109598Z", + "base_url": "https://smplxnf.anomalon.com", + "tls_validation": "default HTTPS certificate validation succeeded", + "dns_resolver": "1.1.1.1", + "cname_target": "smplxnf-live.onrender.com", + "health_status": 200, + "health": { + "status": "ok", + "service": "smplcache-live", + "api_version": "1.0.0", + "app_commit": "810fc7cf0d99", + "engine_commit": "810fc7cf0d99" + }, + "html_status": 200, + "html_title": "SmplCache Live — Certified SQL Cache Maintenance Demo", + "expected_service": "smplxnf-studio", + "passed": false, + "reason": "Custom domain serves SmplCache Live instead of Simplex-NF Studio; no Studio API POSTs attempted." +} diff --git a/wiki/Project-Status.md b/wiki/Project-Status.md index 261f414..3e7ae50 100644 --- a/wiki/Project-Status.md +++ b/wiki/Project-Status.md @@ -11,13 +11,21 @@ deployment is a separate, unfinished gate. | Tests | 232 tests passed in premerge Python 3.12/3.14 runs; [merged-main hosted CI](https://github.com/dbccpage/simplex-normal-forms/actions/runs/41529136846) passed | | Local HTTP | 15 smoke checks passed on 2026-11-02, including two repetitions of five API cases with two clients | | DNS | User corrected CNAME to `smplxnf → smplxnf-live.onrender.com`; observed through resolver `1.1.1.1` at 22:07 UTC, TTL 3600 | -| Render / HTTPS | Supplied target's `/healthz` returned HTTP 404. The canonical hostname still failed through the local resolver at 22:08 UTC. No successful public workbench or custom-domain TLS check is recorded | +| Render / HTTPS | At 22:25 UTC HTTPS succeeded and returned HTTP 200, but `/healthz` identified `smplcache-live` and the page title identified SmplCache Live. Certificate issuance is complete; the domain is attached to the wrong application | The original Gemini/Teamwork notes' 136 pytest items / 89 unittest methods and instructions to merge PR #17 are historical. A Render blueprint is configuration, not proof that a service was provisioned. The new service must have the custom domain registered in Render; the CNAME alone does not establish routing or TLS. +The latest user screenshot lists `smplcache-live.onrender.com` as Render's +target, while public DNS still points to `smplxnf-live.onrender.com`. The custom +domain currently serves the existing SmplCache application, commit `810fc7cf0d99`. +Create/verify the separate service from `dbccpage/simplex-normal-forms`, then +move this custom-domain association to it and align the CNAME with its assigned +hostname. Preserve the existing SmplCache service. No Studio API POSTs were +attempted against that application; HTTP 200 alone does not pass acceptance. + ## Catalog evidence levels All 112 contracts pass structural catalog checks. Per-contract `identity`