Skip to content
Merged
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
47 changes: 45 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,25 @@ src/sapiens/
budget.py cooperative step/time budgets and preemption exceptions
queue.py bounded SQLite job queue with leases/idempotency
daemon.py bounded background worker skeleton
cli.py synthetic-only demo
cli.py synthetic-only demo + `gates` subcommand
adapters/ synthetic adapters + Phase-4 real-data Kepler adapter
(published-signal re-derivation; validators sandboxed
behind the adapter contract)
gates/ discovery-gate hardening (scout Phases 0–5), stdlib-only:
surprise.py G-03 literature-measured surprise + G-07 robust full-set baseline
nulls.py Phase-1 mandatory null-provenance records (+FP-04 state)
fdr.py family-wide Benjamini-Hochberg + sigma<->p conversions
thresholds.py decoupled ENTRY/RANK/CONFIRM + B-02/B-03/B-05 fixes
devils_advocate.py B-06 permutation test on the confound-independence null
promotion.py recalibrated promotion_score + anomaly_priority (G-05/G-06/FP-06)
pipeline.py per-family orchestration -> GateOutcome + bounded shortlist
corpus.py gaming vectors + ~20 historical positives + decoys
blind.py Phase-3 blind re-run harness (strip/expand/custody/grade)
dossier.py Phase-4 referee dossier, tiered authority, override log
criteria.py Phase-5 automated success-criteria suite
```

Dependency rule: `sapiens` core modules do not import `sapiens.adapters`; tests enforce this. Adapters import core contracts.
Dependency rule: `sapiens` core modules and `sapiens.gates` do not import `sapiens.adapters`; tests enforce this. Adapters import core contracts. The `gates` subpackage is self-contained (stdlib only) and copies no ASTRA/GEODISC/BIODISC/SLATE code — a test enforces the clean-room invariant on it too.

## DomainAdapter contract

Expand Down Expand Up @@ -78,4 +90,35 @@ Hash chaining detects tampering but does **not** prove authorship, scientific tr

`WorkQueue` gives bounded jobs, serialized payload-size limits, idempotency keys, leases, stale-lease rejection, and retry/dead states. `DiscoveryDaemon.run_bounded` executes only explicitly registered handlers under time/step budgets; it does not dynamically import or shell out from queue payloads.

## Discovery-gate hardening (`sapiens.gates`, scout Phases 0–5)

A separate, stdlib-only subpackage that hardens the *discovery-decision* gates
against gaming and null-model incompleteness. It does not touch the L0→L4
foundation; it consumes a domain-neutral `GateInputs` (adapters translate their
measurements into it) and returns a `GateOutcome`/`FamilyResult`.

Data flow for a candidate family: `evaluate_family` builds a family-wide
Benjamini-Hochberg p-value set from each candidate's **null-provenance**
σ (`nulls.NullProvenance`, only counted when the null is *calibratable* — data
fetched and no un-excluded instrument systematic); computes `promotion_score`
(recalibrated weights, never penalising mechanism-absence) and
`anomaly_priority` (mechanism-absence boost gated by **literature-measured
surprise**, G-03, and the **conservation-law guard**, FP-06); then applies the
decoupled thresholds — **ENTRY** = 3σ-equiv AND family-FDR survival AND a
calibratable null (so an UNCALIBRATED candidate is *surfaced, never admitted*,
G-06); **RANK** = continuous `promotion + anomaly`; **CONFIRM** = domain-specific
claim bar. Reserved paradigm-breaker slots (`reserved_slot_eligible`, G-05:
`promotion_score ≥ 0.30` + surprise + the UNEXPLAINED_CONFIRMED signature) get
guaranteed representation in a bounded top-K shortlist.

The G-07 baseline receipt (`surprise.robust_baseline`) computes median/MAD-σ over
**every** point and records the count, so a "clean-subset" gamed baseline is
structurally impossible through the API. `criteria.run_success_criteria` runs the
five Phase-5 ship gates; `blind` provides the sealed, separate-custody re-run
harness; `dossier` builds the human referee bundle (with the strongest
disconfirming explanation forced next to each claim) and enforces that only
CONFIRM-tier candidates are autonomous-claim-eligible. Everything is a pure
function or frozen dataclass; nothing here claims a discovery
(`scientific_discoveries_claimed = 0` remains true by construction).

Synthetic and CORE (first-party, real-data) adapters run in-process with cooperative preemption. UNTRUSTED (third-party) adapters run only via `sapiens.isolation`: a child process applies POSIX rlimits (CPU, address space, open files) to itself, the parent enforces a wall-clock timeout, and every failure mode is contained fail-closed (no evidence on failure). Third-party adapters additionally require a recorded owner permission (`permissions.json`, empty by default) before the registry will validate them at all. rlimits bound resources; they are not a full security sandbox.
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@

SAPIENS is an **experimental platform for traceable cross-domain
scientific-discovery workflows** — a `DomainAdapter` boundary, a hash-chained
L0→L4 evidence ledger, and kernel-gated promotions. **Phases 0–4 shipped (v0.5.0): three
synthetic adapters plus one real-data Kepler photometry adapter that
re-derives a published signal — no discoveries claimed.**
L0→L4 evidence ledger, and kernel-gated promotions. **Foundation phases 0–4
shipped: three synthetic adapters plus one real-data Kepler photometry adapter
that re-derives a published signal. v0.6.0 adds the `sapiens.gates`
discovery-gate-hardening track (anti-gaming seams, a mandatory logged null
layer, decoupled thresholds, a blind re-run harness, and a human-gate dossier
layer). No discoveries claimed.**

It provides the plumbing a discovery system needs before it can be trusted:
a domain-neutral adapter boundary, an append-only hash-chained evidence
Expand Down Expand Up @@ -68,11 +71,17 @@ src/sapiens/
discovery.py autonomous discovery driver — proposes candidates and
climbs them L0→L3 under budgets via the daemon; L4 stays
human-gated
cli.py synthetic-only demo entrypoint
cli.py synthetic-only demo entrypoint (+ `gates` subcommand)
adapters/ three deterministic synthetic adapters (linear, threshold,
periodic-signal photometry) plus the Phase-4 real-data
Kepler photometry adapter (re-derivation of a published
signal; no discovery claimed)
gates/ discovery-gate hardening (scout Phases 0–5), stdlib-only:
anti-gaming seams (G-03/05/06/07 + FP-06 guard), a mandatory
logged null layer + family-wide BH-FDR, decoupled
ENTRY/RANK/CONFIRM thresholds, a blind re-run harness, a
human-gate dossier layer, and an automated success-criteria
suite — self-contained, imports no adapters, claims nothing
```

