Skip to content

test(console): pin what the engine actually emits for every halt category - #294

Closed
abrichr wants to merge 3 commits into
mainfrom
test/human-decision-golden-tasks
Closed

test(console): pin what the engine actually emits for every halt category#294
abrichr wants to merge 3 commits into
mainfrom
test/human-decision-golden-tasks

Conversation

@abrichr

@abrichr abrichr commented Jul 28, 2026

Copy link
Copy Markdown
Member

The gap

A demo or sample fixture stays schema-valid and digest-consistent forever, because both properties are computed from the fixture. Nothing anywhere asserted that a fixture is still a shape this engine can emit.

That is not theoretical. The mapping tables in console/human_decisions.py and the accept-list in console/attention.py both moved this cycle. Every downstream test would have stayed green while a fixture described last month's product — which is exactly what would have happened silently between the merge of #290 and today.

What this adds

tests/golden/human_decision_tasks.json, regenerated from real engine output: one durable pause per halt category controller.classify_halt() can produce, each projected through portable_remote_decision_task() — the Cloud-bound projection, with presentation already discarded, so nothing in the artifact describes evidence the dashboard is not entitled to.

The artifact records:

field why a consumer needs it
category_question_map the (task_kind, question.template) a fixture may claim
delivery_uncertain_override the kind/template an unresolved delivery substitutes for any category
action_vocabulary the only buttons a fixture may advertise
safe_slot_fields / evidence_fields the projected key sets
never_populated_safe_slots = ["candidate_count"] any surface that says "found N matches" is asserting a number the engine has never sent
never_emitted_task_kinds = ["operator_review"] no Flow path emits it
halts[] per category: what attention._normal_category made of it, the delivery state, the resulting kind/template, non-null slots, allowed actions

Four tests: the golden comparison; the action vocabulary is exactly the portable enum; teach and escalate are offered at every pause (proven from real capabilities, not from reading _allowed_actions); and an anchor that a replayer-driven halt — Replayer(durable=True).run(...), nobody's hand-built pause — lands on the same golden row.

Proof it bites

The comparison walks both documents together, so a failure names the exact field rather than dumping two documents at each other. Two mutations, both restored afterwards:

  1. _TASK_COPY["resolution"] -> ("halt", "review_halt", ...):
    golden drift at category_question_map.resolution.question_template: the engine now emits 'review_halt', the artifact still says 'resolve_ambiguity'
  2. dropped "resolution" from attention._KNOWN_CATEGORIES:
    golden drift at halts.resolution.projected_category: the engine now emits 'operator_review', the artifact still says 'resolution'

Regenerate deliberately with OPENADAPT_UPDATE_GOLDEN=1 pytest tests/test_human_decision_golden.py; the run is ~0.9s and adds no CI leg.

Boundary

The artifact is closed-vocabulary metadata — enum names, key names, and counts. No values, no run content, no PHI, no tuning. Mechanism, not data, so it is on the public side of AGENTS.md 4.2.

Consumers

openadapt-cloud vendors this file and checks its /demo/attention and ?sample=eligibility fixtures against it (companion PR). Nothing in Flow's behaviour changes here — this PR is test-only.

Noted, not fixed

Two findings this work surfaced; both are in the shared attended projection another lane is editing, so neither is touched here:

  • delivery_uncertain is missing from attention._KNOWN_CATEGORIES. The golden artifact records it: halts.delivery_uncertain.projected_category is operator_review, not delivery_uncertain. The task kind still recovers at the next layer, so the operator gets the right question, but the queue headline is the generic "This run needs local operator review" for the one halt where the operator must reconcile rather than retry. Fix is two edits — add the category to _KNOWN_CATEGORIES and give it a _COPY entry — and the golden file will show the change.
  • tests/test_halt_detail.py::test_resolution_halt_says_what_broke_and_what_continue_will_recheck fails on origin/main in a freshly uv sync --all-extras'd tree (halt["target_label"] is None, expected "Open"), independent of this branch. Flagging for whoever owns _safe_target_label.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM

@abrichr

abrichr commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

Note for the reject lane (flow feat/attended-reject-action, cloud feat/attended-reject-action)

