test(export): the stuck-export census was taken after the worker, so it could not answer its own question - #231
Conversation
… was taken after the worker, so it could not answer its own question
`export-flows-and-agents.spec.ts` fails on `development` with
the export job must finish — last status "queued";
last worker pass: worker ok: (no output); job list: 112 total, 0 RunExportJob
and that message has been read as proof that the enqueue never happened. It is
not proof of anything. `runExportJobWorker()` took the census AFTER running
`background-job:worker`, and a QueuedJob is DELETED from `oc_jobs` once it
executes — so `0 RunExportJob` is what you see whether the job never existed or
ran to completion. The helper's own comment says the census exists to
disambiguate exactly those two cases; in that order it cannot.
- census taken BEFORE the worker pass, and both are reported;
- on a non-terminal status the failure now also reports the DEPLOYED exportJob
schema's version and whether it carries `x-openregister-lifecycle`. Without
that block OR's TransitionEngine finds no state machine and returns silently,
the job row is consumed, and the object sits at "queued" with no log line —
#219.
Read from the schema API, not the object: this OpenRegister build exposes no
`available-actions` on an object read at all (measured, including
`?_extend=all`), so probing for that key would report "absent" on a healthy
instance and read as evidence.
No assertion is relaxed and no skip is added; the failure only gets louder.
Quality Report — ConductionNL/openbuild @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| check-gitignore | ✅ | ||||
| check-nc-floor | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 106/106 | |||
| npm | ✅ | ✅ 626/626 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-16 23:51 UTC
Download the full PDF report from the workflow artifacts.
…ycle means now Merging development brought in #229, which shipped `exportJob` 0.1.0 -> 1.1.0. The probe's docblock and its failure string still told the reader that a missing `x-openregister-lifecycle` meant #219 was unfixed. On the merged base that is no longer true, and a comment that survives the change it describes is the half of a diff git cannot check. A MISSING reading now means the instance under test never converged onto the bumped schema — a narrower fault than the one #219 described. The probe keeps its value; only the conclusion it licenses changes.
The two
|
| the complaint | where #229 fixed it |
|---|---|
[gate-66] openregister-dependency-shape: FAIL |
13 × lib/ — the whole lib/Mcp/Handler/* set, OpenBuildToolProvider, ExportJobService, JobOwnerImpersonator. gate-66 inspects ADR-083 shape in lib/. My diff has no lib/ — your own instinct was right. |
| "Outsider sees the seeded app in NO list — IDOR closed" | tests/integration/openbuild-rbac.postman_collection.json |
| "newman-added page is reflected" | tests/integration/openbuild-page-editor.postman_collection.json |
| "Invalid manifest is rejected with 4xx" | ApplicationsController.php + ApplicationsControllerSaveManifestTest.php |
the two unknown-templateId 4xx-not-500 assertions |
openbuild-templates-marketplace.postman_collection.json |
That is why the five failures "do not look like one defect" — they are not one defect, and they are not one bad instance either. They are five separate repairs I was missing, which is exactly the spread you would expect.
Both hypotheses tested and rejected, on their own terms:
- State leakage from my spec — structurally impossible, twice over. I audited every added line for a write verb: the diff adds exactly two server interactions,
occ background-job:list(read) andGET /api/schemas(read). Zero POST/PUT/PATCH/DELETE. There is nothing to orphan. AndIntegration Tests (Newman)andE2E Tests (Playwright)are separate jobs, each provisioning its own Nextcloud — a Playwright spec cannot write into Newman's database at all. (The procest shape you cite needs a shared instance and a write; this has neither.) - Flake / 504 — not needed, and it would have been the wrong answer. A rerun would have gone green and I would have logged "flaky", which is worse than the truth: these checks are deterministically red at
4ebc9035and deterministically green after fix(ci): green the E2E, Newman and Hydra Gates jobs — at their real causes #229. Nothing was flaking.
No Newman assertion is relaxed. I did not touch a postman collection, and the IDOR assertion is untouched — on the rebased branch it is #229's passing version.
One thing the merge did change, and it is worth flagging. #229 shipped the bump I measured as necessary and deliberately declined to guess at: exportJob 0.1.0 → 1.1.0. That made my own prose stale — the docblock and the failure string still told the reader a missing x-openregister-lifecycle meant #219 was unfixed. Fixed in a753e24f: a MISSING reading now means this instance never converged onto the bumped schema, a narrower fault. A comment that outlives the change it describes is the half of a diff git cannot check, so it gets corrected rather than carried.
What to expect from the rebased run: gate-66 and Newman green (they are green on the base); E2E Tests (Playwright) still red at 189 · 3 · 65, unchanged — this PR relaxes nothing and adds no skip. Read its new failure message, not its cell.
Quality Report — ConductionNL/openbuild @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| check-gitignore | ✅ | ||||
| check-nc-floor | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 106/106 | |||
| npm | ✅ | ✅ 626/626 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-17 01:02 UTC
Download the full PDF report from the workflow artifacts.
Rebased run settled — parity, and a correction to my own predictionAll 42 rows,
INTRODUCED: none. Both checks you flagged are green, with no E2E: parity proven by NAME, not by tally
Same test, by name — a matching tally alone would have been compatible with two different failures. That test is the documented flake #188 ("the version-history row resolves 37× and stays hidden"), and it is in neither my scope nor my diff.
|
|
Merging at parity. The
The same test, at the same line — 'rolling back RESTORES the snapshot manifest onto the active version' — failed on a base commit that does not contain this PR, and passed on a later one. Same code, two outcomes: non-deterministic by construction. This PR's diff touches the export census only; it adds no versioning code, relaxes no assertion, adds no skip and raises no timeout. Green on this head, asserted to exist by name before reading the conclusion: Why merge despite the export test now passing. #229 landed the |
The triage this came out of
openbuild's E2E failures were carried on the board as "3 rotating failures, all DECISION-16 (nc-vue
CnDetailPagelatch)". Measured across three consecutivedevelopmentCode Qualityruns, that is not the shape:319555944123196004390631964268323Read off the embedded
playwrightReportBase64in each run'splaywright-reportartifact, not off log greps. So two failures are persistent and identical, and only the third rotates.builder-host.spec.ts— navigates to a hello-message detail pagedevelopmentand on PRs alike). A detail page whose sibling tests either side of it pass in the same run — the DECISION-16 signature. Not touched here.spec-coverage/app-icon-management.spec.ts— Remove in the dark slot…iconUpload.spec.ts› "the Icons tab mounts the upload section with light + dark variants and an SVG-only picker" passes in the same run, on the same Icons tab of the same detail page. So the tab is not permanently latched, and this needs a repro I could not get. Escalated, not guessed at.export-flows-and-agents.spec.ts— an operator binds a flow…What this PR changes
The export failure reads:
and
0 RunExportJobhas been read as "the enqueue never happened" — the helper's own comment says the census exists to prove exactly that. It proves nothing, because the census was taken afterbackground-job:workerran, and aQueuedJobis deleted fromoc_jobsonce it executes.0 RunExportJobis what you see whether the job never existed or ran to completion. A check that cannot distinguish its two hypotheses is the exact shape this spec was written to close, reproduced inside the helper written to close it.beforenon-zero +afterzero ⇒ the worker consumed our job, so a status still onqueuedmeans the transition did not fire.beforezero against a non-zero total (provingoccshares the web server's database) ⇒ the enqueue genuinely did not happen.exportJobschema — its version, and whether it carriesx-openregister-lifecycle. Without that block OR'sTransitionEnginefinds no state machine and returns silently: the job row is consumed and the object sits atqueuedwith no log line anywhere (fix(schema): bump exportJob past the version instances already carry #219, still open —exportJobdeclares0.1.0, and the dev instance carries 1.0.1, so this repo's lifecycle edits can never reach it).Read from the schema API rather than the object: this OpenRegister build exposes no
available-actionson an object read at all — measured on:8080, including with?_extend=all— so the probe #219 proposed would report "absent" on a perfectly healthy instance and read as evidence.What this does NOT claim
It does not claim the red cell goes green. No assertion is relaxed, no skip is added, no timeout is raised; the failure only gets louder and names its own cause on the next run.
I could not settle the root cause from outside the instance, and I am not going to guess at it: CI seeds a fresh instance (
ci-seed.sh→settings#load (forced import), and the run's own log listsexport-jobamong the provisioned schemas), so the declared-0.1.0schema should import with its lifecycle and #219 should not bite there — while the dev instance, whose stored schema is at 1.0.1 with the lifecycle, has export-job objects sitting atstatus: succeeded. Those two facts do not yet compose into an explanation. The two probes added here are chosen to decide it on the next run rather than argue about it.Verification
npx playwright test --liston the changed file: 2 tests in 1 file, compiles.prettier --checkon the changed file: clean (base was clean too — checked with the change stashed, so this is not laundering a pre-existing violation).eslintreports 2 pre-existingimport-extensionserrors on lines 36–37, present identically on the base and on imports this PR does not touch; CI'slintscript iseslint src, which does not covertests/.