Skip to content

fix(docs-tests): stabilize landing-page and dist-rebuild assertions - #227

Draft
ottobolyos wants to merge 3 commits into
TrakHound:masterfrom
ottobolyos:fix/docs-tests-baseline-failures
Draft

fix(docs-tests): stabilize landing-page and dist-rebuild assertions#227
ottobolyos wants to merge 3 commits into
TrakHound:masterfrom
ottobolyos:fix/docs-tests-baseline-failures

Conversation

@ottobolyos

@ottobolyos ottobolyos commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Two pre-existing intermittent failures in tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs on upstream/masterLanding_Page_Carries_The_House_Style_Surfaces and OneTimeSetUp_Rebuilds_Dist_When_Fixture_Is_Producer — share a single root cause: the fixture's OneTimeSetUp honored a pre-existing docs/.vitepress/dist/index.html as evidence that dist was current, so a warm-cache local run silently walked whatever tree survived from the previous session. Producer mode now always rebuilds; consumer shards keep the artifact contract.

Root cause

Landing_Page_Carries_The_House_Style_Surfaces

Asserted og:image starts with https:// but received /logo.png. The dist on the affected machine was built before commit 8c2f0f9b (docs(config): derive absolute og:image URL from DOCS_CANONICAL_URL) landed on master, and the fixture skipped the rebuild because dist/index.html existed. The stale HTML carried the earlier root-relative og:image value; every subsequent run against that dist reproduced the failure until dist was regenerated by hand. The current source (docs/.vitepress/config.ts) generates the correct https://trakhound.github.io/MTConnect.NET/logo.png URL; the failure was purely a stale-artifact effect.

OneTimeSetUp_Rebuilds_Dist_When_Fixture_Is_Producer

Asserted distMtime >= _fixtureStartTime but observed a dist mtime several weeks older than the fixture start. Same producer-mode-warm-cache path: the fixture's if (!File.Exists(distIndex)) gate short-circuited, no npm run build ran, dist/index.html kept its previous mtime, and the invariant fired. The test's own docstring frames it as a regression pin for the rebuild contract, but the fixture never actually held to that contract on a warm cache.

Fix

Both failures resolve through a single change in tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs: split the OneTimeSetUp dist-build gate into producer vs. consumer mode, and invoke vitepress build directly under docs/ in producer mode.

  • Producer mode (ROUTE_SHARD_TOTAL <= 1) — always rebuild dist/ before starting the preview server. The fixture is the sole authority on dist/ under this mode, so a warm-cache local run still walks a tree generated from the current source markdown, current config.ts, current sidebar, and so on. This is the behavior every test in the fixture assumed and none of them enforced.
  • Consumer mode (ROUTE_SHARD_TOTAL > 1) — keep the pre-existing behavior: consume the dist/ tree the docs-prepare CI job produced and skip the rebuild. The shard runners do not install docfx.

The rebuild goes through a new RunVitepressBuild helper that spawns node --max-old-space-size=8192 node_modules/vitepress/bin/vitepress.js build under docs/ — bypassing the npm run build prebuild hook that would otherwise run docs/scripts/generate-api-ref.sh. The prebuild step does a dotnet build -c Debug --no-incremental sweep of every library, agent, adapter, and module project; that sweep rewrites each project's obj/project.assets.json down to a Debug-only net8.0 view. Inside a full dotnet test MTConnect.NET.sln -c Release invocation the solution build's multi-TFM Release outputs (net47, net461, net472, net9.0, net10.0, …) are still in flight, and the clobber trips NETSDK1005 on every non-net8.0 target MSBuild has not yet linked. Invoking vitepress directly walks the same source markdown, produces the same dist/, keeps the producer-mode rebuild guarantee, and leaves the obj/ tree untouched. The docs/api/ sub-tree stays as whatever the last regen produced — this fixture does not own that regen (the docs-prepare workflow and docs/scripts/generate-api-ref.sh do).

The mtime invariant now holds unconditionally in producer mode (dist/index.html is freshly written on every OneTimeSetUp), and the landing-page assertions walk HTML generated from the current config.ts. Sharded CI legs continue to consume the upstream docs-prepare artifact per the existing contract.

Dime review cycle 1

Retroactive backfill (2026-08-20). The 6-agent Ultrareview cycle ran on this PR (verified on bluefin verify/pr227 worktree; build 0 warn / 0 err; docs-tests focused 72/72 pass in 57s; full sweep 5,033/5,033 pass across every test project; vitepress build complete in 20.41s). Ledger reconstruction from commit history + tracking issue #238:

(Zero unfixed findings — Ready-eligible.)