Key design rules (enforced by tests in [`tests/`](tests/)):
Expand All @@ -97,6 +106,12 @@ Key design rules (enforced by tests in [`tests/`](tests/)):
every time.
- **Bounded autonomy** — background work runs through a bounded SQLite queue
with leases, cooperative budgets, and preemption; no unbounded loops.
- **Gate-hardening is clean-room too** — `sapiens.gates` implements the
discovery-decision hardening (anti-gaming + null layer + decoupled thresholds
+ blind harness + human-gate dossier) from spec only; a test enforces it
imports no adapters and copies no ASTRA-family code. Run it with
`sapiens gates` (full success-criteria suite) or `sapiens gates --mode gaming`
(the Phase-0 gaming re-test, which reports **0 leaks**).

Full details: [`ARCHITECTURE.md`](ARCHITECTURE.md) ·
[`VALIDATION.md`](VALIDATION.md) · [`PROVENANCE.md`](PROVENANCE.md) ·
Expand Down
81 changes: 81 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,84 @@ Real domain adapters:
- Human L4 gates.
- Reproduction bundles.
- Prediction tracking and demotion on contradicting evidence.

---

# Discovery-Gate Hardening (scout HOW-TO-PROCEED Phases 0–5) — shipped v0.6.0

> **Distinct track, distinct numbering.** The *foundation* phases above (0–4)
> built the ledger/adapter/review plumbing. This second track hardens the
> **discovery-decision gates** against gaming and null-model incompleteness,
> following `beast-scout`'s blind red-team benchmark. It ships as the
> self-contained, stdlib-only `sapiens.gates` subpackage and changes **no**
> foundation behaviour. Honesty invariant unchanged:
> `scientific_discoveries_claimed = 0`.

Status legend: **code-complete** (implemented + tested) · **library-complete**
(decision-support code done; the surrounding human workflow is process, not
code) · **synthetic-corpus** (validated on spec-derived synthetic profiles, not
real datasets — the real measurement is a prospective trial).

### DG-0 — weld the 4 gaming seams — **code-complete**
- **G-03** anomaly boost is conditioned on *literature-measured surprise*
(`gates.surprise`), never mere absence of a mechanism.
- **G-05** reserved paradigm-breaker slots require `promotion_score >= 0.30`
**and** the G-03 surprise condition (`gates.promotion.reserved_slot_eligible`).
- **G-06** L2-holdout-passed is a *prerequisite* for CALIBRATED; the additive
score only ranks *within* the calibrated set (`gates.promotion`,
`gates.pipeline` entry gate).
- **G-07** MAD/σ baseline is computed on the **full dataset** with a robust
estimator that consumes every point (`gates.surprise.robust_baseline`).
- **FP-06** conservation-law guard: a violation ⇒ correct null is measurement
error ⇒ no boost/entry without reproducible orthogonal confirmation.
- **Exit met:** the 7 gaming vectors re-run to **0 leaks** (`gates.criteria.run_gaming_retest`; test `test_phase0_exit_zero_gaming_leaks`).

### DG-1 — mandatory, logged null layer — **code-complete**
- Every candidate carries a `NullProvenance` record (which null, external data
required/fetched y/n, σ-under-null); no null ⇒ **UNCALIBRATED**, never a
silent pass (`gates.nulls`).
- **Family-wide** BH-FDR across the candidate family (`gates.fdr`) — the
multiplicity/look-elsewhere control (FP-09/FP-10).
- **FP-04** "instrument-systematic-not-excluded" is an explicit state that
surfaces to the human instead of passing as a clean 6σ.

