A wait names its actions, and a proposal is taken as what it names #158

Merged
agra merged 2 commits from task/3728-a-wait-names-its-actions-and-a-proposal into master 2026-10-26 10:13:51 +02:00
Owner

Tracker: #3728

Step 8 of plan #3694. It fixes the store and API half of #3688. An action a Proposer proposes is taken as that action, not fed to the step as an answer, and the Orchestrator can propose a stop without naming a node that does not exist.

What it delivers

  • A waiting flow run's needs_you carries actions, the routes its wait takes: answer while it waits about a step, more_runs at run_limit, then propose and stop.
  • The Proposer's instructions list the wait's actions. Its report names one: {"action":"answer","answer":…,"proposal":…}, {"action":"stop","proposal":…}, or, at run_limit, {"action":"more_runs","runs":…,"proposal":…}. A report naming an action the wait does not take, or lacking what its action takes, records no proposal.
  • needs_you.proposal reads {run_id, action, text, answer, runs}. The history's proposal event carries action, proposal, answer and runs. Migration 49 adds the flow run's proposal_action, proposal_answer and proposal_runs, and upgrades each stored proposal, on a flow run and on its history, to action answer with its text as the answer.
  • POST /api/flow-runs/:id/take {"run_id":…} takes the waiting proposal of that Proposer run as what it names, in one transaction: answer as /answer with its answer, stop as /stop, more_runs as /more-runs with its runs. It answers what that route answers. A flow run whose proposal is another run's, or that has none, answers 409 {"error":"proposal_changed"}.
  • The Orchestrator may report {"stop":"<why>"}. sak records the decision (with stop, next null) and waits on Agra with cause stop_proposed, the why as its question, its actions answer, propose and stop. sak does not end the flow run itself. A next naming no node of the flow still waits unknown_next.
  • answer stays a step's input: no route reads an action out of an answer's text.
  • docs/api.md Flow runs describes all of it. Every schema-head assert moves to 49, and the roll-backs to 40 (tests/tools.sx) and 44 (tests/utf8_text.sx) drop the three new columns.
  • web/e2e/work-the-flows.spec.ts: the stub Proposer's report in "Ask an agent to propose…" names action answer, with the same text. The needs-you-proposal golden is unchanged.

The Needs you card is step 9 (#3729). Until then it shows the proposal's text and fills it in through "Answer with it", as before.

Regressions

Each was written before the fix, and run at the step's base 1144f367 (test files only, from a git archive arm) and at the head.

tests/runner_core.sx (check_needs_you, and the new check_proposals). At the base: RUNNER CORE FAIL (15 checks), 467 ok:

FAIL a wait on a step's question takes answer, propose and stop
FAIL a wait at the run limit takes answer, more runs, propose and stop
FAIL the Proposer's instructions list the actions its wait takes
FAIL a proposed answer reads its action and its answer
FAIL taking a proposed answer re-runs Implement, asked the question and the proposal's answer
FAIL a Proposer at the run limit proposes more runs
FAIL taking it raises the step's limit by its runs and runs the step with no answer in its prompt
FAIL a proposal taken once is not taken again
FAIL the step chosen again past its first limit runs
FAIL a Proposer naming an action its wait does not take records no proposal
FAIL a newer Proposer's run replaces the proposal
FAIL taking the proposal it replaced is refused
FAIL taking a proposed stop ends the flow run stopped with no run queued
FAIL the Orchestrator's instructions say how to propose a stop
FAIL an Orchestrator's stop waits on Agra with cause stop_proposed, asking why, its decision recorded and no run queued

At the head: RUNNER CORE OK, 482 ok.

tests/flows.sx (check_proposal_upgrade, a db taken back to 48 with a stored proposal). At the base: FAIL a v48 database whose flow run waits with a proposal is built, FLOWS FAIL (1 checks), as the base has no proposal columns to take back. At the head: FLOWS OK.

Flow run 27 at the base, from a probe added to the base arm only. A Proposer at run_limit reports {"action":"more_runs","runs":10,…}. The base records needs_you.proposal as {"run_id":127,"text":"Allow ten more runs."}. The only way to take it, answer with that text, runs the step with ## Answer\n\nAllow ten more runs. in its prompt, records no more_runs, and the step chosen again waits at run_limit once more. All four probe checks held.

Gates

At the head aa7e2cb5:

  • fast: gate run 539 (the run's gate #1), passed.
  • full: gate run 542 (the run's gate #2), passed: every tests/*.sx file, release.sh and step9, the deploy contract, the main Playwright config (764 passed, 1 skipped) and the runner config (10 passed).

Golden set: none. Nothing under web/e2e changes but the one stub report above.

Tracker: [#3728](https://sak.swipelab.com/tasks/3728) Step 8 of plan [#3694](https://sak.swipelab.com/tasks/3694). It fixes the store and API half of [#3688](https://sak.swipelab.com/tasks/3688). An action a Proposer proposes is taken as that action, not fed to the step as an answer, and the Orchestrator can propose a stop without naming a node that does not exist. ## What it delivers - A waiting flow run's `needs_you` carries `actions`, the routes its wait takes: `answer` while it waits about a step, `more_runs` at `run_limit`, then `propose` and `stop`. - The Proposer's instructions list the wait's actions. Its report names one: `{"action":"answer","answer":…,"proposal":…}`, `{"action":"stop","proposal":…}`, or, at `run_limit`, `{"action":"more_runs","runs":…,"proposal":…}`. A report naming an action the wait does not take, or lacking what its action takes, records no proposal. - `needs_you.proposal` reads `{run_id, action, text, answer, runs}`. The history's `proposal` event carries `action`, `proposal`, `answer` and `runs`. Migration 49 adds the flow run's `proposal_action`, `proposal_answer` and `proposal_runs`, and upgrades each stored proposal, on a flow run and on its history, to action `answer` with its text as the answer. - `POST /api/flow-runs/:id/take {"run_id":…}` takes the waiting proposal of that Proposer run as what it names, in one transaction: `answer` as `/answer` with its answer, `stop` as `/stop`, `more_runs` as `/more-runs` with its `runs`. It answers what that route answers. A flow run whose proposal is another run's, or that has none, answers `409 {"error":"proposal_changed"}`. - The Orchestrator may report `{"stop":"<why>"}`. sak records the `decision` (with `stop`, `next` null) and waits on Agra with cause `stop_proposed`, the why as its question, its actions `answer`, `propose` and `stop`. sak does not end the flow run itself. A `next` naming no node of the flow still waits `unknown_next`. - `answer` stays a step's input: no route reads an action out of an answer's text. - `docs/api.md` Flow runs describes all of it. Every schema-head assert moves to 49, and the roll-backs to 40 (`tests/tools.sx`) and 44 (`tests/utf8_text.sx`) drop the three new columns. - `web/e2e/work-the-flows.spec.ts`: the stub Proposer's report in "Ask an agent to propose…" names action `answer`, with the same text. The `needs-you-proposal` golden is unchanged. The Needs you card is step 9 ([#3729](https://sak.swipelab.com/tasks/3729)). Until then it shows the proposal's text and fills it in through "Answer with it", as before. ## Regressions Each was written before the fix, and run at the step's base 1144f367 (test files only, from a `git archive` arm) and at the head. `tests/runner_core.sx` (`check_needs_you`, and the new `check_proposals`). At the base: `RUNNER CORE FAIL (15 checks)`, 467 ok: ``` FAIL a wait on a step's question takes answer, propose and stop FAIL a wait at the run limit takes answer, more runs, propose and stop FAIL the Proposer's instructions list the actions its wait takes FAIL a proposed answer reads its action and its answer FAIL taking a proposed answer re-runs Implement, asked the question and the proposal's answer FAIL a Proposer at the run limit proposes more runs FAIL taking it raises the step's limit by its runs and runs the step with no answer in its prompt FAIL a proposal taken once is not taken again FAIL the step chosen again past its first limit runs FAIL a Proposer naming an action its wait does not take records no proposal FAIL a newer Proposer's run replaces the proposal FAIL taking the proposal it replaced is refused FAIL taking a proposed stop ends the flow run stopped with no run queued FAIL the Orchestrator's instructions say how to propose a stop FAIL an Orchestrator's stop waits on Agra with cause stop_proposed, asking why, its decision recorded and no run queued ``` At the head: `RUNNER CORE OK`, 482 ok. `tests/flows.sx` (`check_proposal_upgrade`, a db taken back to 48 with a stored proposal). At the base: `FAIL a v48 database whose flow run waits with a proposal is built`, `FLOWS FAIL (1 checks)`, as the base has no proposal columns to take back. At the head: `FLOWS OK`. Flow run 27 at the base, from a probe added to the base arm only. A Proposer at `run_limit` reports `{"action":"more_runs","runs":10,…}`. The base records `needs_you.proposal` as `{"run_id":127,"text":"Allow ten more runs."}`. The only way to take it, `answer` with that text, runs the step with `## Answer\n\nAllow ten more runs.` in its prompt, records no `more_runs`, and the step chosen again waits at `run_limit` once more. All four probe checks held. ## Gates At the head aa7e2cb5: - `fast`: gate run 539 (the run's gate #1), passed. - `full`: gate run 542 (the run's gate #2), passed: every `tests/*.sx` file, release.sh and step9, the deploy contract, the main Playwright config (764 passed, 1 skipped) and the runner config (10 passed). Golden set: none. Nothing under `web/e2e` changes but the one stub report above.
A waiting flow run's needs_you lists the actions its wait takes: answer
while it waits about a step, more_runs at run_limit, propose and stop.
The Proposer's instructions list them, and its report names one:
answer with its answer, more_runs with its runs, or stop. A report
naming an action the wait does not take records no proposal.

POST /api/flow-runs/:id/take takes the waiting proposal of a Proposer's
run as what it names, through the same work as answer, more-runs and
stop, and answers 409 proposal_changed for a proposal another run
replaced, or none.

Migration 49 keeps a proposal's action, answer and runs beside its
text, and makes each stored proposal an answer of its text.
An Orchestrator that finds the flow should end, with no node to end it,
reports {"stop": "<why>"}. sak records the decision and waits on Agra
with cause stop_proposed, the why as its question; Agra stops the flow
run or answers its step. A next naming no node still waits
unknown_next.
agra merged commit 98e9b12c60 into master 2026-10-26 10:13:51 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sx/sak!158
No description provided.