Skip to content

Fix six follow-ups: xdist ids, decision table graphs, gate output, Studio replay identity, human-task races, platform user viewer scope - #57

Merged
ancongui merged 10 commits into
mainfrom
fix/followups-oct-20
Oct 20, 2026
Merged

ancongui merged 10 commits into
mainfrom
fix/followups-oct-20

Conversation

@ancongui

@ancongui ancongui commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Six small, independent fixes. Each fix has a test that fails without it.

Stable parametrized test ids under pytest-xdist (F56)

tests/unit/operations/test_run_views.py and tests/unit/test_ai_endpoints.py built parametrized ids from uuid4(), so each xdist worker collected different ids and pytest tests/unit -n 4 stopped at collection ("Different tests were collected between gw0 and gw1"). Both now use fixed ids. A double --collect-only diff over tests/unit tests/contracts finds no other nondeterministic id. pytest tests/unit tests/contracts -n 4 now collects and runs: 7,761 passed.

weave workflow graph draws decision tables (F48)

The graph labels had no entry for the decisionTable node kind, so text, Mermaid and SVG exports raised KeyError: 'decisionTable'. Decision table steps are labeled Apply a decision table. The tests compile a workflow with a decision table and export it in all three formats. A second test checks that every node kind in IRNode has a label.

Failing stage and test ids when the gate fails (F60)

scripts/check.py printed only Checks failed; inspect private evidence., and CI keeps no evidence, so the failing test could not be identified. On failure it now prints Stage <name> failed. and, from the stage log's last pytest short summary, the FAILED/ERROR node ids. The - message part, tracebacks, captured output and lines printed before the summary are never shown. The output is capped at 50 lines of 300 characters, and ANSI color codes are removed. Stage logs stay in the private evidence directory. CONTRIBUTING.md describes the new output.

Studio replays a capacity-refused change only for the same person (F57)

StudioApi.request paused for Retry-After and then sent a keyed change again without rechecking who was signed in. If another person signed in during the pause, the host sent the replay as that person. The shell now gives the API a replay guard: before sending a keyed change again, the API waits for identitySettled() and compares scopeKeyOf(shell) with its value at the first send. This is the same check the Workers and Runs bulk loops have used since #47. If the person or workspace changed, the original 429 stands and nothing more is sent. Reads are unchanged, so identity checks, which are reads, cannot wait on themselves.

  • vitest: the person changes during the pause, and nothing is sent until the check settles, then nothing is sent at all. With the same person, the change is sent again after the check.
  • Playwright (worker-operations.spec.ts): a bulk Drain whose first send gets 429 with Retry-After: 2, while John signs in during the pause. Without the fix, the replay went out as John.

Human-task race tests and capacity refusals (F45)

Running test_human_tasks.py before test_human_files.py was not what caused the failures. These tests race two changes in one project, and a change waits at most 250 ms (lock_timeout) for the project's operation lock. When the other side holds the lock longer, which happens on a cold or loaded database, the request gets 429 WV-OPERATION-CAPACITY instead of its real outcome. The failures came and went with the database's speed, in either order. Refusing in this case is correct product behavior: nothing ran. So the tests now send a capacity refusal again, as a client does, before checking the outcome. This applies to the expiry-versus-completion, completion-versus-cancellation, concurrent claim, concurrent completion and keyed run-start races. A new variant makes the deadline tick hold the lock for 600 ms, and it reproduced the 429 on every run before the fix.

weave platform user grants viewer on the demo project (F63)

viewer was granted on the demo environment. The catalog is a project resource, and under tenant ⊃ project ⊃ environment an environment grant never covers a project-level read. So a person created with, for example, --role operator --role viewer or --role deployment_approver --role viewer could not see published workflows, although the guides say viewer reads the catalog. viewer is now granted on the demo project, as developer already is, and as the X1 fixture does. The CLI help, CLI reference, local-platform, people-and-access and roles guides are updated. Roles that never include catalog.read, such as deployment_approver alone, still get no catalog read. Adding one automatically would be an implicit grant, and that is left for a product decision.

