Skip to content

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

Draft
ottobolyos wants to merge 7 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 7 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 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.

Also folds in a fix for the master-side docs-prepare CI job (and local bash docs/scripts/generate-api-ref.sh), which failed with 120 CS0518 errors plus 3,943 InvalidAssemblyReference warnings when docfx metadata loaded the 43 pre-built Debug/net8.0 DLLs. The docs-prepare job was red on every branch's own head and every downstream cascade branch, blocking the whole train; folding the fix in here unblocks it.

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.

docs-prepare CI job CS0518

docs/.docfx/docfx.json's metadata[].src.files listed 43 pre-built .dll artefacts. Docfx's DLL-loader path cannot resolve corelib references (System.Runtime / mscorlib) because the DLL tree carries no matching reference assemblies alongside; docfx then falls back to synthesising a stub compilation that has no System.* imports, which trips CS0518 (Predefined type 'System.Object' / 'System.String' / 'System.Void' is not defined or imported) on every emitted AssemblyInfo.cs. Reproducer on bare upstream/master @ dc7b100c: Build failed. 3943 warning(s) 120 error(s).

Fix

Two coordinated changes, both in-scope of the docs-tests baseline:

RouteCheckTests.cs producer/consumer split

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.

docs/.docfx/docfx.json DLL → csproj rewrite

Switch the metadata source from DLLs to .csproj project files so docfx uses MSBuild workspace loading. MSBuild resolves references via the project's <TargetFramework> and the .NET SDK's reference-assembly packs, exactly as dotnet build does. This is the intended docfx analysis path for library documentation; the DLL path is only meant for third-party assemblies without source.

Composite of the two sub-fixes both required for a Linux-CI-runnable metadata run:

  1. Metadata src.files now lists the 43 .csproj paths that mirror the previous DLL list one-for-one (including the templates/mtconnect.net-agent/content/MTConnect.NET-Embedded-Agent/Agent.csproj naming override).
  2. metadata[].properties: { TargetFramework: "net8.0", Configuration: "Debug" } pins the msbuild workspace to the single modern TFM. Without this, multi-TFM projects (all libraries + agent + adapter + examples target net461/462/47/47.1/47.2/48/8.0/9.0) trigger a per-TFM msbuild load attempt; the .NET Framework 4.x TFMs then fail on Linux with "reference assemblies for .NETFramework,Version=v4.6.1 were not found" because the developer packs are Windows-only. Pinning to net8.0 keeps the CI docs-prepare job (which installs only 8.0.x + 9.0.x SDKs) buildable and mirrors the analysis target the docfx metadata run was always intended for.

Verified on bluefin against docfx 2.78.5 + .NET 10.0.302: bash docs/scripts/generate-api-ref.sh now completes cleanly with 0 errors, 2 pre-existing DeviceFinder XML-doc warnings (unchanged), and 2,242 markdown pages generated.

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
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 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
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 22, 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 93df4ba to f123d1b Compare August 22, 2026 00:52
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 23, 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 f123d1b to 3911623 Compare August 23, 2026 02:59
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 23, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 25, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 25, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 25, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 25, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 25, 2026
@ottobolyos
ottobolyos marked this pull request as ready for review August 25, 2026 07:16
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 25, 2026
@ottobolyos
ottobolyos marked this pull request as draft August 25, 2026 12:06
@ottobolyos
ottobolyos marked this pull request as ready for review August 25, 2026 12:10
@ottobolyos
ottobolyos marked this pull request as draft August 25, 2026 21:41
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 26, 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 2933bca to 8b94ab0 Compare August 26, 2026 04:28
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 26, 2026
…ation/up-to-pr-227