This is a deliberate interaction, not a conflict. When reject lands it will touch HumanDecisionAction in openadapt-types and _ACTION_MAP in console/human_decisions.py, and two assertions here will go red:

  • test_the_action_vocabulary_is_exactly_the_portable_enum_ACTION_MAP must stay a bijection with the enum, so adding the action to only one side fails.
  • the golden comparison — action_vocabulary in tests/golden/human_decision_tasks.json will no longer match.

The fix is one command plus a read of the diff:

OPENADAPT_UPDATE_GOLDEN=1 pytest tests/test_human_decision_golden.py

and, if the action becomes offerable at a pause, the per-halt allowed_actions rows change too. That is the point — a demo fixture in openadapt-cloud may advertise a reject button only after the vocabulary ships and the artifact is refreshed, which is exactly the gate the design asked for. Nothing here blocks that lane; it just makes forgetting a consumer loud.

abrichr and others added 2 commits July 27, 2026 23:24
…gory

A demo or sample fixture stays schema-valid and digest-consistent forever,
because both properties are computed from the fixture itself. Nothing anywhere
asserted that a fixture is still a shape this engine can emit, so the mapping
tables in `console.human_decisions` and the accept-list in `console.attention`
could move -- both did this cycle -- and every downstream test would stay green
while the fixture described last month's product.

This commits `tests/golden/human_decision_tasks.json`, regenerated from real
engine output: one durable pause per halt category the controller can classify,
each projected through `portable_remote_decision_task` (the Cloud-bound
projection, with `presentation` already discarded). The artifact records the
category -> (task_kind, question.template) map, the uncertain-delivery
override, the action vocabulary, the safe-slot and evidence key sets, the slots
the engine never populates, and which task kinds and templates are emitted at
all.

The test walks both documents together, so a failure names the exact field:

    golden drift at category_question_map.resolution.question_template: the
    engine now emits 'review_halt', the artifact still says 'resolve_ambiguity'

Proven to bite: rewriting `_TASK_COPY["resolution"]` and dropping "resolution"
from `_KNOWN_CATEGORIES` each fail with the field named, and both were restored.

Two facts the artifact now makes checkable by a consumer:

* `never_populated_safe_slots` is `["candidate_count"]`. Any surface that says
  "found N matches" is asserting a number the engine has never sent.
* `never_emitted_task_kinds` is `["operator_review"]`. No Flow path emits it.

Regenerate deliberately with `OPENADAPT_UPDATE_GOLDEN=1` and read the diff:
every line of it is a change to what an operator is told.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM
`public-artifacts.json` is the reviewed positive inventory every `.json` in the
public source tree must appear in, with its exact SHA-256, before the wheel and
sdist boundary check will pass. Regenerated with
`--write-public-artifact-inventory`; the diff is exactly one line, the golden
artifact this branch adds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM
@abrichr
abrichr force-pushed the test/human-decision-golden-tasks branch from 1b4b70f to a695e09 Compare July 28, 2026 03:25
PR #295 made `reject` an always-available attended action, so the engine
now emits it in `action_vocabulary` and in `allowed_actions` for every
halt category. The committed artifact predates that change and still
lists four actions.

Regenerated with OPENADAPT_UPDATE_GOLDEN=1. The vocabulary assertion
compares `_ACTION_MAP` against `openadapt_types.HumanDecisionAction`
rather than a hand-copied list, which is what caught the drift.
@abrichr
abrichr force-pushed the test/human-decision-golden-tasks branch from a695e09 to 0a22ccb Compare July 28, 2026 03:30
@abrichr

abrichr commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

Closing this stale contract PR without deleting its branch. Current main includes PR #296, where RemoteDecisionProjection adds the outer delivery_tier and halt_context fields. This PR's generator records only projection.task while describing the artifact as the exact Cloud projection, so its current golden cannot validate the contract that Flow now emits. It also conflicts in public-artifacts.json. Preserve the branch for reference. A later focused PR can regenerate the contract from current main and either cover the complete outer projection or narrow the claim to the signed task only.

@abrichr abrichr closed this Jul 28, 2026
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