### DG-2 — decoupled threshold architecture — **code-complete**
- **ENTRY** = 3σ-equiv AND survives family BH-FDR q<0.05; **RANK** = continuous
σ/effect; **CONFIRM** = 5σ (physics) / FDR<0.05 + orthogonal replication (bio)
/ formal proof-check (math) (`gates.thresholds`, `gates.pipeline`).
- Boundary fixes: **B-02** adaptive `ci_floor`, **B-03** hash-committed
thresholds, **B-05** continuous degree-of-calibration, **B-06** DevilsAdvocate
r≥0.90 → permutation test at ~3σ-equiv (`gates.devils_advocate`).

### DG-3 — genuine blind re-run harness — **code-complete (synthetic-corpus)**
- Strip `ground_truth`/`expected_verdict`/`gates_probed`/`how_*`; expand with
~20 historical positives + fresh decoys; separate-custody key with a
published SHA-256 commitment; blind scoring runner + grade
(`gates.blind`, `gates.corpus`).
- *Honest scope:* the positives/decoys are **spec-derived synthetic profiles**,
and separate custody is implemented in-process. A real sealed set on real
post-cutoff data with a genuine second-party custody ceremony is the
next-stage (external) test this unblocks.

### DG-4 — human-in-loop final gate — **library-complete**
- Per-candidate dossier (σ, FDR-q, null used, external-data y/n, replication
status, UNCALIBRATED/instrument flags, and the single strongest
**disconfirming** explanation next to the claim); tiered authority
(only CONFIRM-tier is autonomous-claim-eligible, human co-sign still required);
bounded top-K throttle; override log feeding recalibration (`gates.dossier`,
`gates.pipeline` shortlist).
- *Honest scope:* this is the decision-support **data + eligibility layer**; the
reviewer UI/workflow itself is out of repo scope.

### DG-5 — success-criteria suite — **code-complete (synthetic-corpus)**
- Automated `gates.criteria.run_success_criteria` checks all five ship gates:
0 gaming leaks · null layer mandatory+logged (100% of shortlisted) · blind
kill-rate ≥ 8/10 + recovery ≥ 8/10 + abstention < 10% · boundary stability
≤ 1 flip/item over 3 seeds · thresholds hash-committed per run. All pass on
the synthetic corpus (`test_success_criteria_all_pass`).
- *Honest scope:* retrospective/synthetic gates are an intuition pump. The
**only** real validation is a prospective, pre-registered precision@K /
false-positive-rate trial on a post-cutoff stream — stated in the suite output
and not faked here.

CLI: `sapiens gates` (full suite) · `sapiens gates --mode gaming` (Phase-0 re-test).
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "sapiens-discovery"
version = "0.5.0"
version = "0.6.0"
description = "Experimental foundation for traceable cross-domain scientific-discovery workflows"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
4 changes: 3 additions & 1 deletion src/sapiens/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""SAPIENS: experimental, traceable scientific-discovery workflow plumbing."""

from . import gates
from .adapter import DomainAdapter
from .bridge import TransferEnvelope, transfer
from .calibration import CalibrationReport, run_calibration
Expand Down Expand Up @@ -43,9 +44,10 @@
"ValidationGates",
"aggregate_confidence",
"fixture_suite",
"gates",
"reference_panel",
"run_calibration",
"score_panel",
"transfer",
]
__version__ = "0.5.0"
__version__ = "0.6.0"
32 changes: 31 additions & 1 deletion src/sapiens/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,27 @@ def run_discovery(
}


def run_gates(*, mode: str = "criteria") -> dict[str, object]:
"""Run the discovery-gate-hardening checks (scout Phases 0-5).

``mode="criteria"`` runs the full Phase-5 success-criteria suite;
``mode="gaming"`` runs only the Phase-0 gaming re-test. Neither claims a
discovery — this is evidence-hygiene plumbing.
"""
from .gates.criteria import run_gaming_retest, run_success_criteria

if mode == "gaming":
retest = run_gaming_retest()
return {
"experimental": True,
"scientific_discoveries_claimed": 0,
"gaming_vectors_checked": retest.checked,
"gaming_leaks": list(retest.leaks),
"clean": retest.clean,
}
return run_success_criteria().to_dict()


@contextmanager
def _workdir(path: Path | None):
if path is not None:
Expand All @@ -134,9 +155,18 @@ def main() -> None:
discover_parser.add_argument("--steps-per-job", type=int, default=20)
discover_parser.add_argument("--limit-per-adapter", type=int, default=2)

gates_parser = sub.add_parser(
"gates", help="run discovery-gate-hardening checks (scout Phases 0-5)"
)
gates_parser.add_argument(
"--mode", choices=["criteria", "gaming"], default="criteria"
)

args = parser.parse_args()

if args.command == "discover":
if args.command == "gates":
result = run_gates(mode=args.mode)
elif args.command == "discover":
with _workdir(args.workdir) as workdir:
result = run_discovery(
workdir,
Expand Down
Loading
Loading