Cascade rebuild after upstream/master advanced to 0ddc3c6 (PR TrakHound#220 merge).
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 26, 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 8b94ab0 to 7d9482d Compare August 26, 2026 16:23
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 26, 2026
…ation/up-to-pr-227 on top of integration/up-to-pr-226
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Aug 26, 2026
…ation/up-to-pr-227 on top of previous cascade tip
… 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
…iagnostics

Ultrareview cycle-1 convergent finding (code-review F-CR-001, simplification
F-SIMP-001, improvement F-IMP-001): RunNpm leaked its Process handle and had
no timeout bound, the exact defect class this PR's own fix commit resolved in
RunVitepressBuild. Extract both into a shared RunProcess helper so `npm ci`
gets the same dispose-on-every-path + bounded-wait-with-kill hardening.

Also: sanitize control characters out of captured child-process output before
it lands in exception messages (security-audit F-SEC-001, LOW); correct the
ServerReadyTimeoutMs doc comment, which claimed the 60 s bind deadline covers
npm/build time that has, in fact, already completed by the time the preview
server starts (code-review F-CR-002); cross-reference the shared prebuild-hook
obj/ race from EnvironmentVariables_Page_Is_In_Sync_With_Source back to
RunVitepressBuild's remarks (improvement F-IMP-002).

72/72 MTConnect.NET-Docs-Tests pass; build 0/0; format clean.
Bluefin's full-solution `dotnet test MTConnect.NET.sln` run on
integration/up-to-pr-227 surfaced EnvironmentVariables_Page_Is_In_Sync_With_Source
as flaky (fails at solution scope, passes when the Docs-Tests project
runs alone). Root cause: EnvVarInventory.Collect walks repoRoot
recursively for *.cs files and only excludes bin/obj/node_modules/.git/
.vitepress by directory name — it does not exclude .claude/worktrees/,
the repo's convention for nested agent-session git worktrees. On a host
with sibling worktrees checked out under .claude/worktrees/ (as bluefin
had at test time), the scan picks up GetEnvironmentVariable call sites
from whatever branch each nested worktree happens to have checked out,
producing a false "out of sync" verdict unrelated to the current
branch's actual source tree.

Excludes .claude the same way bin/obj/.git already are. Verified
locally (72/72 Docs-Tests pass, this worktree has no nested .claude
pollution so this is a defensive/root-cause fix pending bluefin
re-verification against the actual polluted host state).
…ve CS0518

The docs-prepare CI job and local `bash docs/scripts/generate-api-ref.sh`
runs failed with 120 CS0518 errors (`Predefined type 'System.Object' /
'System.String' / 'System.Void' is not defined or imported`) plus 3,943
`InvalidAssemblyReference` warnings when docfx metadata loaded the 43
pre-built Debug/net8.0 DLLs listed in `docs/.docfx/docfx.json`
`metadata[].src.files`. Root cause: docfx's DLL-loader path cannot
resolve corelib references (System.Runtime / mscorlib) because the DLL
tree carries no matching reference assemblies alongside; docfx then
falls back to synthesising a stub compilation that has no `System.*`
imports, which trips CS0518 on every emitted AssemblyInfo.cs.

Fix — switch the metadata source from DLLs to `.csproj` project files
so docfx uses MSBuild workspace loading. MSBuild resolves references
via the project's `<TargetFramework>` and the .NET SDK's reference-
assembly packs, exactly as `dotnet build` does. This is the intended
docfx analysis path for library documentation; the DLL path is only
meant for third-party assemblies without source.

Composite of the two sub-fixes both required for a Linux-CI-runnable
metadata run:

  1. Metadata src `files:` now lists the 43 `.csproj` paths that
     mirror the previous DLL list one-for-one (including the
     `templates/mtconnect.net-agent/content/MTConnect.NET-Embedded-
     Agent/Agent.csproj` naming override).
  2. `metadata[].properties`: `{ TargetFramework: "net8.0",
     Configuration: "Debug" }` pins the msbuild workspace to the
     single modern TFM. Without this, multi-TFM projects (all
     libraries + agent + adapter + examples target
     net461/462/47/47.1/47.2/48/8.0/9.0) trigger a per-TFM msbuild
     load attempt; the .NET Framework 4.x TFMs then fail on Linux
     with "reference assemblies for .NETFramework,Version=v4.6.1
     were not found" because the developer packs are Windows-only.
     Pinning to net8.0 keeps the CI docs-prepare job (which installs
     only 8.0.x + 9.0.x SDKs) buildable and mirrors the analysis
     target the docfx metadata run was always intended for.

Verified on bluefin against `docfx 2.78.5` + `.NET 10.0.302`:
`bash docs/scripts/generate-api-ref.sh` now completes cleanly with 0
errors, 2 pre-existing DeviceFinder XML-doc warnings (unchanged), and
2,242 markdown pages generated. Fix is master-side pre-existing bug —
not scoped originally to this PR, but the docs-prepare CI job was also
red on this branch's own head and every downstream cascade branch, so
folding the fix in here unblocks the whole train.
@ottobolyos
ottobolyos force-pushed the fix/docs-tests-baseline-failures branch from d4533c7 to 3ad6862 Compare August 27, 2026 02:49
Per §1.0d-trigies-vicies-octies (AmE mandatory across every PR-associated surface),
three PR-introduced BrE tokens are converted:

- docs/development/docs-site.md: 'honours' -> 'honors' in the dist sentinel note
- tests/MTConnect.NET-Docs-Tests/DocsReferenceGenerationTests.cs L67: 'behaviour' -> 'behavior' in test docstring
- tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs L160: 'Honouring' -> 'Honoring' in policy comment

Comment / docstring / prose only; no behavior change.
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