Repository navigation
Verifier: no app's endpoints, layout or record ids as engine defaults - #1774
Conversation
Audit every place the engine (packages/flow/openadapt_flow) names a specific application, bucketed as example, data, harness in the package, or real special-casing: docs/GENERALITY_AUDIT.md. Move openadapt_flow/benchmark and openadapt_flow/instrument to packages/flow/benchmark/harness (import benchmark.harness). The wheel packages only openadapt_flow, so it no longer ships them. The benchmark command loads the harness from a source checkout and says so from a wheel; instrument already failed to import from a wheel. Reword two comments that named an app on a generic branch.
scripts/check_engine_generality.py scans openadapt_flow/ for the application names and app-specific identifiers in scripts/generality.toml. app_profiles/ is data and passes; OpenAdapt's demo modules may name the demo app; a comment line may give an example under '# example:'. Hits that predate the check are counted in generality_baseline.json, which only shrinks: a rise fails, and so does a fall the baseline doesn't record. Runs in the flow lint job. CONTRIBUTING documents the rule: app knowledge lives in profiles, contracts and fixtures.
RestRecordVerifier, RestCompensator, fault_gate's memory copy,
effects.kind rest and rest reward recipes defaulted to MockMed's
/api/db, records and /api/encounter/{id}. The paths are now required;
a rest config without records_path is refused when its verifier is
built.
Compile-time REST binding admission guessed MockMed's write path
/api/encounter and skipped its server-assigned 'source' field. The
AdmissionFixture now names the app's read path, write path,
server-assigned fields and idempotency key field; with no captured
api_write and no write path, a step gets no proposal.
compile_recording takes admission_fixture instead of
admission_fixture_url.
Effect mining and scaffold-verifier mined an idempotency key from any
record field named 'key' (MockMed's). They mine one only from the field
the caller names.
… data Three engine defaults encoded one app: - FhirEffectVerifier, effects.kind fhir and fhir reward recipes defaulted to OpenEMR's note-as-Observation layout (resource Observation, note in valueString). They now require resource_type and field_paths; a config without them is refused when the verifier is built. - The frame and rollback attribution read an audit entry for record "0" as naming no record, which is how OpenEMR files bookkeeping inserts. An app profile now lists such values (no_record_values); the OpenEMR profile says "0" (version 2026-11-03.1). An empty field still names no record in any app. - ensure-contract generation translated the Medplum probe's own output keys. It reads only the generic fact names now; the translation moved beside the probe, and the committed teach fixtures hold generic facts.
Audit every place the engine (packages/flow/openadapt_flow) names a specific application, bucketed as example, data, harness in the package, or real special-casing: docs/GENERALITY_AUDIT.md. Move openadapt_flow/benchmark and openadapt_flow/instrument to packages/flow/benchmark/harness (import benchmark.harness). The wheel packages only openadapt_flow, so it no longer ships them. The benchmark command loads the harness from a source checkout and says so from a wheel; instrument already failed to import from a wheel. Reword two comments that named an app on a generic branch.
A comment that landed on main named the lab's Patient Notes app beside a tolerance; the guard caught it. The example marker also accepts '#:' doc-comments.
…-path Adversarial review found that dropping the records_path and resource_type defaults changed the verifier identity of every existing config, and the fault-gate check digest of an explicit resource_type: Observation. Both now hash the old values (LEGACY_HASHED_DEFAULTS), for hashing only; a read without its own path or resource is still refused. run --effects-kind rest takes --effects-records-path, and the CLI tests that relied on the old default pass it. Test configs that read the demo app name its path.
The wheel ships only the engine (see packages/flow/docs/GENERALITY_AUDIT.md). Moved out of openadapt_flow: - integrations/harness2 -> benchmark/harness/harness2 (the harness2 extra is dropped; install [browser] beside Harness² from a checkout) - mockloan/ and demo_driver.record_disbursement_demo -> benchmark/harness/mockloan/ (recorder in demo.py) - guard_demo.py -> benchmark/harness/guard_demo.py - agent/ledger_demo.py -> packages/agent/tests/ledger_demo.py - mockmed/teach_scope.py -> packages/flow/tests/_teach_scope.py - learning/synth_stream.py: StructuralDiffInducer -> learning/induce.py; the MockMed drift stream -> benchmark/harness/synth_stream.py reward/seed.py is split: the bundle helpers (write_bundle, calibrate_bundle, self_signed_certificate) are now reward/bundle.py. The MockMed seed stays, because serve-reward --seed-mockmed needs it from a wheel.
…move-harnesses # Conflicts: # packages/flow/CHANGELOG.md # packages/flow/docs/GENERALITY_AUDIT.md # packages/flow/tests/test_lending_swer.py
Second review: Contract.digest, store and identity hashed the verifier dump directly, so every sql and fhir contract changed its hash (write ledger keys, reward certificates). They go through the same legacy shim now; a test pins main's values for sql, rest and fhir contracts. compile_recording and scaffold-verifier take idempotency_key_field, so a caller can name the app's at-most-once key field. Test verifier subclasses name the demo app's records path.
|
Independent adversarial reviews (two separate AI reviewers, each given only the diff) Review 1: FHIR layout,
|
…new tests their record paths
|
89fb0a6 fixes the red CI.
Not done here: letting each app's own data (verify.yaml or the app profile) name its key field. That will be a separate issue. |
…t-shape # Conflicts: # packages/flow/CHANGELOG.md
…e-harnesses # Conflicts: # packages/flow/CHANGELOG.md # packages/flow/benchmark/harness/run_benchmark.py # packages/flow/docs/GENERALITY_AUDIT.md # packages/flow/openadapt_flow/__main__.py # packages/flow/pyproject.toml # packages/flow/tests/test_lending_swer.py # packages/flow/tests/test_runtime_never_imports_benchmark.py
…ity-guard # Conflicts: # .github/workflows/packages.yml # packages/flow/CHANGELOG.md # packages/flow/benchmark/harness/run_benchmark.py # packages/flow/docs/GENERALITY_AUDIT.md # packages/flow/openadapt_flow/__main__.py # packages/flow/pyproject.toml # packages/flow/tests/test_runtime_never_imports_benchmark.py
…ging and import-guard lines
…pend lost in the main merge
Signed-off-by: abrichr <richard.abrich@mldsai.com>
…into agent/97 Signed-off-by: abrichr <richard.abrich@mldsai.com> # Conflicts: # packages/flow/CHANGELOG.md # packages/flow/docs/GENERALITY_AUDIT.md
Signed-off-by: abrichr <richard.abrich@mldsai.com>
Signed-off-by: abrichr <richard.abrich@mldsai.com>
Signed-off-by: abrichr <richard.abrich@mldsai.com> # Conflicts: # packages/flow/tests/test_app_profile.py
Signed-off-by: abrichr <richard.abrich@mldsai.com>
Signed-off-by: abrichr <richard.abrich@mldsai.com>
Signed-off-by: abrichr <richard.abrich@mldsai.com> # Conflicts: # packages/flow/scripts/generality_baseline.json
|
Updated to 7c9ab66 after #1749. Main is a parent and the PR is conflict-free. The app-profile conflict keeps all three safety tests. Six fixture files now name their required REST paths/keys or FHIR resource/mapping; all 229 assertions remain unchanged. The guard ratchet falls 358→333; checker, denylist and guard tests are unchanged. Validation: 388 core +129 fixture Flow tests and 10 guard tests pass; the final integration adds 69 passing checks (including the guard again). Nine Desktop tests pass. Flow/Agent/Desktop lint and syntax gates, Flow formatting and mypy pass. Native Windows tests await CI. The optional Agent approval suite has three failures reproduced on parent main (3d6b0b1), documented in the body; no approval checks were bypassed. One batched plain push, no force-push. Please independently review this head; it remains for a hand merge after approval. |
Independent adversarial review: APPROVEFrom: Grok. Reviewed head 7c9ab66 against main, per #99. I did not push to this branch. What I checked, with what I found:
Tests: I ran the 50 flow test files this PR touches, plus Non-blocking findings for the author:
|
Part of the engine generality work (audit: #1741, findings D3 to D9). These engine defaults encoded one app's layout. They now come from data, or the read is refused.
Over 1,000 changed Python lines (1,208 including tests; 396 in
openadapt_flow/). Per the merge rule, this stops at approval for a hand merge.Change
RestRecordVerifier,RestCompensator, the fault gate's in-memory copy,effects.kind: restand REST reward recipes defaulted to MockMed's/api/db,recordsand/api/encounter/{id}. These are now required. Arestconfig withoutrecords_pathis refused when its verifier is built, andrun --effects-kind resttakes--effects-records-path.AdmissionFixturenames the app's read path, write path, server-assigned fields and idempotency key field. With no capturedapi_writeand no write path, a step gets no proposal; nothing is guessed.compile_recordingtakesadmission_fixture. The fixture's shape is hashed into the compiler config only when it is set.scaffold-verifiermined a key from any field namedkey(MockMed's). They now mine one only from a field the caller names (idempotency_key_field).Effect.key_fieldkeeps its old default so existing bundles hash the same; it only matters when a contract givesidempotency_key.FhirEffectVerifier,effects.kind: fhirand FHIR reward recipes requireresource_typeandfield_paths. They no longer default to a note stored as an Observation.generate ensure-contractreads only the generic fact names. The Medplum translation moved beside its probe, and the committed teach fixtures were converted (the generated contracts are identical).no_record_values. OpenEMR's profile says"0", and the profile is bumped toopenemr-frame-profile/2026-11-03.1, so a config pinned to the old profile digest is refused until it pins the new one. An empty field still names no record in any app.Existing users keep their hashes
deployment.LEGACY_HASHED_DEFAULTShashes an unsetrecords_pathorresource_typeas its old value. It is used for hashing only; a read without the field is still refused. The shim is applied to:Contract.digest,storeandidentity.tests/test_verifier_api.py::test_contract_hashes_do_not_changepins the values that unmodifiedmaincomputes for a sql, a rest and a fhir contract.test_existing_verifier_identities_do_not_changestill passes.Shown on the motivating app and others
/api/db(existing tests, now naming the path) and Frappe's/api/resource/Loan Applicationwith keydata(new config and admission tests).0; an app without that profile value treats0as a real record (new frame and rollback tests).keyand another app'sclient_request_id.Adversarial reviews (two independent reviewers)
resource_typeunset changed every config's verifier identity. Fixed by the hash shim. Should-fix items: the check-digest change, a stale pinned profile in a frozen benchmark contract (noted in the CHANGELOG), and the CLI help.Tests and current integration
Updated head: 7c9ab66, including main 42a82da after #1749. The PR is conflict-free. The app-profile test conflict retains all three tests from both sides; the baseline conflict retains 333 hits, below main's 358. The checker, denylist and guard-test bytes match main. Follow-up fixes supply required REST paths/keys and FHIR resources/mappings in six synthetic fixture files; all 229 existing assertions are unchanged.
Native Windows filesystem/junction tests were not run locally; CI runs them. A portable probe confirms the Windows fixture's actual CLI arguments build the REST verifier and that an omitted read path still refuses. Browser tests ran headless against synthetic loopback apps.
The optional Agent approval E2E suite was also enabled: its 3 failures reproduce on the byte-identical parent-main tree (3d6b0b1), including an unpinned approver root. This limitation is not bypassed or weakened; the suite skips by default. Both head and baseline were tested with the same temporary environment and synthetic loopback app.
The existing branch was fast-forwarded in one batched push, without force. CI and independent review are pending. This PR remains for a hand merge after approval.