Checks

  • make check: all 17 stages passed. Unit and contract tests ran serially: 7,761 passed. Agentic worker 222 passed, files worker 42.
  • pytest tests/unit tests/contracts -n 4: 7,761 passed.
  • Studio: npm run check, format:check, vitest 2,393 passed. Playwright: shell, studio-bootstrap, operate-releases, deployment-operations, operate-overview, layout-fixes, operate-runs, run-actions and worker-operations, 260 passed.
  • PostgreSQL 17 on a task-owned container: test_human_tasks.py and test_human_files.py (27 tests) passed in both orders, four runs in total. The container and its network were removed using the recorded IDs. No other suites ran against real services.

Independent review

A fresh reviewer read the diff and found nothing Critical or Important; the verdict was ready. It reverted each fix and confirmed its test then fails, for F56, F48, F60, F63 and the F57 vitest cases. For the F57 Playwright test and F45 it reasoned from the code instead. Three Minor findings are fixed in d98f192:

  • A cancel now also ends the wait for a sign-in check that is still out. A new vitest case shows the cancel otherwise hung until the test timed out.
  • The gate reads node ids only up to pytest's closing summary line.
  • The capacity-replay browser test gives a slow runner more time inside the pause (Retry-After: 5).

Two Minor findings remain open as follow-ups:

  • The guard compares Studio's view of who is signed in. A sign-in made outside this window that Studio has not checked yet is not detected; the bulk loops have the same gap. Closing it needs the host to check the expected principal.
  • scopeKeyOf compares the whole identity, so a refresh for the same person whose grants changed refuses the replay. This fails safe.

Andres Contreras added 10 commits October 19, 2026 21:25
Two parametrized tests built their ids from uuid4(), so each xdist
worker collected different ids and 'pytest -n 4' aborted at collection.
Use fixed ids instead.
weave workflow graph raised KeyError: 'decisionTable' on any workflow
with a decision table step because the graph labels had no entry for
that node kind. Label it, and pin that every compiled node kind has a
label.
scripts/check.py printed only 'Checks failed; inspect private evidence.',
and CI keeps no evidence, so a failing unit test could not be identified.
On failure it now prints the stage name and the FAILED/ERROR node ids
from pytest's short summary. The ' - message' part, tracebacks and
captured output stay in the private stage log.
After a capacity refusal, Studio paused and sent the same keyed change
again without rechecking who was signed in. If another person signed in
during the pause, the host sent the replay as them. The replay now waits
for any sign-in check still out, as the Workers and Runs bulk loops do,
and is sent only when the person and workspace are unchanged; otherwise
the refusal stands and nothing more is sent.
test_due_expiry_wins_concurrent_completion and
test_completion_races_cancellation_under_run_lock failed with 429
WV-OPERATION-CAPACITY instead of 409 whenever one side held the
project's operation lock longer than the other's 250 ms lock wait,
which a cold or loaded database does. That depended on timing, not on
which module ran first. A capacity refusal runs nothing, so the tests
now send it again, as a client does, and assert the real outcome. A
variant whose deadline tick holds the lock for 600 ms reproduces the
429 every time.
weave platform user granted viewer on the demo environment, but the
catalog is a project resource and an environment grant never contains a
project-level read (tenant > project > environment). A person created
with operator or deployment_approver plus viewer could not see published
workflows in Studio, although the guide says viewer reads the catalog.
viewer is now granted on the demo project, as developer is.
The concurrent claim, concurrent completion and keyed run start tests
race two changes in one project the same way, and the completion race
failed with 429 WV-OPERATION-CAPACITY in a tasks-before-files run.
From the independent review: a canceled request no longer waits for a
slow sign-in check before it stops, the gate reads node ids only up to
pytest's closing summary line, and the browser test of the capacity
replay leaves a slow runner more time inside the pause.
@ancongui
ancongui merged commit 900bbb1 into main Oct 20, 2026
19 checks passed
@ancongui
ancongui deleted the fix/followups-oct-20 branch October 20, 2026 06:23
@ancongui ancongui mentioned this pull request Oct 20, 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