Dispatch now pushes the assignment's projection synchronously, right before minting the artifact/cache runtime token, instead of relying solely on the periodic sweep (up to 250ms behind). Closes the race behind the artifacts-401 investigation: the token names a node+fence the forge hasn't necessarily heard about yet when a job's first real step (artifact upload) runs.
Adds a regression test that fails without the fix (verified by temporarily reverting the dispatch call) and passes with it: syncode-control/tests/node_session.rs::dispatch_projects_the_assignment_before_the_node_can_act_on_it. Also adds Runs::projection(run) unit tests. Full workspace: fmt clean, clippy clean, all tests green (including store.rs against a real Postgres).
Dispatch now pushes the assignment's projection synchronously, right before minting the artifact/cache runtime token, instead of relying solely on the periodic sweep (up to 250ms behind). Closes the race behind the artifacts-401 investigation: the token names a node+fence the forge hasn't necessarily heard about yet when a job's first real step (artifact upload) runs.
Adds a regression test that fails without the fix (verified by temporarily reverting the dispatch call) and passes with it: syncode-control/tests/node_session.rs::dispatch_projects_the_assignment_before_the_node_can_act_on_it. Also adds Runs::projection(run) unit tests. Full workspace: fmt clean, clippy clean, all tests green (including store.rs against a real Postgres).
A node's actions_runtime_token is only valid once the forge has heard
which node and fence hold the job it names, but that binding only
existed through the periodic projection sweep - up to 250ms behind the
assignment that already carries the token. A job's first real step
(uploading an artifact) can easily run before the sweep catches up,
so the forge rejects it as unauthorized.
Dispatch now pushes that one run's projection synchronously, right
before minting the token, closing the gap for the common case. The
push is best-effort and does not block dispatch if the forge is
unreachable; the periodic sweep still covers everything after.
Keeps session.rs and dispatch.rs under the workspace's 250-line limit
after adding the synchronous projection push, by moving the pure
wire-message construction (assignment_body, wire_conclusion) into its
own module and the NodeSessionServer Clone impl next to its
constructors.
day01
merged commit e0c8eedf0e into %!s()2026-08-17 20:59:20 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Dispatch now pushes the assignment's projection synchronously, right before minting the artifact/cache runtime token, instead of relying solely on the periodic sweep (up to 250ms behind). Closes the race behind the artifacts-401 investigation: the token names a node+fence the forge hasn't necessarily heard about yet when a job's first real step (artifact upload) runs.
Adds a regression test that fails without the fix (verified by temporarily reverting the dispatch call) and passes with it: syncode-control/tests/node_session.rs::dispatch_projects_the_assignment_before_the_node_can_act_on_it. Also adds Runs::projection(run) unit tests. Full workspace: fmt clean, clippy clean, all tests green (including store.rs against a real Postgres).