Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
248ef78
test(flow): enforce import layers for the core runtime and verifier
abrichr Nov 4, 2026
5d3116b
test(flow): link the import-layer exception to its public issue
abrichr Nov 4, 2026
573a5fd
Merge remote-tracking branch 'origin/main' into agent/136
abrichr Nov 4, 2026
e7c7aca
chore(flow): register import_layers.json in the public artifact inven…
abrichr Nov 4, 2026
05c8bdf
test(flow): import-layer checker reads literal dynamic imports and la…
abrichr Nov 5, 2026
3a08641
test(flow): import-layer checks refuse an empty policy and walk past …
abrichr Nov 5, 2026
6a6eb06
test(flow): resolve importer bindings and table use in the import-lay…
abrichr Nov 5, 2026
2fce127
test(flow): resolve importer bindings in their lexical scope and foll…
abrichr Nov 5, 2026
7be4934
Inject proof fixture sources across runtime entry points
abrichr Nov 9, 2026
0a372ff
Merge remote-tracking branch 'origin/main' into HEAD
abrichr Nov 9, 2026
52dce56
Bound import provenance analysis and pin unresolved migration debt
abrichr Nov 9, 2026
b8f0c42
fix: inventory the import layer debt configuration
abrichr Nov 9, 2026
70664c5
chore: lower resolved engine generality debt
abrichr Nov 9, 2026
f0ccde4
Merge origin/main into agent/1912
abrichr Nov 9, 2026
6c9a7cb
test(import-layers): move the teach_scope debt row to line 186 after …
abrichr Nov 9, 2026
a0aa5a7
test: the sealed-at-rest disk watch writes no bytecode while armed
abrichr Nov 9, 2026
ba61d8b
Merge remote-tracking branch 'origin/main' into HEAD
abrichr Nov 9, 2026
ebe3381
test(import-layers): move the deployment debt row to line 2489 after …
abrichr Nov 9, 2026
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
2 changes: 1 addition & 1 deletion packages/flow/benchmark/effect_e2e/prove_check.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""The effect_e2e record service as a fixture for proving a proposed check.

