Skip to content

spec(runtime): REQ-OBR-002/003 described an architecture the app abandoned - #459

Merged
rubenvdlinde merged 6 commits into
developmentfrom
fix/runtime-spec-matches-what-shipped
Aug 25, 2026
Merged

spec(runtime): REQ-OBR-002/003 described an architecture the app abandoned#459
rubenvdlinde merged 6 commits into
developmentfrom
fix/runtime-spec-matches-what-shipped

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

The requirements described an architecture the app abandoned

REQ-OBR-002/003 mandated a nested CnAppRoot inside the Buildiq shell —
outer CnAppNav, header and chrome staying visible, mounted by
BuilderHost.vue — plus an outer router forwarding an opaque path suffix to
an inner one.

The product deliberately does the opposite, and says so in its own source.
appinfo/routes.php maps the bare /builder/{slug} to dashboard#builder, a
standalone page booting the src/builder.js entry, whose header states why:

It is deliberately NOT the Buildiq SPA: rendering the app inside Buildiq's
shell nests one NcContent in another (double chrome) and, worse, shares
Buildiq's router — which has none of the app's page routes, so page content
never resolves.

BuilderHost.vue still exists and is still registered, but only ever mounts for
builder sub-paths falling through to the SPA catch-all — never for the
runtime route these scenarios navigate to. So buildiq-builder-host was
genuinely absent while the app rendered correctly, and the two e2e tests could
not pass however they were written. They were left permanently skipped with a
note recommending exactly this rewrite.

What changed

REQ-OBR-002 now describes the standalone shell, and its scenario asserts the
property the old wording had backwards: one shell, not two.

REQ-OBR-003 now describes the app's own router resolving its own routes, and
drops the outer/inner forwarding machinery that has no counterpart in the
shipped design. Its scenario is written around opening a row rather than
deep-linking 00000000-0000-0000-0000-000000000000 — a uuid in no fixture, so
the old scenario could not have been asserted even had the architecture matched.

The test is real, not a stub

This matters, because 29 of this repo's disabled tests have bodies that are only
goto(/applications) + expect(main).toBeVisible(). Enabling one of those
would make it pass while proving nothing, and start crediting its scenario with
coverage it does not have.

The new REQ-OBR-002 test opens /builder/hello-world, asserts the app's own
seeded index content, then counts mounted CnAppRoot instances through the
existing componentTree helper and requires exactly one. Counting is the
point: the abandoned design would show two, and a visibility check cannot tell
those apart. It also asserts the SPA's builder-host wrapper has count 0 here.

No test is written for REQ-OBR-003. "REQ-OBR-004 — the seeded index lists the
three sample messages and opens one" already clicks a row, waits for the URL to
move onto /messages/:id, and asserts the detail renders. That is the
rewritten scenario; a second test would add a passing assertion without adding
coverage.

Verification

npm run format — the repo's own gate, prettier --check "**/*.{js,ts,vue,css,scss}" — passes across the tree.

Surfaced by the e2e skip-discipline gate, ConductionNL/.github#559.

…doned

Both requirements mandated a NESTED CnAppRoot inside the Buildiq shell, with
the outer CnAppNav, header and chrome staying visible, mounted by
BuilderHost.vue, and an OUTER router forwarding an opaque path suffix to an
INNER one.

The product deliberately does the opposite, and says so in its own source.
appinfo/routes.php maps the bare `/builder/{slug}` to `dashboard#builder`, a
standalone page booting the `src/builder.js` webpack entry, whose header states
the reason:

    It is deliberately NOT the Buildiq SPA: rendering the app inside Buildiq's
    shell nests one NcContent in another (double chrome) and, worse, shares
    Buildiq's router — which has none of the app's page routes, so page content
    never resolves.

BuilderHost.vue still exists and is still registered, but only ever mounts for
builder sub-paths falling through to the SPA catch-all — never for the runtime
route these scenarios navigate to. So `[data-testid="buildiq-builder-host"]`
was genuinely absent while the app rendered correctly, and the two e2e tests
could not pass however they were written. They were left permanently skipped
with a note recommending exactly this rewrite. This is that rewrite.

REQ-OBR-002 now describes the standalone shell, and its scenario asserts the
property the old wording had BACKWARDS: one shell, not two.

REQ-OBR-003 now describes the app's own router resolving its own routes, and
drops the outer/inner forwarding machinery, which has no counterpart in the
shipped design. Its scenario is written around opening a row rather than
deep-linking `00000000-0000-0000-0000-000000000000` — a uuid that exists in no
fixture, so the old scenario could not have been asserted even had the
architecture matched.

THE TEST IS REAL, NOT A STUB

The REQ-OBR-002 test now opens `/builder/hello-world`, asserts the app's own
seeded index content, and then COUNTS mounted CnAppRoot instances via the
existing componentTree helper, requiring exactly one. Counting is the point: the
abandoned design would show two, and a plain visibility check cannot tell those
apart. It also asserts the SPA's builder-host wrapper has count 0 on this route.

No test is written for REQ-OBR-003: "REQ-OBR-004 — the seeded index lists the
three sample messages and opens one" already clicks a row, waits for the URL to
move onto the manifest's `/messages/:id` route, and asserts the detail renders.
That IS the rewritten scenario; a second test would add a passing assertion
without adding coverage.

Verified with the repo's own gate — `npm run format`
(prettier --check "**/*.{js,ts,vue,css,scss}") passes across the tree.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ 25fedd4

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
check-l10n-js
check-schema-l10n
composer ✅ 106/106
npm ✅ 642/642
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-25 05:38 UTC

Download the full PDF report from the workflow artifacts.

Two tests failed on flow creation, not on anything they assert:

    creating the fixture flow must succeed (got 400)
    seeding the definition must succeed (got 400)

Both fixtures build `openregister.trigger-schedule` nodes with `config: {}`.
OpenRegister validates that config on save now and refuses an empty one: the
node requires a five-field `cron`, and TriggerScheduleNode::validateActingIdentity()
requires a `runAs` naming an existing account — "nobody is present when a
schedule fires, so there is no session to take an identity from, and the flow's
owner is not used as a fallback".

These fixtures exist to be exported and re-imported, never to fire, but they
still have to be documents the platform accepts.

`runAs` is the admin, which is the account the e2e session actually runs as, so
the fixture names something true rather than a placeholder that happens to
exist.

THIRD APP HIT BY THE SAME UPSTREAM CHANGE

integriq's JobToFlowGenerator emitted schedule triggers with no `runAs` (fixed
in ConductionNL/integriq#1568) and its PHPUnit went red on the node's own
validateConfig. decidiq and learniq were untouched. Worth noting for anyone
else seeing a sudden 400 on flow-create: the request shape did not change, the
node's contract did.

Verified: `npm run format` clean across the tree.
THE DEFECT THIS FILE'S HEADER DESCRIBED NO LONGER EXISTS

It read: "DocumentActions filters attachments by object['@self'].schema, which
OpenRegister returns as the NUMERIC schema id ("21"), while a
runtime.documents[] entry declares a schema SLUG ("hello-message"). The two
never match, so the surface renders nothing for every real object."

The component now injects `cnObjectContext` — whose `schema` is the manifest
slug — and resolves candidates through `objectSchemaKeys()`, which collects
`[ctx.schema, obj.schema, self.schemaSlug, self.schema]` and matches any of
them. Slug and numeric id both hit.

A skip reason outliving its cause is how this file came to carry twelve tests
asserting nothing, so it is corrected rather than left to age further. It is the
second such case in this repo today: the `buildiq#41` quarantine cited across 16
files names a PR that MERGED on 2026-07-27.

TWO STUBS BECOME REAL TESTS

Both were `goto('/applications')` + `expect(main).toBeVisible()` — bodies that
would pass without ever reaching a runtime object.

  REQ-DDT-004 "no attachments renders nothing" now opens a real object detail on
  the seeded `hello-world` runtime app (which declares no runtime.documents[] —
  exactly the fixture the scenario needs) and asserts `.ob-document-actions` has
  count 0. The component gates its whole root on `v-if="schemaAttachments
  .length"`, so "renders nothing" is an ABSENT element, not an empty one.

  REQ-DDT-005 "runtime surface degrades without requests" asserts what the DOM
  cannot show: that no request to /apps/docudesk/ is issued at all. A surface
  that renders empty while still calling Docudesk on every object detail is the
  failure this scenario exists to catch, and it is invisible in markup.

THE REQ-DDT-003 STUBS ARE LEFT SKIPPED, DELIBERATELY

They need a published app carrying an attachment — real fixture work, not a
skip to remove. Their notes now say that instead of citing the retired defect.
Enabling them as they stand would produce green tests asserting nothing and
would start crediting their scenarios with coverage they do not have.

Verified: `npm run format` clean across the tree.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ 21e20c9

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
check-l10n-js
check-schema-l10n
composer ✅ 106/106
npm ✅ 642/642
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-25 05:46 UTC