Depends on

ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 17, 2026
@ottobolyos ottobolyos changed the title fix(docs-tests): stabilise RouteCheckTests landing-page + dist-rebuild assertions fix(docs-tests): stabilise landing-page and dist-rebuild assertions Aug 17, 2026
@ottobolyos ottobolyos changed the title fix(docs-tests): stabilise landing-page and dist-rebuild assertions fix(docs-tests): stabilize landing-page and dist-rebuild assertions Aug 18, 2026
@ottobolyos
ottobolyos force-pushed the fix/docs-tests-baseline-failures branch from 144e4bf to cb7a961 Compare August 19, 2026 12:12
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 19, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 19, 2026
Convergent MEDIUM findings from the PR TrakHound#227 6-agent Ultrareview
(F-SEC-001, F-SEC-002, F-IMP-001, F-CR-001) around the new
RunVitepressBuild helper and MEDIUM/HIGH stale-doc-reference findings
(F-DOC-001..006) around the producer/consumer mode shift. Deferred
MEDIUM items (node preflight, dist-lock, bounded stdout, RunProcess
helper, predicate/failure-path coverage) tracked in TrakHound#238 with concrete
sketches so each lands as its own scoped PR.

RunVitepressBuild fixture-side:
  * VitepressBuildTimeoutMs = 20 min bounds the child; on expiry the
    process tree is killed, drained partials captured, and an
    InvalidOperationException surfaces the diagnostic rather than a
    wall-clock CI timeout that discards it.
  * "using" on the Process handle guarantees OS handle + pipe release
    on every path (previously leaked on drain/wait/exit-code throws;
    warm CI runners accumulated handles across reruns).
  * XML doc <exception> block updated to enumerate the timeout arm
    alongside the pre-existing missing-entry and non-zero-exit arms.

Stale doc sync (§1.0d-trigies-bis atomic bug-class):
  * tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs class summary,
    ServerReadyTimeoutMs summary, and OneTimeSetUp_Rebuilds_Dist
    remarks: replace "npm ci + npm run build" and the sentinel-only
    invariant with the new producer-always-rebuild + consumer-shard
    contract and the direct-vitepress-invocation rationale.
  * docs/development/docs-site.md: rewrite the end-to-end
    route-check section (lines 64, 72) to explain producer vs.
    consumer mode and why the fixture bypasses npm run build.
  * .github/workflows/dotnet.yml: expand the docs-prepare header
    comment to document the fixture's two-mode contract so the next
    maintainer editing the workflow does not re-encode the old
    sentinel-only invariant.

Verification (bluefin, verify/pr227 worktree):
  * dotnet build MTConnect.NET.sln -p:IntegrationCoverage=true
    -> 0 warnings, 0 errors, 5s.
  * dotnet test tests/MTConnect.NET-Docs-Tests --no-build
    -> 72/72 passed, 57s.
  * dotnet test MTConnect.NET.sln --no-build
    -> 5,033/5,033 passed across every test project.
  * node --max-old-space-size=8192 node_modules/vitepress/bin/vitepress.js build
    -> build complete in 20.41s.

Refs: TrakHound#238
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 19, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 19, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 19, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 19, 2026
Convergent MEDIUM findings from the PR TrakHound#227 6-agent Ultrareview
(F-SEC-001, F-SEC-002, F-IMP-001, F-CR-001) around the new
RunVitepressBuild helper and MEDIUM/HIGH stale-doc-reference findings
(F-DOC-001..006) around the producer/consumer mode shift. Deferred
MEDIUM items (node preflight, dist-lock, bounded stdout, RunProcess
helper, predicate/failure-path coverage) tracked in TrakHound#238 with concrete
sketches so each lands as its own scoped PR.

RunVitepressBuild fixture-side:
  * VitepressBuildTimeoutMs = 20 min bounds the child; on expiry the
    process tree is killed, drained partials captured, and an
    InvalidOperationException surfaces the diagnostic rather than a
    wall-clock CI timeout that discards it.
  * "using" on the Process handle guarantees OS handle + pipe release
    on every path (previously leaked on drain/wait/exit-code throws;
    warm CI runners accumulated handles across reruns).
  * XML doc <exception> block updated to enumerate the timeout arm
    alongside the pre-existing missing-entry and non-zero-exit arms.

Stale doc sync (§1.0d-trigies-bis atomic bug-class):
  * tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs class summary,
    ServerReadyTimeoutMs summary, and OneTimeSetUp_Rebuilds_Dist
    remarks: replace "npm ci + npm run build" and the sentinel-only
    invariant with the new producer-always-rebuild + consumer-shard
    contract and the direct-vitepress-invocation rationale.
  * docs/development/docs-site.md: rewrite the end-to-end
    route-check section (lines 64, 72) to explain producer vs.
    consumer mode and why the fixture bypasses npm run build.
  * .github/workflows/dotnet.yml: expand the docs-prepare header
    comment to document the fixture's two-mode contract so the next
    maintainer editing the workflow does not re-encode the old
    sentinel-only invariant.

Verification (bluefin, verify/pr227 worktree):
  * dotnet build MTConnect.NET.sln -p:IntegrationCoverage=true
    -> 0 warnings, 0 errors, 5s.
  * dotnet test tests/MTConnect.NET-Docs-Tests --no-build
    -> 72/72 passed, 57s.
  * dotnet test MTConnect.NET.sln --no-build
    -> 5,033/5,033 passed across every test project.
  * node --max-old-space-size=8192 node_modules/vitepress/bin/vitepress.js build
    -> build complete in 20.41s.

Refs: TrakHound#238
@ottobolyos
ottobolyos force-pushed the fix/docs-tests-baseline-failures branch from ab5367a to ec9a385 Compare August 19, 2026 22:06
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 19, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 21, 2026
Convergent MEDIUM findings from the PR TrakHound#227 6-agent Ultrareview
(F-SEC-001, F-SEC-002, F-IMP-001, F-CR-001) around the new
RunVitepressBuild helper and MEDIUM/HIGH stale-doc-reference findings
(F-DOC-001..006) around the producer/consumer mode shift. Deferred
MEDIUM items (node preflight, dist-lock, bounded stdout, RunProcess
helper, predicate/failure-path coverage) tracked in TrakHound#238 with concrete
sketches so each lands as its own scoped PR.

RunVitepressBuild fixture-side:
  * VitepressBuildTimeoutMs = 20 min bounds the child; on expiry the
    process tree is killed, drained partials captured, and an
    InvalidOperationException surfaces the diagnostic rather than a
    wall-clock CI timeout that discards it.
  * "using" on the Process handle guarantees OS handle + pipe release
    on every path (previously leaked on drain/wait/exit-code throws;
    warm CI runners accumulated handles across reruns).
  * XML doc <exception> block updated to enumerate the timeout arm
    alongside the pre-existing missing-entry and non-zero-exit arms.

Stale doc sync (§1.0d-trigies-bis atomic bug-class):
  * tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs class summary,
    ServerReadyTimeoutMs summary, and OneTimeSetUp_Rebuilds_Dist
    remarks: replace "npm ci + npm run build" and the sentinel-only
    invariant with the new producer-always-rebuild + consumer-shard
    contract and the direct-vitepress-invocation rationale.
  * docs/development/docs-site.md: rewrite the end-to-end
    route-check section (lines 64, 72) to explain producer vs.
    consumer mode and why the fixture bypasses npm run build.
  * .github/workflows/dotnet.yml: expand the docs-prepare header
    comment to document the fixture's two-mode contract so the next
    maintainer editing the workflow does not re-encode the old
    sentinel-only invariant.

Verification (bluefin, verify/pr227 worktree):
  * dotnet build MTConnect.NET.sln -p:IntegrationCoverage=true
    -> 0 warnings, 0 errors, 5s.
  * dotnet test tests/MTConnect.NET-Docs-Tests --no-build
    -> 72/72 passed, 57s.
  * dotnet test MTConnect.NET.sln --no-build
    -> 5,033/5,033 passed across every test project.
  * node --max-old-space-size=8192 node_modules/vitepress/bin/vitepress.js build
    -> build complete in 20.41s.

Refs: TrakHound#238
@ottobolyos
ottobolyos force-pushed the fix/docs-tests-baseline-failures branch from ec9a385 to eab89cc Compare August 21, 2026 06:18
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 21, 2026
Convergent MEDIUM findings from the PR TrakHound#227 6-agent Ultrareview
(F-SEC-001, F-SEC-002, F-IMP-001, F-CR-001) around the new
RunVitepressBuild helper and MEDIUM/HIGH stale-doc-reference findings
(F-DOC-001..006) around the producer/consumer mode shift. Deferred
MEDIUM items (node preflight, dist-lock, bounded stdout, RunProcess
helper, predicate/failure-path coverage) tracked in TrakHound#238 with concrete
sketches so each lands as its own scoped PR.

RunVitepressBuild fixture-side:
  * VitepressBuildTimeoutMs = 20 min bounds the child; on expiry the
    process tree is killed, drained partials captured, and an
    InvalidOperationException surfaces the diagnostic rather than a
    wall-clock CI timeout that discards it.
  * "using" on the Process handle guarantees OS handle + pipe release
    on every path (previously leaked on drain/wait/exit-code throws;
    warm CI runners accumulated handles across reruns).
  * XML doc <exception> block updated to enumerate the timeout arm
    alongside the pre-existing missing-entry and non-zero-exit arms.

Stale doc sync (§1.0d-trigies-bis atomic bug-class):
  * tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs class summary,
    ServerReadyTimeoutMs summary, and OneTimeSetUp_Rebuilds_Dist
    remarks: replace "npm ci + npm run build" and the sentinel-only
    invariant with the new producer-always-rebuild + consumer-shard
    contract and the direct-vitepress-invocation rationale.
  * docs/development/docs-site.md: rewrite the end-to-end
    route-check section (lines 64, 72) to explain producer vs.
    consumer mode and why the fixture bypasses npm run build.
  * .github/workflows/dotnet.yml: expand the docs-prepare header
    comment to document the fixture's two-mode contract so the next
    maintainer editing the workflow does not re-encode the old
    sentinel-only invariant.

Verification (bluefin, verify/pr227 worktree):
  * dotnet build MTConnect.NET.sln -p:IntegrationCoverage=true
    -> 0 warnings, 0 errors, 5s.
  * dotnet test tests/MTConnect.NET-Docs-Tests --no-build
    -> 72/72 passed, 57s.
  * dotnet test MTConnect.NET.sln --no-build
    -> 5,033/5,033 passed across every test project.
  * node --max-old-space-size=8192 node_modules/vitepress/bin/vitepress.js build
    -> build complete in 20.41s.

Refs: TrakHound#238
@ottobolyos
ottobolyos force-pushed the fix/docs-tests-baseline-failures branch from eab89cc to 504a7a5 Compare August 21, 2026 08:34
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 21, 2026
Convergent MEDIUM findings from the PR TrakHound#227 6-agent Ultrareview
(F-SEC-001, F-SEC-002, F-IMP-001, F-CR-001) around the new
RunVitepressBuild helper and MEDIUM/HIGH stale-doc-reference findings
(F-DOC-001..006) around the producer/consumer mode shift. Deferred
MEDIUM items (node preflight, dist-lock, bounded stdout, RunProcess
helper, predicate/failure-path coverage) tracked in TrakHound#238 with concrete
sketches so each lands as its own scoped PR.

RunVitepressBuild fixture-side:
  * VitepressBuildTimeoutMs = 20 min bounds the child; on expiry the
    process tree is killed, drained partials captured, and an
    InvalidOperationException surfaces the diagnostic rather than a
    wall-clock CI timeout that discards it.
  * "using" on the Process handle guarantees OS handle + pipe release
    on every path (previously leaked on drain/wait/exit-code throws;
    warm CI runners accumulated handles across reruns).
  * XML doc <exception> block updated to enumerate the timeout arm
    alongside the pre-existing missing-entry and non-zero-exit arms.

Stale doc sync (atomic bug-class fix):
  * tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs class summary,
    ServerReadyTimeoutMs summary, and OneTimeSetUp_Rebuilds_Dist
    remarks: replace "npm ci + npm run build" and the sentinel-only
    invariant with the new producer-always-rebuild + consumer-shard
    contract and the direct-vitepress-invocation rationale.
  * docs/development/docs-site.md: rewrite the end-to-end
    route-check section (lines 64, 72) to explain producer vs.
    consumer mode and why the fixture bypasses npm run build.
  * .github/workflows/dotnet.yml: expand the docs-prepare header
    comment to document the fixture's two-mode contract so the next
    maintainer editing the workflow does not re-encode the old
    sentinel-only invariant.

Verification (bluefin, verify/pr227 worktree):
  * dotnet build MTConnect.NET.sln -p:IntegrationCoverage=true
    -> 0 warnings, 0 errors, 5s.
  * dotnet test tests/MTConnect.NET-Docs-Tests --no-build
    -> 72/72 passed, 57s.
  * dotnet test MTConnect.NET.sln --no-build
    -> 5,033/5,033 passed across every test project.
  * node --max-old-space-size=8192 node_modules/vitepress/bin/vitepress.js build
    -> build complete in 20.41s.

Refs: TrakHound#238
@ottobolyos
ottobolyos force-pushed the fix/docs-tests-baseline-failures branch from 504a7a5 to 30a9b5f Compare August 21, 2026 14:13
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 21, 2026
… source

