Skip to content

flow: induce search-open-act-return and row-varying loops, each row checked on its own record - #1435

Open
abrichr wants to merge 7 commits into
mainfrom
flow/target-param-loops
Open

abrichr wants to merge 7 commits into
mainfrom
flow/target-param-loops

Conversation

@abrichr

@abrichr abrichr commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Loop induction learns worklists that aren't back-to-back repeats, and each pass works on its own row's record.

Why

An external review found that loop induction only recognized back-to-back repeats. A body that wrote was always refused. The runtime also had no way to point a click at a different record: entity_ref was a declared slot with no behavior behind it. So a loop that searches a record, opens it, works it and goes back to the list couldn't open the second record.

What changes

Runtime: Step.target_param

A click that selects a record (a search result or a worklist row) names the parameter whose value identifies the row.

  • Absent when unset. The field is left out of serialization when None, so existing bundle digests and contract hashes don't change.
  • Finding the row. When the run's value differs from the demonstrated one, the click looks for the run's value. It drops the demonstrated crop. It keeps a structural locator only when its name was exactly the demonstrated value.
  • Halts before input when:
    • the value is empty;
    • the anchor has no identity evidence;
    • the evidence doesn't embed the parameter;
    • the evidence embeds another parameter the run doesn't supply.
  • Identity must be verified. An unreadable or abstained identity halts, even on a reversible click. The hover shortcut is skipped.
  • No healing. A rebound click never heals its anchor, so it can't adopt another record's row.
  • Integrity check. The governed sealed-leaf check compares the program's own step.

Compiler: loop induction (compiler/loop_shapes.py, compiler/induction.py)

  • Search, open, act, return. The last row may skip the return. The return then runs at the start of each row, only when the list's first control isn't on screen. Only navigation can be part of the return; a write is never moved.
  • Rows that differ.
    • A typed field some rows lack runs only when the row has a value.
    • A reversible step runs only when its target shows.
    • A write some rows get is refused until a person accepts a proposed screen-text check (accepted_loop_guards).
  • Rows with several columns, for example lines in a form.
  • Record selection. A click whose label holds a typed row value selects that row's record. Identity text that changes by row and isn't explained by a typed value (a date of birth, say) becomes an identity column the worklist must supply.
  • When a writing body is emitted. Every write needs a record check bound to the row's values. In addition, either an identity-checked selection of the row comes before the write, or the check counts a new record. Otherwise induction refuses with a specific reason.
  • LoopCertificate. Every emitted loop states its bound (finite worklist, max_iterations), the selections and what they verify, and the writes and the row values they're checked against.
  • Hashing after induction. Recordings compile with readable identity bands (compile(..., phi_free_identity=False)). Induction hashes the bands before returning, with the row columns indexed.

Evidence

  • tests/test_target_param.py (18): binding rules, refusals, serialization stability, and replay through the interpreter. Covers the right row verified, a wrong row halted, an unreadable row halted, and no healing.
  • tests/test_loop_shapes.py (17): induced programs run held-out rows through the real interpreter against an in-memory chart app, and the test reads the app's records afterwards.
    • Each held-out row writes only its own record.
    • A row with a wrong date of birth stops before any write.
    • A row missing its identity column stops.
    • It works with readable and hashed identity bands, and from one demonstration of several rows.
    • Refusals are covered: a write without a record check, a check pinned to the demonstrated row, a click no row value names, a band without the value, and an unaccepted per-row write.
  • The existing induction, replayer, identity, program, qualification, governed and durable test files pass locally.

Not covered yet

  • Pagination (a Next-page step between rows). Next PR.
  • Lists nested inside each row.
  • Qualifying rebound clicks. Qualification's fault driver needs the recorded crop, so it can't qualify a rebound click yet. Until it can, a bundle with a target_param step is refused with a plain reason by certification under any identity-requiring policy (clinical-write), by the run gate's identity gate for Standard, Regulated and governed runs (whatever --policy), and by qualify readiness. Demo replay still runs it. This was the coordinator's review condition.
  • Short values. Values under 4 characters can't select a row; that is the existing identity threshold.
  • Live-app evidence. The behavioral test on a live OpenEMR copy waits for a fixture slot.

Safety review requested from the coordinator: this changes which record a click acts on, and when induction emits a loop that writes.

🤖 Generated with Claude Code

abrichr and others added 4 commits October 20, 2026 12:40
Adds Step.target_param (dropped from serialization when None, so existing
bundle digests and contract hashes are unchanged) and runtime.target_param:
a click that selects a record looks for the run's value instead of the
demonstrated label, drops the demonstrated crop, and halts before acting when
the value is empty or the identity evidence does not embed the parameter.
Loop induction (search-process-return, row-varying bodies) is not written yet.
Untested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l from it

A target_param click now halts on an unreadable or abstained identity even
when the step is reversible, skips the hover shortcut, and never heals its
anchor from another record's row. Adds loop_shapes helpers (not wired yet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r row

Loop induction now finds worklists whose rows are not back-to-back repeats:
search a record, open it, work it, return to the list (the last row may skip
the return, which then runs only when the list is not on screen), and rows
that differ (a field some rows lack, a write some rows get behind a screen
check a person accepts). A click whose label holds a typed row value selects
that row's record (Step.target_param); identity text that changes by row
becomes a required identity column.

A body that writes is emitted only when every write has a record check bound
to the row's values and either an identity-checked selection precedes it or
the check counts a new record. Every loop carries a LoopCertificate.

The sealed-leaf integrity check compares the program's own step for a
rebound click. Recordings compile with identity bands readable and induction
hashes them once the row columns are known.

Tests run held-out rows through the interpreter against an in-memory app and
check its records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@abrichr abrichr changed the title WIP: record-selecting clicks follow the run's value (Step.target_param) flow: induce search-open-act-return and row-varying loops, each row checked on its own record Oct 20, 2026
Qualification's fault driver cannot qualify a target_param click yet (it
needs the recorded crop of the row). Until it can, certification under any
identity-requiring policy (clinical-write), the run gate's identity gate
(Standard, Regulated, governed, whatever --policy) and qualify readiness
refuse such a bundle with a plain reason. Demo replay still runs it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@abrichr
abrichr marked this pull request as ready for review October 20, 2026 18:29
@abrichr

abrichr commented Oct 20, 2026

Copy link
Copy Markdown
Member Author

Coordinator safety review: approve at d71cd96.

  • A target_param click halts before input without VERIFIED identity, and never heals from another row.
  • A writing loop body is emitted only with a record check bound to the row's values.
  • Until qualification can drive these clicks, they are refused for certification under identity-requiring policies, by the run gate (Standard, Regulated, governed) and by qualify readiness.
    Merge when green.

🤖 Generated with Claude Code

@abrichr
abrichr enabled auto-merge October 20, 2026 18:30
# Conflicts:
#	packages/flow/tests/test_qualify_declarative.py

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant