Task: Fix the compute grid dispatch and wrapper result-submit channels dropped by the ores.compute bind

Table of Contents

This page documents a task in the Manage the compute grid from the shell story. It captures the goal, current status, acceptance, and any notes or results.

1. Goal

Bind PR #2012 (the ores.compute entity bind) replaced the hand-written workunit save path and the wrapper result reporting path with the generated CRUD service. Two side effects the grid depends on were lost in that replacement: the dispatch seam that publishes JetStream assignments for saved workunits, and the trusted result-submit channel wrappers report through (wrapper nodes hold no JWT, so the generated save_result CRUD rejects them). Post-merge, the smoke script this story owns failed: batches never drained and results never landed. Restore both channels in the hand-written files that per-entity regeneration cannot remove, and prove the grid drains again live.

2. Status

Field Value
State DONE
Parent story Manage the compute grid from the shell
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-09-05

3. Acceptance

  • Saved workunits are dispatched again: the service creates the result row and publishes the JetStream assignment for platform x64-linux.
  • Wrapper nodes submit finished jobs over compute.v1.results.submit and the service accepts them without a user session; results land with server_state Done and outcome Success.
  • The Validator sets canonical_result_id at target redundancy and the Assimilator closes the batch when every workunit is canonical.
  • Live smoke on brave_hopper: 10/10 results success and every online host exercised (SMOKE PASS).
  • Full build clean; ctest 71/71 green; roundtrip and drift checks clean.

4. Plan

Restore each dropped channel in the hand-written files that per-entity codegen regeneration cannot remove: the machine protocol structs belong in work_protocol.hpp (result_protocol.hpp regenerates on every bind), the trusted handler is a new header beside the hand-written work_handler.hpp, its subscription goes in the aggregate registrar.cpp next to the heartbeat and report-submit blocks, and the dispatch seam lives in the service app layer, subscribed to the in-process event bus before event_source.start(). The wrapper call site returns to the submit protocol; the handler replies a typed envelope on every path (never error_reply), so a rejected request can never again surface as a wrapper decode failure. Verify with a full build and ctest, then redeploy the fleet and run the story's smoke script live: dispatch, drain, and assert every online host was exercised.

5. Notes

Found by the post-merge system test of the compute/iam bind PRs (the ores.shell-only test program). Placed in this story because it is the sprint's open compute-grid story and its smoke script is the artifact that caught the regression; the parent bind story ores.compute drift story is closed. The fix commits and PR description carry the full finding write-up.

6. Test Scenarios

Manual QA scenarios (scaffolded via compass add test_scenario, run through the QA Validation Runner panel) that verify this task. Link new ones here as they're created; the scenario doc itself links back via its "Verifies task" field.

Scenario State Notes
     

7. PRs

PR Title
#2015 [ores.compute] Restore grid dispatch and result-submit channels

8. Review

# Comment summary File Decision Notes
1 Validator/Assimilator read-then-write race under competing consumers result_submit_handler.hpp Declined The race needs two service instances on one queue group. The service runs single-instance, and scale-out breaks the dispatcher first (each instance would run its own event source and double-dispatch). The guarded update becomes a scale-out prerequisite, recorded in #79.
2 Partial dispatch failure is never retried workunit_dispatcher.cpp Fixed 1b8118df97: dispatch converges on target_redundancy, topping up the missing assignments on each event; skip only at the target. Build clean + ctest 71/71 green on the fix.
3 Unauthenticated submit channel (informational) result_submit_handler.hpp Confirmed intentional Trusted transport, heartbeat-grade; wrapper nodes hold no JWT. Documented in the class docstring; kept out of the JWT-gated generated result handler.
4 No unit tests for the new logic (minor) workunit_dispatcher.cpp Declined, follow-up recorded Live smoke exercised the happy path (10/10 results, 5/5 hosts). The redundancy>1 and top-up branches need a dependency seam first; recorded as test-coverage follow-up in #79.
5 Nit: avps[i % avps.size()] round-robin vs "platform x64-linux" wording workunit_dispatcher.cpp No change The spread across an app version's platform packages is deliberate; the PR wording described the smoke environment's single platform.

9. Result

Restored both channels on brave_hopper and proved the grid drains.

  • Finding #4 (dispatch): the service app layer regains workunit_dispatcher, an event_bus subscriber that creates the result row and publishes the JetStream assignment for shell-saved workunits, constructed before event_source.start() so no change event is missed.
  • Finding #5 (result submit): the wrapper reports over the trusted compute.v1.results.submit channel again — submit_result_request / submit_result_response in the hand-written work_protocol.hpp, the trusted result_submit_handler (no verifier, heartbeat-grade transport trust), its queue subscription in the aggregate registrar.cpp, and the reverted wrapper call site. The handler stamps the audit fields, runs the Validator (canonical_result_id at target redundancy) and the Assimilator (batch closed when all workunits canonical), and replies a typed envelope so wrapper decode failures cannot recur.

Verification: build clean (linux-clang-debug-make); ctest 71/71 green; roundtrip clean; cmake-sources drift clean for ores.compute (12 stale lists elsewhere pre-date this branch — untracked codegen residue in six other components, untouched here); codegen drift regenerates refdata/reporting/marketdata byte-identical. Live smoke on brave_hopper (smoke-fix3): SMOKE PASS — results 10/10 success, 5/5 online hosts exercised, canonical ids set, batch closed by the Assimilator. The system-test residue (smoke-fix1's ten state-2 rows, the st250905 dead batch, the stale queue consumer, the post-restart settle transient) is recorded in the post-merge system-test report (#79): smoke-fix1's ten state-2 rows are inert (pre-fix submit failures; the reaper only re-queues state 4), and the fleet needs a settle period after restart before the JetStream queue group covers every member (smoke-fix2 hit 3/5 hosts).

Emacs 29.3 (Org mode 9.6.15)