The RouteCheckTests fixture previously honored a pre-existing
docs/.vitepress/dist/index.html as evidence that dist was current.
That was safe under the sharded CI matrix (ROUTE_SHARD_TOTAL > 1),
where the docs-prepare job is the docfx-owning producer and each
shard downloads its artefact — but not in producer mode. A warm
local checkout keeps a stale dist across sessions, and the fixture
would silently walk it: the landing-page test asserted against the
old rendered HTML (missing the https:// og:image URL that landed
in a later config.ts revision), and the producer-mode mtime
invariant fired because no rebuild took place.

Split the two modes explicitly: consumer shards keep the artefact
contract (skip rebuild when index.html is present); producer mode
always rebuilds via npm run build. This walks the current source
on every run, satisfies the mtime invariant unconditionally in
producer mode, and preserves the sharded artefact flow.

Claude-Session: https://claude.ai/code/session_0162RfaA55VT8NX6QfU7RUVo
…uched

Amends the earlier producer-mode-always-rebuilds fix so the fixture
no longer routes through npm run build. The `prebuild` hook wired
into docs/package.json runs docs/scripts/generate-api-ref.sh, which
`dotnet build -c Debug --no-incremental`s every library, agent,
adapter and module project. That sweep clobbers each project's
obj/project.assets.json down to a Debug-only net8.0 view and races
any in-flight multi-TFM Release build under
`dotnet test MTConnect.NET.sln -c Release`, tripping NETSDK1005 on
every non-net8.0 target MSBuild has not yet linked (net47, net461,
net472, net9.0, net10.0, …).

Invoke node node_modules/vitepress/bin/vitepress.js build directly
under docs/ instead. Walks the same source markdown, produces the
same dist/, keeps the producer-mode rebuild guarantee, and leaves
the obj/ tree untouched. The docs/api/ sub-tree stays as whatever
the last regen produced — the fixture never owned that regen (the
docs-prepare workflow and generate-api-ref.sh do).

Claude-Session: https://claude.ai/code/session_0162RfaA55VT8NX6QfU7RUVo
Convergent MEDIUM findings from the PR TrakHound#227 6-agent Ultrareview
(F-SEC-001, F-SEC-002, F-IMP-001, F-CR-001) around the new
RunVitepressBuild helper and MEDIUM/HIGH stale-doc-reference findings
(F-DOC-001..006) around the producer/consumer mode shift. Deferred
MEDIUM items (node preflight, dist-lock, bounded stdout, RunProcess
helper, predicate/failure-path coverage) tracked in TrakHound#238 with concrete
sketches so each lands as its own scoped PR.

RunVitepressBuild fixture-side:
  * VitepressBuildTimeoutMs = 20 min bounds the child; on expiry the
    process tree is killed, drained partials captured, and an
    InvalidOperationException surfaces the diagnostic rather than a
    wall-clock CI timeout that discards it.
  * "using" on the Process handle guarantees OS handle + pipe release
    on every path (previously leaked on drain/wait/exit-code throws;
    warm CI runners accumulated handles across reruns).
  * XML doc <exception> block updated to enumerate the timeout arm
    alongside the pre-existing missing-entry and non-zero-exit arms.

Stale doc sync (atomic bug-class fix):
  * tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs class summary,
    ServerReadyTimeoutMs summary, and OneTimeSetUp_Rebuilds_Dist
    remarks: replace "npm ci + npm run build" and the sentinel-only
    invariant with the new producer-always-rebuild + consumer-shard
    contract and the direct-vitepress-invocation rationale.
  * docs/development/docs-site.md: rewrite the end-to-end
    route-check section (lines 64, 72) to explain producer vs.
    consumer mode and why the fixture bypasses npm run build.
  * .github/workflows/dotnet.yml: expand the docs-prepare header
    comment to document the fixture's two-mode contract so the next
    maintainer editing the workflow does not re-encode the old
    sentinel-only invariant.

Verification (bluefin, verify/pr227 worktree):
  * dotnet build MTConnect.NET.sln -p:IntegrationCoverage=true
    -> 0 warnings, 0 errors, 5s.
  * dotnet test tests/MTConnect.NET-Docs-Tests --no-build
    -> 72/72 passed, 57s.
  * dotnet test MTConnect.NET.sln --no-build
    -> 5,033/5,033 passed across every test project.
  * node --max-old-space-size=8192 node_modules/vitepress/bin/vitepress.js build
    -> build complete in 20.41s.

Refs: TrakHound#238
@ottobolyos
ottobolyos force-pushed the fix/docs-tests-baseline-failures branch from 30a9b5f to 93df4ba Compare August 21, 2026 16:05
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 21, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 21, 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