Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions AGENT_HANDOFF.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,35 @@
> **Update this file with every PR and every push to main.**
> Last updated: 2026-08-12

## 2026-08-12 Private Morris authority host (#4142 R13.5)

Branch `codex/4142-morris-authority-host` starts at exact authority-bridge
parent `3c95dcaf88c4a0eacc747b48678e1f5c225f12ec`. The standalone Rate React
launcher now owns an ephemeral child-process authority for exactly the Vite
development-server lifetime. The child binds IPv4 `127.0.0.1:0`, announces a
canonical numeric port over a bounded private pipe, proves the exact
authenticated capability document, and exits through an authenticated graceful
control request with bounded terminate/kill fallback. The bearer is redacted
from runtime representations, never uses a `VITE_` variable, and is injected
only into Vite's server-side `/api/rate-of-closure` proxy. Every response is
`no-store`/`nosniff`; there is no CORS, docs, OpenAPI, browser token, or browser
authority endpoint. Authenticated 404, validation, and sanitized unhandled-500
responses retain the same headers without exposing exception details.
`KeyboardInterrupt` and `SystemExit` during post-spawn readiness reap the child
and close the bounded readiness pipe before propagating unchanged; secondary
terminate/wait/pipe failures are contained and cannot replace the primary
startup exception.

The canonical authority prefix is `/api/rate-of-closure/v1`; capability is
`/api/rate-of-closure/v1/morris/capabilities`. Host lifespan owns the injected
registry exactly once after startup transfer; the child closes it when socket,
app, or server setup fails before lifespan. Listener and registry cleanup are
both attempted, with secondary failures contained whenever a primary setup
error is active. Optional `rate-morris-authority` dependencies are
FastAPI, Uvicorn, and SciPy. This is a local development-launch host, not a
static-preview or deployed-host contract. UI polling/presentation, export,
persistence, UpstreamDrift consumption, and completion of #4142 remain open.

## 2026-08-12 Rate Morris authority bridge (#4142 R13.5)

Exact request/job v1 contracts and `RateMorrisService` now bridge the current
Expand Down
5 changes: 3 additions & 2 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
| **Owner** | D-sorganization |
| **Primary Language(s)** | Python 3.11+, Rust, JavaScript, TypeScript |
| **License** | MIT |
| **Current Version** | 1.16.28 |
| **Spec Version** | 1.16.28 |
| **Current Version** | 1.16.29 |
| **Spec Version** | 1.16.29 |
| **Last Spec Update** | 2026-08-12 |

## 2. Purpose & Mission
Expand Down Expand Up @@ -2922,6 +2922,7 @@ Active development with stable core, continuous tool expansion, and web API in p

