Skip to content

Harden sidecar release contract#76

Merged
rupertgermann merged 18 commits into
mainfrom
codex/sidecar-open-issues-hardening
Jul 4, 2026
Merged

Harden sidecar release contract#76
rupertgermann merged 18 commits into
mainfrom
codex/sidecar-open-issues-hardening

Conversation

@rupertgermann

@rupertgermann rupertgermann commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add sidecar contract/package version metadata to /health and enforce schema/contract compatibility in UI readiness and the API connect route before capture can start.
  • Require sidecar frame schemaVersion and source, and make final pose-stream drain failures reject so partial uploads cannot silently proceed to finalize.
  • Return full sidecar health diagnostics from /session/start refusal responses and preserve app-side start-refusal status/details instead of flattening them to generic unreachable errors.
  • Reject sidecars that omit contractVersion, so compatibility failures are predictable rather than implicitly accepted.
  • Reject invalid ready health payloads when camera count is missing/zero, FPS is missing/zero, or a non-synthetic source reports no calibration id.
  • Reject recorded FreeMoCap JSON frames with missing, duplicated, or out-of-range keypoint indexes before the sidecar reports ready.
  • Prefer current FreeMoCap recorded output filenames such as skeleton_3d.npy and 3dData_numFrames_numTrackedPoints_spatialXYZ.npy when a recorded-output directory is passed.
  • Load known companion FreeMoCap reprojection-error NPY files from recorded-output directories and map finite per-frame means into quality.reprojection_error_mm.
  • Add installed rowing-tracker-sidecar startup coverage, a repeatable npm run test:sidecar gate that includes sidecar tracer regressions, a GitHub sidecar release workflow, and compatibility/smoke-test docs.
  • Add sidecar source-owned session lifecycle hooks so a future live FreeMoCap runtime can own start/stop session ids, calibration ids, and structured start/stop failures without changing the HTTP contract.
  • Add --freemocap-provider module:factory as a live-runtime provider boundary for injected FreeMoCap adapters, with fake-provider contract coverage and fail-closed diagnostics when no provider is configured.
  • Add --freemocap-recording to process an existing FreeMoCap recording folder through the optional package's headless processor, report initializing health while it runs, refuse starts with 409, then stream generated output_data or report processing errors through the ADR-0005 sidecar contract.
  • Add a default FreeMoCap package probe for --source freemocap without data/recording/provider, with explicit missing-package and installed-but-no-supported-live-API diagnostics instead of a generic unconfigured error.
  • Add a manual hardware-gated FreeMoCap sidecar smoke procedure plus an opt-in Playwright helper that records through the Rowing Tracker UI against a running sidecar and skips by default when hardware/provider is unavailable.
  • Add real Python synthetic-sidecar process coverage for the app-side FreemocapSidecarSource start/stream/upload/stop path.
  • Preserve recorded FreeMoCap-style per-frame reprojection_error_mm quality metadata when streaming ADR-0005 frames.
  • Preserve high recorded reprojection error as v2 frame quality flags and as a sidecar session-level high-reprojection-error marker.
  • Preserve active Rowing Tracker sidecar sessions when stop or finalize fails, so captured data is not deleted as an abandoned setup row and the user sees a clear error.
  • Clean up the browser pose worker even when final pose-stream drain upload fails, while still propagating the upload error.

Issue scope

Testing

  • python3 -m py_compile sidecar/src/rowing_tracker_sidecar/sources.py
  • npx tsc --noEmit
  • npm run test:sidecar (53/53)
  • npm run test (252/252)
  • npx tsx --test tests/freemocapSidecarSource.test.ts (10/10)
  • npx tsx --test tests/sidecarCliContract.test.ts (15/15)
  • npx tsx --test tests/sidecarPackageInstall.test.ts (1/1)
  • npx eslint src/lib/mocap/freemocapSidecarSource.ts src/lib/mocap/poseFrameStream.ts src/app/mocap/page.tsx tests/freemocapSidecarSource.test.ts tests/sidecarCliContract.test.ts
  • npx eslint tests/sidecarCliContract.test.ts
  • npx eslint tests/sidecarCliContract.test.ts tests/sidecarPackageInstall.test.ts
  • npx eslint src/app/api/mocap/sessions/[id]/sidecar/connect/route.ts src/lib/mocap/sidecarClient.ts tests/sidecarCliContract.test.ts tests/sidecarPoseSource.test.ts
  • npx eslint tests/e2e/sidecar-hardware-smoke.spec.ts
  • npx eslint tests/e2e/mocap-capture.spec.ts
  • PLAYWRIGHT_SKIP_SERVER=1 npm run test:e2e:sidecar-smoke (1 skipped by default)
  • ROWING_TRACKER_SIDECAR_HARDWARE_SMOKE=1 ROWING_TRACKER_SIDECAR_HARDWARE_ALLOW_SYNTHETIC=1 PORT=3114 npm run test:e2e:sidecar-smoke (1/1 against the synthetic sidecar path)
  • PORT=3113 npm run test:e2e -- tests/e2e/mocap-capture.spec.ts (4/4, including sidecar high-reprojection finalize flags)
  • git diff --check
  • npm run build passes with the existing Turbopack/NFT warning in next.config.ts -> src/app/api/achievements/image/save/route.ts
  • npm run lint still fails on pre-existing unrelated repo-wide lint debt (118 errors, 161 warnings), not in the files touched by this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d01fd558d0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +55 to +57
if (
health.contractVersion !== undefined &&
health.contractVersion !== SIDECAR_CONTRACT_VERSION

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require contractVersion in the readiness gate

This check only rejects a mismatched contractVersion when the field is present. If a user is still running an older or third-party sidecar that reports status: "ready" and schemaVersion: 2 but omits contractVersion, sidecarReadinessError returns null, so both the UI readiness check and the connect route accept an unversioned HTTP/WebSocket contract even though this change is intended to require contractVersion = 1. Treating undefined as incompatible would make the new release gate enforce the contract it advertises.

Useful? React with 👍 / 👎.

});
if (final) {
await this.uploadInflight;
if (this.uploadError) throw this.uploadError;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve source cleanup when final drains fail

When a final pose upload fails, this new throw propagates out of BrowserPoseSource.stop() at its await this.drain() call before that method posts close, terminates the worker, clears the worker reference, or reports a stopped/error status. In the upload-failure path this leaves the MediaPipe worker and its listeners alive until page unload, and retries of stop() immediately throw the stored uploadError again. The stop methods need to perform their resource cleanup in a finally before rethrowing the drain failure.

Useful? React with 👍 / 👎.

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