Download the full PDF report from the workflow artifacts.

…notes

THE QUARANTINE CITED ACROSS THIS SUITE IS STALE

Sixteen spec files deferred to "Conduction/buildiq#41: buildiq admin UI not
functional in this build". #41 is a PULL REQUEST that MERGED on 2026-07-27, and
47 spec files in this suite already pass against that same UI —
applicationDetailOverview.spec.ts alone has 9 passing tests.

Every one of those notes now says what actually blocks its test: the body is a
stub. Leaving them pointed at a merged PR is how a file ends up carrying twelve
tests that assert nothing, which this repo has already done twice.

ONE STUB BECOMES A REAL TEST

REQ-NTS-005 "designer degrades when nldesign is missing" was
`goto('/applications')` + `expect(main).toBeVisible()` — it asserts nothing
about themes and passes on any page that renders.

It now opens the page designer, scrolls to the Theme section, and asserts BOTH
halves of degrading: that the absence is EXPLAINED (`.ob-theme-section__hint`
visible) and that the control which cannot work is disabled rather than
silently inert (`Change` is `:disabled="!nldesignAvailable"`).

Deterministic, not hopeful: this app's CI installs openregister and docudesk and
NOT nldesign (code-quality.yml `additional-apps`), so `nldesignAvailable` is
false and the degraded branch is the one under test.

WHAT IS DELIBERATELY LEFT SKIPPED

The other stubs stay disabled with corrected notes. They need real fixtures — a
published app carrying a theme, a seeded token-set catalogue — and enabling them
as they stand would produce green tests asserting nothing AND start crediting
their scenarios with coverage they do not have. gate-19 counts `skip`/`fixme`
as off, so while skipped they credit nothing; that is the honest state until
bodies exist.

Verified: `npm run format` clean across the tree. The one eslint finding on the
new import is `import-extensions/extensions`, which fires 71 times across
tests/e2e already — no file in this repo uses the `.ts` extension, so the
import follows the established convention rather than inventing a new one.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ b25644c

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
check-l10n-js
check-schema-l10n
composer ✅ 106/106
npm
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-25 05:49 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ 0c14fe7

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
check-l10n-js
check-schema-l10n
composer ✅ 106/106
npm ✅ 642/642
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-25 06:20 UTC

Download the full PDF report from the workflow artifacts.

rubenvdlinde and others added 2 commits August 25, 2026 10:04
Both new runtime tests timed out at 30s, and not for want of the element:

    locator.click: Test timeout of 30000ms exceeded
    waiting for getByText('Welcome to Buildiq').first()
      52 x waiting for element to be visible, enabled and stable

The page snapshot shows the runtime app rendered correctly — the "Messages"
heading, the table, and the "Welcome to Buildiq" cell all present. The click
spent its whole timeout on actionability, which is what a covering overlay
looks like.

The runtime app declares a walkthrough that pops a beat AFTER navigation
settles. `dismissOverlays()` from appFixture checks instantaneously and races
it; `dismissFirstVisitOverlays()` from support/overlays polls with `waitFor()`,
which is why buildiq-runtime.spec.ts drives this same route successfully with
it. Switched, and the row is scrolled into view before the click.

`dismissOverlays` stays imported — the designer tests in this file still use it
on a surface that has no walkthrough.
The conflict was three hunks of the same fix, written twice. This branch
carried the earlier partial version — a `NC_ADMIN` constant plus an
eight-line comment repeated at each of the two call sites. development
has since landed the complete one: `RUN_AS` and `FIXTURE_CRON` as named
constants, with the rationale hoisted into their docblocks instead of
duplicated, and the finding that `cron` and `runAs` are refused
SEPARATELY — so supplying only one still fails, with an error naming
only the other, which is why the first attempt at this fix appeared to
move the error rather than clear it.

Resolved to development's version verbatim for that file. It is now
identical there, which is correct: the fixture's schedule config is
development's concern, not this branch's.

This branch's own contribution is untouched — the openbuild-runtime spec
rewrite (+58/-32) and seven other e2e files.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ f67b5db

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
check-l10n-js
check-schema-l10n
composer ✅ 106/106
npm ✅ 642/642
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-25 08:49 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit 51a8fd3 into development Aug 25, 2026
87 checks passed
@rubenvdlinde
rubenvdlinde deleted the fix/runtime-spec-matches-what-shipped branch August 25, 2026 09:28
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