| Date | Version | Changes |
| ---- | ------- | ------- |
| 2026-08-12 | 1.16.29 | feat(rate-of-closure, #4142 R13.5): add a bounded private Morris authority host for the standalone React development launcher: exclusive ephemeral IPv4 loopback child socket, exact authenticated capability readiness, redacted bearer, no-store/nosniff no-CORS FastAPI host including sanitized authenticated errors, explicit pre-lifespan-to-ASGI exact-once registry ownership transfer, BaseException-safe startup cleanup and pipe closure that preserve the primary error through secondary cleanup failures, graceful authenticated shutdown with bounded reap fallback, and a strict server-only Vite proxy at the canonical `/api/rate-of-closure/v1` prefix. Declare the optional FastAPI/Uvicorn/SciPy host dependencies and retain UI polling/presentation, export, persistence, static or deployed authority hosting, UpstreamDrift consumption, and epic completion as open gates. |
| 2026-08-12 | 1.16.28 | feat(rate-of-closure, #4142 R13.5): add exact primitive-only Morris request/job v1 contracts, deterministic execution into unchanged report v1, a dependency-injected mountable FastAPI router with strict bounded raw JSON and lock-linearized ephemeral jobs, and a strict TypeScript parser plus injected transport. Retain presentation, export, persistence, host registration, UpstreamDrift consumption, and a genuine fixed-ball double-pendulum hit as open gates. |
| 2026-08-12 | 1.16.27 | feat(rate-of-closure, #4142 R13.3): add the bounded Rate fixed-ball Morris evaluator for ten exact global simulation variables and the current 17-scalar output contract; extract shared trial capture/projection so ensemble and Morris execution retain identical hit/miss/numerical-failure availability, apply samples through one public immutable config seam, reject fixed-contact timing no-ops/localized or invalid factors, and validate a genuine double-pendulum miss while retaining double-pendulum fixed-hit validation, UI/export, per-sample failure diagnostics, and UpstreamDrift consumption as open gates. |
| 2026-08-12 | 1.16.26 | feat(rate-of-closure, #4142 R13.3): add a bounded UI-neutral Morris execution adapter with immutable physical sample identity, injected typed evaluators that explicitly normalize their own domain failures, exact per-output availability, deterministic serial/parallel tensors and completed-prefix progress every eight samples plus final, cooperative no-partial-result cancellation, and named worker/sample/observation-cell resource limits; retain Rate, UI, export, and `evaluate_run` integration as later scope. |
Expand Down
30 changes: 30 additions & 0 deletions docs/development/RATE_OF_CLOSURE_CAMPAIGN_HANDOFF.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
# Rate of Closure Campaign Handoff

## 2026-08-12 #4142 R13.5 private authority host

- The standalone Rate React launcher now owns one ephemeral authority child
around the Vite development server. The child exclusively binds IPv4
`127.0.0.1:0`; the parent accepts only a bounded canonical port line and an
exact authenticated capability response before launching Vite.
- The canonical API is `/api/rate-of-closure/v1`; capability discovery is
`/api/rate-of-closure/v1/morris/capabilities` and advertises the exact request
and job schema IDs. All routes require the generated bearer and return
`Cache-Control: no-store` plus `X-Content-Type-Options: nosniff`; there is no
CORS, docs, OpenAPI, browser credential, or browser-visible target variable.
- Vite's development server alone receives `ROC_MORRIS_AUTHORITY_URL` and
`ROC_MORRIS_AUTHORITY_TOKEN` and proxies `/api/rate-of-closure`. Targets are
strict credential-free IPv4-loopback origins with valid explicit ports;
tokens are visible ASCII. Preview/static hosting intentionally has no proxy.
- Registry ownership is exact-once through FastAPI lifespan. Parent shutdown is
authenticated and graceful first, with bounded terminate/kill fallback;
startup interrupts and launch errors also reap the child and close its pipe.
Secondary reap/pipe errors are contained without replacing the original
startup exception. Listener and registry cleanup are both attempted even
when either cleanup operation fails.
Before lifespan transfer, child-side setup failures close the registry;
afterward ASGI lifespan owns the one close. Authenticated 404, 422, and
sanitized unhandled-500 responses retain security headers. Real child smoke, host
lifecycle/security tests, proxy rejection tests, TypeScript gates, and the
production web build are green locally.
- This does not add the Morris UI, polling, persistence, export, production
authority deployment, or UpstreamDrift consumption and does not complete
#4142.

## 2026-08-12 #4142 R13.5 bounded authority bridge

- Added exact request/job v1 contracts, deterministic Rate execution, bounded
Expand Down
9 changes: 9 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ dependencies = [
]

[project.optional-dependencies]
# Private loopback authority used by the Rate React launcher.
rate-morris-authority = [
"fastapi>=0.100.0",
"scipy>=1.10.0,<1.18",
"uvicorn>=0.30.0",
]
# Full installation with all features
all = [
"scipy>=1.10.0,<1.18",
Expand All @@ -40,6 +46,8 @@ all = [
"mujoco>=3.0.0",
"PyQt6>=6.5.0",
"sympy>=1.12",
"fastapi>=0.100.0",
"uvicorn>=0.30.0",
]
# AI and simulation models
ai = [
Expand Down Expand Up @@ -152,6 +160,7 @@ test = [
"python-multipart>=0.0.7",
"requests>=2.32.0",
"sqlmodel>=0.0.22",
"uvicorn>=0.30.0",
]
# Development tools
dev = [
Expand Down
28 changes: 28 additions & 0 deletions src/rate_of_closure/AGENT_HANDOFF.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,34 @@
> **Update this file with every PR and every push to main.**
> Last updated: 2026-08-12

## 2026-08-12 Private child authority and Vite proxy (#4142 R13.5)

`application/morris/{host,child,runtime}.py` is the bounded local host slice.
The child alone owns an exclusive IPv4 loopback port-zero socket; parent
readiness requires the exact bearer-authenticated capability document at
`/api/rate-of-closure/v1/morris/capabilities`, including both request and job
schema identities. Parent probes use direct `http.client` connections so proxy
environment variables cannot receive the credential. Host lifespan closes an
injected `MorrisJobRegistry` exactly once; shutdown first uses an authenticated
control route and then a bounded reap fallback. `runtime.py` does not import the
Uvicorn-owning child module and redacts its token from `repr`. Post-spawn
`BaseException` paths reap before re-raising and every final reap closes the
readiness pipe; cleanup failures are generically logged and never replace the
original startup exception. Pre-lifespan setup failures remain child-owned;
listener and registry cleanup are both attempted without masking setup errors; successful
lifespan startup explicitly transfers registry ownership, avoiding leaks and
double close. Sanitized authenticated 500s, plus 404/422 paths, retain
no-store/nosniff headers.

`launch_web.py` wraps the complete shared Vite launcher call in that runtime.
The Vite development server proxies `/api/rate-of-closure` using only
`ROC_MORRIS_AUTHORITY_URL` and `ROC_MORRIS_AUTHORITY_TOKEN`; strict validation
rejects non-loopback, credentialed, path/query/fragment, invalid-port, and
header-unsafe configuration. Preview/static builds do not proxy the authority,
and no `VITE_` value or CORS path exposes the endpoint/token to browser code.
The React client defaults to the canonical same-origin v1 prefix. UI startup,
polling, display, export, persistence, and non-development hosting remain open.

## 2026-08-12 Morris authority bridge (#4142 R13.5)

`application/morris/` now provides strict primitive request/job v1 contracts,
Expand Down
96 changes: 96 additions & 0 deletions src/rate_of_closure/application/morris/child.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"""Child-process entry point owning the authority's ephemeral loopback socket."""

from __future__ import annotations

import logging
import os
import socket
import sys

import uvicorn

from .host import create_morris_authority_app
from .router import MorrisJobRegistry
from .service import RateMorrisService

AUTHORITY_TOKEN_ENV = "ROC_MORRIS_AUTHORITY_CHILD_TOKEN"
logger = logging.getLogger(__name__)


def _private_token() -> str:
token = os.environ.pop(AUTHORITY_TOKEN_ENV, "")
if len(token) < 8:
raise RuntimeError("missing Morris authority child token")
return token


def _cleanup(
listener: socket.socket,
registry: MorrisJobRegistry | None,
lifespan_owns_registry: bool,
) -> None:
"""Attempt every child cleanup without replacing an active primary error."""
preserve_primary = sys.exception() is not None
cleanup_error: BaseException | None = None
try:
listener.close()
except BaseException as error:
cleanup_error = error
logger.warning("Morris authority listener cleanup failed")
if registry is not None and not lifespan_owns_registry:
try:
registry.close()
except BaseException as error:
if cleanup_error is None:
cleanup_error = error
logger.warning("Morris authority registry cleanup failed")
if cleanup_error is not None and not preserve_primary:
raise cleanup_error.with_traceback(cleanup_error.__traceback__)


def main() -> int:
"""Bind IPv4 loopback port zero, announce its port, and serve until stopped."""
token = _private_token()
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
registry: MorrisJobRegistry | None = None
lifespan_owns_registry = False

def transfer_registry() -> None:
nonlocal lifespan_owns_registry
lifespan_owns_registry = True

try:
registry = MorrisJobRegistry(RateMorrisService())
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0)
exclusive = getattr(socket, "SO_EXCLUSIVEADDRUSE", None)
if exclusive is not None:
listener.setsockopt(socket.SOL_SOCKET, exclusive, 1)
listener.bind(("127.0.0.1", 0))
listener.listen(socket.SOMAXCONN)
port = int(listener.getsockname()[1])
holder: dict[str, uvicorn.Server] = {}
app = create_morris_authority_app(
token,
registry,
lambda: setattr(holder["server"], "should_exit", True),
lifespan_started=transfer_registry,
)
config = uvicorn.Config(
app,
log_level="warning",
access_log=False,
server_header=False,
)
server = uvicorn.Server(config)
holder["server"] = server
print( # noqa: T201 - private parent/child readiness protocol
port, flush=True
)
server.run(sockets=[listener])
return 0
finally:
_cleanup(listener, registry, lifespan_owns_registry)


if __name__ == "__main__":
raise SystemExit(main())
108 changes: 108 additions & 0 deletions src/rate_of_closure/application/morris/host.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""Authenticated loopback-only FastAPI host for the Morris authority router."""

from __future__ import annotations

import hmac
from collections.abc import AsyncIterator, Awaitable, Callable
from contextlib import asynccontextmanager

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from starlette.responses import Response

from .contracts import MORRIS_JOB_SCHEMA_ID, MORRIS_REQUEST_SCHEMA_ID
from .router import MorrisJobRegistry, create_morris_router

API_PREFIX = "/api/rate-of-closure/v1"
CAPABILITY_PATH = f"{API_PREFIX}/morris/capabilities"
_CAPABILITY = {
"schema_id": "rate-of-closure/morris-authority-capability",
"schema_version": 1,
"available": True,
"api_prefix": API_PREFIX,
"request_schema_id": MORRIS_REQUEST_SCHEMA_ID,
"job_schema_id": MORRIS_JOB_SCHEMA_ID,
}


def _token(value: object) -> str:
if not isinstance(value, str) or len(value) < 8 or value != value.strip():
raise ValueError("authority token must be a nonempty private token")
if any(ord(character) < 33 or ord(character) > 126 for character in value):
raise ValueError("authority token must contain visible ASCII only")
return value


def create_morris_authority_app(
token: str,
registry: MorrisJobRegistry,
shutdown: Callable[[], None] | None = None,
*,
lifespan_started: Callable[[], None] | None = None,
) -> FastAPI:
"""Build a no-CORS, bearer-authenticated mountable authority app."""
secret = _token(token)
if not isinstance(registry, MorrisJobRegistry):
raise TypeError("registry must be a MorrisJobRegistry")

@asynccontextmanager
async def lifespan(_app: FastAPI) -> AsyncIterator[None]:
if lifespan_started is not None:
lifespan_started()
try:
yield
finally:
registry.close()

app = FastAPI(
title="Rate Morris Authority",
docs_url=None,
redoc_url=None,
openapi_url=None,
lifespan=lifespan,
)

def secured(response: Response) -> Response:
response.headers["Cache-Control"] = "no-store"
response.headers["X-Content-Type-Options"] = "nosniff"
return response

@app.exception_handler(Exception)
async def internal_error(_request: Request, _error: Exception) -> Response:
return secured(
JSONResponse({"error": "internal server error"}, status_code=500)
)

@app.middleware("http")
async def secure(
request: Request,
call_next: Callable[[Request], Awaitable[Response]],
) -> Response:
supplied = request.headers.get("authorization", "")
expected = f"Bearer {secret}"
response: Response
if not hmac.compare_digest(supplied, expected):
response = JSONResponse(
{"error": "authentication required"}, status_code=401
)
response.headers["WWW-Authenticate"] = "Bearer"
else:
response = await call_next(request)
return secured(response)

@app.get(CAPABILITY_PATH)
async def capability() -> dict[str, object]:
return dict(_CAPABILITY)

@app.post("/_control/shutdown")
async def stop_child() -> dict[str, str]:
if shutdown is None:
return {"status": "unavailable"}
shutdown()
return {"status": "stopping"}

app.include_router(create_morris_router(registry), prefix=API_PREFIX)
return app


__all__ = ["API_PREFIX", "CAPABILITY_PATH", "create_morris_authority_app"]
Loading
Loading