Skip to content

Acknowledge reader actions through Target Buttons - #180

Merged
max-sixty merged 7 commits into
mainfrom
codex/mo-ack-receipts
Sep 1, 2026
Merged

Acknowledge reader actions through Target Buttons#180
max-sixty merged 7 commits into
mainfrom
codex/mo-ack-receipts

Conversation

@max-sixty

@max-sixty max-sixty commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Leaf currently shows local work as inline dotted work lines, which makes reader actions look like additional page content. This change projects interaction-scoped acknowledgments from the event log and reuses each target's existing right-edge Button for page actions and widget claims. The Button advances through Sent, Picked up, and Active without changing its semantic outcome coordinate; full thread panels and frozen conversation widgets keep a compact local receipt where no page edge exists.

The change also moves pickup and active-state projection into shared Python and browser contracts, retains receipt nodes across phase changes, filters version-bounded claims, and updates fixtures, documentation, render checks, and browser coverage. Receipt-backed claims now survive later revisions through their live page target even without x-work; recordless page moves settle on the next later version note, preserving moves made from superseded live revisions; and pickup records leave derived sequence numbers to the event parser. The presentation adapter is intentionally narrow and carries a reconciliation TODO because the Target Button implementation is changing in parallel.

Tests: uv run pytest tests (761 passed); pre-commit run --all-files; visual sweep at 1200×900 and 1440×900 in light and dark.

This was written by Codex on behalf of @max-sixty

@leaf-agent leaf-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two of these I confirmed by running the repo's own code against a DECISION_PAGE fixture; transcripts are folded below.

status --on now accepts a claim that then wedges version stamp and page init. work_subject gained the has_receipt escape so a widget with an unsettled action can take a claim without an x-work seat, but widget_work_without_seats — which backs the hard exits in publishing.py and vendoring.py's _refuse_unseated_work — still requires widget_work_seat(...) to be truthy. lf-options declares no x-work, so the ordinary loop the PR's own docs describe (status … --on <widget>, then stamp the version that answers it) dead-ends. Related: skills/leaf/CLAUDE.md's $keys table still describes x-work as "the content or conversation seat in which local agent work may appear", which is no longer what the value governs for a page widget — the seat is now the Target Button either way.

A verb whose x-state spec has no record never settles its acknowledgment. page_action_unsettled returns authored is NO_RECORD or authored != folded, and NO_RECORD means the spec declares no authored representation at all — so the predicate is permanently true. lf-options' answer (facet completion, no record) is exactly that verb, and it is what the reader's Done press sends on every multi-select ask, including the one examples/ship-review.jsonl stages. session-lifetime.md in this PR says "A reply, resolution, or authored state that honors the move settles the acknowledgment"; for completion no authored state can, so the target keeps a "Waiting for pickup" reading in its secondary cluster indefinitely. lf-suggestion's accept/reject (facet settlement) have the same shape, though they reach the page through the frozen-conversation path instead.

The third is a one-line inline suggestion on record_pickup.

Transcripts

Both scripts run against tests/render_cases_interaction.DECISION_PAGE on the merged tree.

The stamp/vendor wedge:

status --on jobs -> 0
plain stamp -> 1 refusing to stamp index.html: it would remove the local seat for active work on 'jobs'; pass --completes for each widget this version completes
re-init/vendor -> 1 the incoming layer would remove the local seat for active widget work on 'jobs'; stamp a later version with --completes for that work before re-vendoring

The stamp there is an unrelated later version that removes nothing, so the message is also inaccurate. test_a_widget_move_reuses_one_target_button_until_the_page_honors_it passes completes=("jobs",) on its stamp, which is why the suite stays green over this.

The unsettleable completion coordinate — one choose and one answer, then a stamp whose markup carries chosen:

--- after choose + answer (Done)
    {'target': {'kind': 'widget', 'id': 'jobs'}, 'coordinate': ['jobs', 'jobs', 'selection'], 'phase': 'sent'}
    {'target': {'kind': 'widget', 'id': 'jobs'}, 'coordinate': ['jobs', 'jobs', 'completion'], 'phase': 'sent'}
--- after stamping the honored markup
    {'target': {'kind': 'widget', 'id': 'jobs'}, 'coordinate': ['jobs', 'jobs', 'completion'], 'phase': 'sent'}

selection settles as designed; completion survives the version that answered it, and --completes is not an escape here because it demands an active work claim.

Comment thread skills/leaf/scripts/leaf/work.py
Comment thread skills/leaf/scripts/leaf/acknowledgments.py Outdated
Comment thread skills/leaf/scripts/leaf/session.py

@leaf-agent leaf-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The seq and admission-predicate fixes both check out, and I confirmed the recordless-settlement regression passes once 17df5f1 lands the positional count. One finding remains on the new settlement rule, inline.

Comment thread skills/leaf/scripts/leaf/acknowledgments.py Outdated
@max-sixty
max-sixty merged commit 7a9d875 into main Sep 1, 2026
6 checks passed
@max-sixty
max-sixty deleted the codex/mo-ack-receipts branch September 1, 2026 02:52
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.

2 participants