``openadapt-flow scaffold-verifier --prove effect-e2e`` runs a proposed record
``python -m benchmark.record_check_proof scaffold-verifier --prove effect-e2e`` runs a proposed record
check through the real replayer against this service, one run per fault in
:data:`benchmark.effect_e2e.run.SCENARIOS`, judged by this harness's
independent ground truth (a read-only connection to the SQLite file that never
Expand Down
133 changes: 133 additions & 0 deletions packages/flow/benchmark/record_check_proof.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"""Repository fixture composition for the generic record-check proof runner.

Run ``python -m benchmark.record_check_proof scaffold-verifier ... --prove NAME``.
Only this qualification entry point resolves fixture names; core Flow requires
an explicitly injected source and never silently omits a requested proof.
"""

from __future__ import annotations

import sys
from pathlib import Path
from typing import Any, Callable, Mapping, Optional, Sequence

from openadapt_flow.record_check_proof import Fixture, ProofUnavailable
from openadapt_flow.runtime.effects.effect import Effect

# -- MockMed -----------------------------------------------------------------


def _mockmed_serve() -> tuple[str, Optional[Path], Callable[[], None], Any]:
from openadapt_flow.mockmed.fault_server import serve

url, db, stop = serve()
return url.rstrip("/"), None, stop, db


def _mockmed_judge(
before: list[dict[str, Any]], after: list[dict[str, Any]], params: Mapping[str, str]
) -> tuple[bool, str]:
patient = params.get("patient_id", "p1")
note = params.get("note", "")
before_ids = {r["id"] for r in before}
after_ids = {r["id"] for r in after}
if any(r["id"] not in after_ids for r in before):
return False, "collateral_loss"
new = [r for r in after if r["id"] not in before_ids]
mine = [r for r in new if str(r.get("patient_id")) == patient]
if not mine:
return False, "wrong_record" if new else "absent"
if len(mine) > 1:
return False, "duplicate"
if str(mine[0].get("note")) != note:
return False, "partial"
if len(new) > 1:
return False, "collateral_write"
return True, "correct"


MOCKMED = Fixture(
name="mockmed",
serve=_mockmed_serve,
reset=lambda db, seed: db.reset(seed_concurrent=seed),
snapshot=lambda db: [dict(r) for r in db.snapshot()["records"]],
judge=_mockmed_judge,
scenarios=(
("ok", False),
("no_persist", False),
("partial", False),
("wrong_record", False),
("stale", True),
("optimistic", False),
("session", False),
),
write_path="/api/encounter",
default_body={"patient_id": "{patient_id}", "type": "Triage", "note": "{note}"},
)


def fixture(name: str) -> Fixture:
if name == "mockmed":
return MOCKMED
if name in ("effect-e2e", "effect_e2e"):
try:
from benchmark.effect_e2e.prove_check import FIXTURE
except ImportError as exc: # pragma: no cover - wheel installs
raise ProofUnavailable(
"the effect-e2e fixture lives in the repository "
"(packages/flow/benchmark); run from a checkout"
) from exc
return FIXTURE
raise ProofUnavailable(f"unknown fault fixture {name!r} (mockmed, effect-e2e)")


def prove_live(
fixture_name: str,
section: Mapping[str, Any],
effects: list[Effect],
params: Mapping[str, str],
*,
write_requests: Optional[list[dict[str, Any]]] = None,
work_dir: Path,
) -> dict[str, Any]:
"""Compatibility composition for the existing repository proof cases."""
from openadapt_flow.record_check_proof import prove_live as run_proof

source = fixture(fixture_name)
return run_proof(
source.name,
section,
effects,
params,
write_requests=write_requests,
work_dir=work_dir,
proof_source=source,
)


def main(argv: Sequence[str] | None = None) -> int:
"""Bind the requested fixture, then invoke the unchanged normal CLI route."""
from openadapt_flow.__main__ import build_parser
from openadapt_flow.__main__ import main as flow_main

raw = list(sys.argv[1:] if argv is None else argv)
args = build_parser().parse_args(raw)
if getattr(args, "command", None) != "scaffold-verifier":
print(
"REFUSED: this fixture entry point requires scaffold-verifier",
file=sys.stderr,
)
return 2
requested = getattr(args, "prove", None)
source = fixture(requested) if requested is not None else None
if source is not None:
# The normal parser stores the last --prove value. Append the trusted
# source name before a literal delimiter without rewriting any existing
# option value or positional text that merely resembles an option.
delimiter = raw.index("--") if "--" in raw else len(raw)
raw[delimiter:delimiter] = ["--prove", source.name]
return flow_main(raw, record_check_proof_source=source)


if __name__ == "__main__":
raise SystemExit(main())
12 changes: 11 additions & 1 deletion packages/flow/docs/EFFECT_KIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,19 @@ it before a person reviews it.
openadapt-flow scaffold-verifier rec/ # network capture only
openadapt-flow scaffold-verifier rec/ --db app.sqlite # plus every table
openadapt-flow scaffold-verifier rec/ --verifier verify.yaml --bundle bundle/
openadapt-flow scaffold-verifier rec/ --db app.sqlite --prove effect-e2e
python -m benchmark.record_check_proof scaffold-verifier rec/ --db app.sqlite --prove effect-e2e
```

The repository's benchmark entry point supplies the named fault fixture.
The core proof runner receives an explicit caller-owned `Fixture`; neither it
nor the engine CLI discovers demo or benchmark sources. A direct core CLI
`--prove` request without an injected source refuses before proof execution or
output. It never falls back to an unproved check. Programmatic callers pass
`proof_source=...` to `prove_live` or `build_record_check`; CLI composition passes
`record_check_proof_source=...` to `main`. These are process-local dependencies,
not serialized callbacks or approval authority. The existing proof still uses
the real replayer, the declared verifier and the fixture's independent judge.

### Finding the read

The recording's parameter values (`meta.json`) identify the saved record.
Expand Down
6 changes: 3 additions & 3 deletions packages/flow/docs/GENERALITY_AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ motivating application.
| `mockmed/teach_scope.py` | Used only by tests | Moved to `packages/flow/tests/_teach_scope.py` | done |
| `learning/synth_stream.py:1-245` | MockMed note stream | `StructuralDiffInducer` moved to `openadapt_flow/learning/induce.py`; the stream moved to `packages/flow/benchmark/harness/synth_stream.py` | done |
| `reward/seed.py:1-236` | MockMed reward seed | Bundle helpers split out to `openadapt_flow/reward/bundle.py`. The seed stays: `serve-reward --seed-mockmed` needs it from a wheel | done |
| `record_check_proof.py:60-124` | `MOCKMED` fixture and `if name == "mockmed"` | Fixtures registered by name from the demo app's own module | planned |
| `record_check_proof.py` | Named fixture discovery coupled to the generic proof runner | Caller-owned `Fixture` injection; names, factories and judges are composed in `benchmark/record_check_proof.py` | implemented |
| `validation/dense_surface.py` | Study harness rendering a MockMed table | Move to the benchmark tree together with the identity studies that import it (`identity_ladder`, `identity_looks_dense`, `pixel_identity_probe`, `known_ids_dense`) | planned |
| `__main__.py:5246-5275` (`bench`) | Always replays against MockMed | Accept `--url`, or load from a checkout like `benchmark` | planned |
| `execute/registry.py`, `execute/service.py`, `execute/app.py`, `execute/dispatch.py:60-108`, `--seed-mockmed` | Demo seeding inside the Execute service | Generic `seed_admissions`; the CLI passes the demo seed | planned |
Expand Down Expand Up @@ -154,7 +154,7 @@ Paths are relative to `packages/flow/openadapt_flow/` as of the audited commit.
| `runtime/identity.py:275-302` | (d) | MockMed/EMR list | _GENERIC_IDENTITY_WORDS hand-tuned EMR list vocabulary (incl. 'demo','chart','review','results'); derive from recording or app profile |
| `runtime/replayer.py:392` | (d) | MockMed | PC_STRUCTURAL_TEMPLATE_THRESHOLD=0.8 (line 394) calibrated on MockMed only |
| `__main__.py:10030-10037` | (c) | MockMed | bench parser. |
| `__main__.py:10166` | (c) | MockMed | verify-scaffold --prove choices ('mockmed','effect-e2e'): fixture names hard-coded in CLI; see record_check_proof.py. |
| `__main__.py` proof CLI | (c) | Qualification fixtures | Generic `--prove FIXTURE` requires an injected process-local source; repository composition resolves fixture names outside the engine. |
| `__main__.py:11940-11989,12341-12415` | (c) | MockMed | serve-execute/serve-reward --seed-mockmed flags and banner paths (data_dir/mockmed/records.db). Opt-in demo seeding; keep as demo flag or rename --seed-demo. |
| `__main__.py:1787-1806` | (c) | MockMed | demo-record command: serves MockMed, runs demo_driver.record_triage_demo. User-facing CLI; demo data could be a recorded script in data. |
| `__main__.py:1808-1998` | (c) | MockMed | tutorial command (via tutorial.py). Deliberate product feature; must work from wheel. |
Expand Down Expand Up @@ -182,7 +182,7 @@ Paths are relative to `packages/flow/openadapt_flow/` as of the audited commit.
| `learning/synth_stream.py:246-525` | (c) | (generic) | StructuralDiffInducer is generic but lives in the MockMed harness module; imported by learning/teach.py:96, learning/branch_teach.py:76, benchmark/openemr_e2e/harness.py, 4 tests; pyproject mypy list (line 611). Move to learning/induce.py first, then move the stream out. |
| `mockloan/ (server.py, fault_server.py, static/)` | (c) | MockLoan | Synthetic app served by the shipped OpenEnv environment from the installed wheel. Keep server.py, fault_server.py and static/ in the package; the recorder and benchmark harness stay outside it. |
| `mockmed/ (server.py, fault_server.py, chart_pages.py, teach_scope.py, static/)` | (c) | MockMed | Own synthetic demo app shipped in wheel. Required by tutorial/demo/demo-record/replay-default/sandbox and root openadapt/quickstart (engine.py, workers.py) + root tests/test_partner_kit.py. Keep shipping (or split to a demo-apps package). mockmed/teach_scope.py (EncounterWrites) is used only by tests -> move to tests/fixtures. chart_pages used by guard_demo + benchmark (agent_guard, effect_e2e, ui_readback). |
| `record_check_proof.py:1-11,60-124` | (c) | MockMed | --prove fixture registry hard-codes MOCKMED Fixture (judge on patient_id/note, write_path /api/encounter, body type Triage) and `if name == 'mockmed'`. Opt-in proof against our fixture. Move MOCKMED Fixture to mockmed/proof_fixture.py and resolve fixtures by entry point / dotted path. Importers: scaffold_verifier.py:594, benchmark/effect_e2e, tests (2). |
| `record_check_proof.py` | (c) | Qualification fixtures | Named factories, application judge and defaults moved to `benchmark/record_check_proof.py`; the generic runner requires explicit matching `Fixture` injection. |
| `repair/dialog_fixture.py:1-` | (c) | synthetic | rendered test app in product package (no real app) |
| `reward/seed.py:1-236` | (c) | MockMed | seed_mockmed, MockMed encounters table/SQL (MOCKMED_TABLE 'encounters', SELECT id, patient_id, type, status), patients, episodes. Move to reward/demo_seed or tests/fixtures; CLI serve-reward --seed-mockmed uses it. |
| `reward/seed.py:238-410` | (c) | (generic) | write_bundle/calibrate_bundle/self_signed_certificate are generic helpers living in the MockMed seed module; split into reward/bundle.py. Importers: integrations/harness2/scorer.py, benchmark/undo_proposal/run.py, tests/test_probe_induction.py, test_undo_proposal.py, test_reward_worker.py, test_reward_trust_boundary.py. |
Expand Down
44 changes: 44 additions & 0 deletions packages/flow/import_layers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"about": "Import layers for openadapt_flow, checked by tests/test_import_layers.py. Core modules are the runtime and the verifier. They must not import app-specific, benchmark, desktop-UI, or lab and harness code, directly or through any chain of openadapt_flow imports, including lazy imports inside functions. Fix a violating import rather than loosening this file. An edge that cannot be removed yet goes in exceptions with its reason and an issue link.",
"core": [
"openadapt_flow.backend",
"openadapt_flow.ensure_record",
"openadapt_flow.fault_gate",
"openadapt_flow.ir",
"openadapt_flow.provenance",
"openadapt_flow.readback_ladder",
"openadapt_flow.receipt",
"openadapt_flow.record_sor",
"openadapt_flow.runtime",
"openadapt_flow.verification",
"openadapt_flow.verifier",
"openadapt_flow.verify",
"openadapt_flow.verify_run",
"openadapt_flow.write_receipt"
],
"forbidden": {
"app-specific": [
"openadapt_flow.demo_driver",
"openadapt_flow.demo_proof",
"openadapt_flow.mockloan",
"openadapt_flow.mockmed",
"openadapt_flow.tutorial"
],
"benchmark": [
"benchmark",
"openadapt_flow.bench"
],
"desktop-UI": [
"openadapt_flow.adapters",
"openadapt_flow.console.app",
"openadapt_flow.console.server",
"openadapt_flow.desktop_record",
"openadapt_flow.interactive_recorder"
],
"lab and harness": [
"openadapt_flow.rl_env",
"openadapt_flow.teach_authorization_fixture"
]
},
"exceptions": []
}
Loading
Loading