Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
215 commits
Select commit Hold shift + click to select a range
c3e0229
feat(biometrics): establish versioned calibration contracts and profi…
ng Oct 12, 2026
baf2b10
fix(lint): use compatible scope analysis for JavaScript and module co…
ng Oct 12, 2026
5c4f971
refactor(biometrics): share parameterized numerical processing with r…
ng Oct 12, 2026
b115e9d
feat(biometrics): normalize versioned references and compare stored r…
ng Oct 12, 2026
68e3815
feat(biometrics): retain processed datasets with whole-night splits
ng Oct 12, 2026
e261603
feat(biometrics): persist fenced backtest jobs and holdout exposure
ng Oct 12, 2026
81406c8
feat(biometrics): execute retained stage backtests in fenced worker p…
ng Oct 12, 2026
d8a6a8f
feat(biometrics): expose calibration profiles datasets and durable ba…
ng Oct 12, 2026
cdf2003
feat(biometrics): coordinate atomic activation intent and runtime del…
ng Oct 12, 2026
fb60aca
feat(biometrics): validate Python runtime projections and boundary se…
ng Oct 12, 2026
9e098d6
feat(biometrics): adopt live profiles with runtime receipts and readi…
ng Oct 12, 2026
4e3464f
feat(biometrics): coordinate stage adoption and migrate legacy settin…
ng Oct 12, 2026
85c3a96
feat(biometrics): audit runtime recovery and expose activation health…
ng Oct 12, 2026
18efba2
feat(biometrics): expose manual activation with cross-runtime verific…
ng Oct 12, 2026
6cf1bad
feat(biometrics): retain large backtest reports as fenced artifacts
ng Oct 12, 2026
9d55bc7
fix(biometrics): serialize report publication and manage worker lifec…
ng Oct 12, 2026
dafef3c
fix(biometrics): interrupt replay when resource admission declines
ng Oct 12, 2026
8f183ad
docs(biometrics): record three-config replay load measurements
ng Oct 12, 2026
a1c12b8
feat(biometrics): evaluate replay admission from fresh resource evidence
ng Oct 12, 2026
fe2c354
feat(biometrics): release report payloads while retaining evaluation …
ng Oct 12, 2026
ef5b798
feat(biometrics): compare stage agreement on common and own support
ng Oct 12, 2026
a5f04af
feat(biometrics): report whole-night effects alongside pooled agreement
ng Oct 12, 2026
d106c79
feat(biometrics): pin reproducible paired-night uncertainty in experi…
ng Oct 12, 2026
5d41626
feat(biometrics): score all stage configurations on shared support
ng Oct 12, 2026
453c855
feat(mcp): expose shared biometric calibration workflows
ng Oct 12, 2026
39f24eb
feat(biometrics): expose hash-pinned paginated replay traces
ng Oct 12, 2026
2e17483
feat(biometrics): add calibration overview and activation history
ng Oct 12, 2026
808487c
feat(biometrics): preview and import reference records in calibration UI
ng Oct 12, 2026
36b9db7
feat(biometrics): browse and delete retained reference imports
ng Oct 12, 2026
57ec9b7
feat(biometrics): support reference corrections and localized time spans
ng Oct 12, 2026
30056bf
feat(biometrics): edit and save numerical profile candidates
ng Oct 12, 2026
00d9193
feat(biometrics): browse and branch saved profile revisions
ng Oct 12, 2026
dd96e68
feat(biometrics): compare candidate values and override intent
ng Oct 12, 2026
78d022c
feat(biometrics): apply reviewed manual profiles and track receipts
ng Oct 12, 2026
84700c2
feat(biometrics): review and request profile reset or rollback
ng Oct 12, 2026
79c0692
fix(biometrics): preserve malformed legacy data during explicit reset
ng Oct 12, 2026
2a1bbbd
fix(biometrics): allow reviewed reset from incompatible pinned profiles
ng Oct 12, 2026
0a49eb1
feat(biometrics): edit sleep-stage rules in profile candidates
ng Oct 12, 2026
5ceb5a2
feat(biometrics): inspect recorded nights for backtest datasets
ng Oct 12, 2026
9db48a1
feat(biometrics): freeze selected nights and reference revisions from…
ng Oct 12, 2026
8bf3604
feat(biometrics): inspect and release retained evaluation datasets
ng Oct 12, 2026
05e26cd
feat(biometrics): configure immutable evaluation protocols in the web UI
ng Oct 12, 2026
3e6343f
feat(biometrics): submit selected candidates to pinned backtest exper…
ng Oct 12, 2026
6bf11f7
feat(biometrics): monitor and cancel durable backtest jobs
ng Oct 12, 2026
4065516
feat(biometrics): review backtest evidence and release reports
ng Oct 12, 2026
0bcc7e3
feat(biometrics): show per-night numerical errors and coverage
ng Oct 12, 2026
a073f9b
feat(biometrics): inspect nightly stage coverage and confusion
ng Oct 12, 2026
3109d33
feat(biometrics): inspect paged replay stage traces
ng Oct 12, 2026
0511f39
feat(biometrics): share profile validation with browser demo
ng Oct 12, 2026
205f99f
feat(biometrics): simulate profile adoption and recovery in demo
ng Oct 12, 2026
455aac0
feat(biometrics): support reference imports in browser demo
ng Oct 12, 2026
88b4a83
feat(biometrics): freeze synthetic datasets in browser demo
ng Oct 12, 2026
20e33a8
feat(biometrics): share evaluation with a cancellable demo worker
ng Oct 12, 2026
fa8f9ad
feat(biometrics): complete demo backtest job lifecycle
ng Oct 12, 2026
96597f9
fix(biometrics): normalize absent candidate parents before hashing
ng Oct 12, 2026
8cb0e44
fix(biometrics): distinguish report section identities
ng Oct 12, 2026
61cac76
fix(biometrics): disable acknowledgement before profile review
ng Oct 12, 2026
2b05674
fix(biometrics): unify demo stage settings with profile adoption
ng Oct 12, 2026
8361063
test(biometrics): measure coordinator and artifact costs on pod
ng Oct 12, 2026
0e5c87c
fix(biometrics): isolate retained evaluation work from app event loop
ng Oct 12, 2026
29253f0
test(biometrics): measure isolated coordinator on pod
ng Oct 12, 2026
d0fb37f
perf(biometrics): summarize reports before coordinator transport
ng Oct 12, 2026
d09ca83
refactor(biometrics): share pump and presence gates with event clocks
ng Oct 12, 2026
c049cef
refactor(biometrics): share stateful processing and output arbitration
ng Oct 12, 2026
ab86b43
feat(biometrics): add bounded event-time waveform replay driver
ng Oct 12, 2026
2985265
feat(biometrics): retain verified bounded RAW artifact copies
ng Oct 12, 2026
ebe07f6
feat(biometrics): decode and inventory retained RAW records safely
ng Oct 12, 2026
b8252f3
feat(biometrics): inspect RAW sources before identity-pinned retention
ng Oct 12, 2026
1630321
feat(biometrics): persist inspected raw source identities
ng Oct 12, 2026
9253c74
feat(biometrics): release retained raw references safely
ng Oct 12, 2026
da57aea
feat(biometrics): replay retained waveform inputs
ng Oct 12, 2026
4631d20
feat(biometrics): add retained waveform worker transport
ng Oct 12, 2026
9d5bb63
feat(biometrics): publish immutable waveform datasets
ng Oct 12, 2026
1d888d9
feat(biometrics): inspect raw sources through isolated coordinator
ng Oct 12, 2026
2c8eab1
feat(biometrics): resolve waveform selections with frozen occupancy c…
ng Oct 12, 2026
0014e65
feat(biometrics): publish waveform datasets through coordinator
ng Oct 12, 2026
8022c49
feat(biometrics): pin waveform worker source and runtime identities
ng Oct 12, 2026
0c1b220
feat(biometrics): execute leased waveform replay jobs
ng Oct 12, 2026
fe25e71
feat(biometrics): submit waveform jobs with runtime baselines
ng Oct 13, 2026
ff9f92b
fix(biometrics): enforce replay limits on Yocto Python
ng Oct 13, 2026
dfb969d
fix(biometrics): reserve report reads during replay
ng Oct 13, 2026
7eb86c3
feat(biometrics): configure bounded waveform execution budgets
ng Oct 13, 2026
cabb6cd
feat(biometrics): expose retained waveform discovery and publication
ng Oct 13, 2026
7190a05
feat(biometrics): select retained waveforms in calibration UI
ng Oct 13, 2026
029306a
feat(biometrics): compare numeric replay on shared observations
ng Oct 13, 2026
ad3e489
feat(biometrics): display waveform comparison reports
ng Oct 13, 2026
ec012a1
feat(biometrics): summarize numeric errors across paired nights
ng Oct 13, 2026
233799e
feat(biometrics): pin numeric objective thresholds and evidence gates
ng Oct 13, 2026
280b2b7
fix(biometrics): collect aged unpublished waveform copies
ng Oct 13, 2026
e1ab394
fix(biometrics): run retained waveform cleanup through coordinator
ng Oct 13, 2026
2ff9bf5
fix(biometrics): verify benchmark report before declaring success
ng Oct 13, 2026
cf52bda
feat(biometrics): start replay host under deployment admission policy
ng Oct 13, 2026
df98b54
feat(biometrics): page exact numeric replay traces by dataset side
ng Oct 13, 2026
90910cb
feat(biometrics): inspect recomputed numeric traces in reports
ng Oct 13, 2026
95ffc6d
feat(biometrics): page frozen reference records for report overlays
ng Oct 13, 2026
fd53bc6
feat(biometrics): overlay frozen references on numeric replay traces
ng Oct 13, 2026
4cd7599
feat(biometrics): retain measured replay process costs
ng Oct 13, 2026
b024ae8
feat(biometrics): retain supervised replay health observations
ng Oct 13, 2026
578932d
feat(biometrics): observe replay process group memory
ng Oct 13, 2026
d599bdc
feat(biometrics): finalize objectives with supervised operational evi…
ng Oct 13, 2026
07e5d13
feat(biometrics): assess reference-defined numeric subgroups
ng Oct 13, 2026
3ed475c
feat(biometrics): assess held-out stage objectives
ng Oct 13, 2026
6090f81
test(biometrics): cover stage objective evidence boundaries
ng Oct 13, 2026
7110f09
feat(biometrics): verify exact evaluation evidence for promotion
ng Oct 13, 2026
fc97ca3
feat(biometrics): commit validated activation with retained evidence
ng Oct 13, 2026
0c3c47d
feat: integrate stage editor and Watch imports with calibration profiles
ng Oct 13, 2026
1e32ec1
feat(biometrics): validate every changed numeric metric independently
ng Oct 13, 2026
44561f0
feat(biometrics): expose evidence-pinned activation through API and MCP
ng Oct 13, 2026
f5691c5
feat(biometrics): review and apply evaluated profiles from reports
ng Oct 13, 2026
d23e146
fix(biometrics): bind promotion qualification to the full protocol
ng Oct 13, 2026
9121307
fix(biometrics): require explicit replay context qualification
ng Oct 13, 2026
272a09b
feat(biometrics): share installed build qualification authority
ng Oct 13, 2026
5fcd0e0
feat(biometrics): derive report status from qualified evidence
ng Oct 13, 2026
718e8b0
test(biometrics): verify qualified report publication
ng Oct 13, 2026
76fba6a
feat(biometrics): validate inert algorithm package manifests
ng Oct 13, 2026
4c39b66
feat(biometrics): validate algorithm frame exchanges
ng Oct 13, 2026
00c9d6e
feat(biometrics): run package experiments in restricted Docker contai…
ng Oct 13, 2026
9b7b091
feat(biometrics): bind experiment inputs and check replay determinism
ng Oct 13, 2026
118064c
feat(biometrics): verify locked experiment runtime environments
ng Oct 13, 2026
8fc3b39
feat(biometrics): expose bounded package experiment CLI
ng Oct 13, 2026
b4a59e7
feat(biometrics): record host-observed experiment resource usage
ng Oct 13, 2026
ea8dea9
feat(biometrics): evaluate isolated artifact and noise fixtures
ng Oct 13, 2026
dc0cf8b
feat(biometrics): gate fixture results on measured resource evidence
ng Oct 13, 2026
17a5ade
feat(biometrics): compare typed package metrics with reference readings
ng Oct 13, 2026
ec3d104
feat(biometrics): persist package study trials and holdout exposure
ng Oct 13, 2026
db0cd79
feat(biometrics): execute managed package trials with fenced reports
ng Oct 13, 2026
1cf7752
feat(biometrics): expose durable managed trial file workflow
ng Oct 13, 2026
2409282
fix(biometrics): recover overdue external experiment containers
ng Oct 13, 2026
bc0a349
fix(biometrics): verify complete retained experiment evidence
ng Oct 13, 2026
b1e5319
feat(biometrics): assess frozen package accuracy criteria
ng Oct 13, 2026
b666859
feat(biometrics): orchestrate candidate package preflight checks
ng Oct 13, 2026
5381abb
feat(biometrics): bind preflight evidence to managed trials
ng Oct 13, 2026
2873ebe
feat(biometrics): combine managed package qualification evidence
ng Oct 13, 2026
3e57897
feat(biometrics): add bounded prospective shadow storage
ng Oct 13, 2026
6e62724
feat(biometrics): report prospective shadow health and agreement
ng Oct 13, 2026
3f19e62
feat(biometrics): add isolated shipped-core shadow adapter
ng Oct 13, 2026
765670c
feat(biometrics): expose bounded shadow worker transport
ng Oct 13, 2026
8f3d47f
feat(biometrics): validate and persist shadow worker readings
ng Oct 13, 2026
c0bdf5a
feat(biometrics): retain unmatched prospective observations
ng Oct 13, 2026
ce0bcd9
chore: merge dev sleep-stage MCP tools into calibration
ng Oct 13, 2026
daceeed
feat(biometrics): enforce kernel budgets before shadow startup
ng Oct 13, 2026
8a35dd3
feat(biometrics): supervise bounded shadow worker processes
ng Oct 13, 2026
7bdafa7
feat(biometrics): coordinate fenced shadow attempt lifecycle
ng Oct 13, 2026
1e6bda0
feat(biometrics): capture bounded prospective sensor frames
ng Oct 13, 2026
9ec8943
feat(biometrics): capture active readings with verified provenance
ng Oct 13, 2026
7715ce7
feat(biometrics): compose live sources with final drain polling
ng Oct 13, 2026
6be09a5
feat(biometrics): capture committed shadow processing context
ng Oct 13, 2026
1fc5103
feat(biometrics): freeze shadow execution and source policies
ng Oct 13, 2026
9641d42
feat(biometrics): apply saved policy to live source construction
ng Oct 13, 2026
c24c1d5
feat(biometrics): require installed shadow qualification for admission
ng Oct 13, 2026
2620d61
feat(biometrics): verify both live runtime bindings for shadow admission
ng Oct 13, 2026
b7bc6c5
feat(biometrics): persist shadow completion resource receipts
ng Oct 13, 2026
535ade8
feat(biometrics): sample live host health for shadow admission
ng Oct 13, 2026
49795df
feat(biometrics): fingerprint the shipped shadow Python worker
ng Oct 13, 2026
1ceaef5
feat(biometrics): compose owned shadow attempt host lifecycle
ng Oct 13, 2026
6a055ca
feat(biometrics): resolve stored profiles for shadow attempts
ng Oct 13, 2026
b60e0b7
feat(biometrics): package and fingerprint the shadow host
ng Oct 13, 2026
3e36e3b
feat(biometrics): load shadow host with verified artifact identity
ng Oct 13, 2026
3857f37
feat(biometrics): guard shadow Python installation changes
ng Oct 13, 2026
26d32d5
feat(biometrics): bind shadow admission to observed hardware
ng Oct 13, 2026
b780f87
feat(biometrics): execute shadow attempts through the installed package
ng Oct 13, 2026
04c79c5
fix(biometrics): terminate shadow workers when their host dies
ng Oct 13, 2026
f587fbf
fix(biometrics): hold a kernel execution lock for shadow workers
ng Oct 13, 2026
ae2fe8c
fix(biometrics): enforce shadow wall time inside the worker
ng Oct 13, 2026
bb5398d
feat(biometrics): reconcile expired shadow run authority
ng Oct 13, 2026
e19c2b7
feat(biometrics): own shadow service cancellation and recovery
ng Oct 13, 2026
fd63151
feat(biometrics): schedule shadow plans with saved candidate identity
ng Oct 13, 2026
a4ab111
feat(biometrics): start configured shadow scheduling on pod boot
ng Oct 13, 2026
9e3ce11
feat(biometrics): expose shadow inspection and cancellation APIs
ng Oct 13, 2026
76f915f
feat(biometrics): expose bounded prospective shadow reports
ng Oct 13, 2026
ef00b79
feat(biometrics): admit and schedule prospective shadow plans
ng Oct 13, 2026
82e66fe
feat(biometrics): show prospective observations in calibration UI
ng Oct 13, 2026
612d37f
feat(biometrics): prepare shadow plans from deployment templates
ng Oct 13, 2026
3c2a755
feat(biometrics): prepare and schedule shadow runs from calibration UI
ng Oct 13, 2026
3b47173
feat(biometrics): compare prospective observations with reference rec…
ng Oct 13, 2026
493f352
fix(biometrics): recover shadow scheduling requests after reload
ng Oct 13, 2026
5c84fe3
fix(biometrics): serialize waveform replay and shadow workers
ng Oct 13, 2026
554d9f0
fix(biometrics): quarantine shadow execution after uncertain cleanup
ng Oct 13, 2026
b1cd8ad
fix(biometrics): verify shadow process group disappearance
ng Oct 13, 2026
03fca99
chore(biometrics): refresh calibration catalogs and demo build identity
ng Oct 13, 2026
f2f56f4
fix(startup): exclude Node lifecycle imports from Edge instrumentation
ng Oct 13, 2026
d8334bf
fix(build): exclude pod runtime data from standalone tracing
ng Oct 13, 2026
825c88d
feat(i18n): complete Spanish calibration and observation messages
ng Oct 13, 2026
06721fc
fix(biometrics): reconcile manifest and integration contracts
ng Oct 13, 2026
20aaac5
feat(biometrics): implement simulated shadow observation demo
ng Oct 13, 2026
bdb2958
feat(biometrics): retain partial shadow resource evidence
ng Oct 13, 2026
4453c0a
feat(biometrics): share background capacity across replay and shadow
ng Oct 13, 2026
5921cbd
docs(biometrics): record current-build pod replay smoke
ng Oct 13, 2026
5e43c00
fix(biometrics): keep experiment staging parent private
ng Oct 13, 2026
e29cb71
fix(biometrics): finalize trials when failure reports cannot publish
ng Oct 13, 2026
a8942dc
feat(biometrics): bound managed experiment workspace storage
ng Oct 13, 2026
b6a4f48
feat(biometrics): recover abandoned owned experiment staging
ng Oct 13, 2026
2806647
test(biometrics): exercise candidate flows in isolated browser fixtures
ng Oct 13, 2026
db52f1d
test(biometrics): verify calibration replay and rollback in browser
ng Oct 13, 2026
5d5de9b
fix(biometrics): retain original vitals provenance in API and CSV
ng Oct 13, 2026
216dab4
fix(biometrics): separate incompatible vitals baselines and chart runs
ng Oct 13, 2026
a85dff7
feat(mcp): guide isolated biometric algorithm development
ng Oct 13, 2026
b1e05db
fix(deploy): omit workstation Python environments from archives
ng Oct 13, 2026
4ee74c2
test(biometrics): add target-pod shadow resource harness
ng Oct 13, 2026
ed97727
docs(biometrics): map acceptance evidence and remaining release checks
ng Oct 13, 2026
4779e2e
docs(biometrics): clarify current shadow service integration
ng Oct 13, 2026
8c84429
docs(biometrics): record completed full-night pod replay measurements
ng Oct 13, 2026
d25656c
docs(biometrics): retain target-pod shadow supervisor receipt
ng Oct 13, 2026
d5d6469
docs(biometrics): record measured pod replay admission policy
ng Oct 13, 2026
a41e759
fix(biometrics): detect the runtime OS for replay admission
ng Oct 13, 2026
2ab2281
fix(biometrics): preserve Linux health probes in cross-platform builds
ng Oct 13, 2026
fc61e0e
docs(biometrics): keep private reference inventory out of release evi…
ng Oct 13, 2026
1ab0978
docs(biometrics): record deployed build and healthy replay admission
ng Oct 13, 2026
ef48519
fix(ci): use locked numerical dependencies for common Python tests
ng Oct 13, 2026
b4b6fd1
test(biometrics): accept procfs disappearance after parent-death cleanup
ng Oct 13, 2026
4c3a6a6
fix(biometrics): handle shadow RSS disappearance during clean exit
ng Oct 13, 2026
429c548
test(biometrics): cover coordinator admission and host resource owner…
ng Oct 13, 2026
cb39444
docs(biometrics): record live shadow source and isolated sink verific…
ng Oct 13, 2026
8909f30
docs(biometrics): refresh deployed build and qualification readiness
ng Oct 13, 2026
7b9ac5d
test(biometrics): exercise discovery boundaries and retained report l…
ng Oct 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ jobs:
mutate: "src/streaming/**/*.ts,!src/**/tests/**,!src/**/*.test.ts,!src/**/*.test.tsx,!src/**/*.d.ts"
- name: hooks
mutate: "src/hooks/**/*.ts,src/hooks/**/*.tsx,!src/**/tests/**,!src/**/*.test.ts,!src/**/*.test.tsx,!src/**/*.d.ts"
- name: biometrics
mutate: "src/biometrics/**/*.ts,!src/**/tests/**,!src/**/*.test.ts,!src/**/*.test.tsx,!src/**/*.d.ts"
- name: db
mutate: "src/db/**/*.ts,!src/**/tests/**,!src/**/*.test.ts,!src/**/*.test.tsx,!src/**/*.d.ts"
name: mutate (${{ matrix.shard.name }})
Expand Down
19 changes: 14 additions & 5 deletions .github/workflows/python-modules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
branches: [dev]
paths:
- 'modules/**/*.py'
- 'modules/common/biometrics/*.json'
- 'modules/piezo-processor/fixtures/*.json'
- 'modules/**/pyproject.toml'
- 'modules/**/uv.lock'
- 'modules/.coveragerc'
Expand All @@ -14,6 +16,8 @@ on:
types: [opened, synchronize, reopened]
paths:
- 'modules/**/*.py'
- 'modules/common/biometrics/*.json'
- 'modules/piezo-processor/fixtures/*.json'
- 'modules/**/pyproject.toml'
- 'modules/**/uv.lock'
- 'modules/.coveragerc'
Expand Down Expand Up @@ -50,9 +54,9 @@ jobs:
workdir: modules
target: common
cov_source: common
# cbor2 is needed to decode the real NATS capture fixtures through
# NatsFollower's decode path (test_nats_follower).
deps: "cbor2"
# Build-identity tests inspect installed NumPy/SciPy metadata;
# use the piezo module's locked environment, including cbor2.
deps: ""
- name: cover-buttons
workdir: modules/cover-buttons
target: .
Expand All @@ -72,6 +76,7 @@ jobs:
workdir: modules/piezo-processor
target: .
cov_source: .
cov_extra: "--cov=../common/biometrics"
deps: "numpy scipy"
- name: sleep-detector
workdir: modules/sleep-detector
Expand All @@ -95,7 +100,7 @@ jobs:
with:
# Pod runtime is pinned to 3.10 by every module's pyproject
# (`requires-python = ">=3.9,<3.11"`). Match it in CI.
python-version: '3.14'
python-version: '3.10'

- name: Install uv
uses: astral-sh/setup-uv@v4
Expand All @@ -105,7 +110,10 @@ jobs:
- name: Install test deps
working-directory: ${{ matrix.module.workdir }}
run: |
if [ -f uv.lock ]; then
if [ "${{ matrix.module.name }}" = "common" ]; then
# Keep common's cwd/import layout while using the numerical lock.
UV_PROJECT_ENVIRONMENT="$PWD/.venv" uv sync --project piezo-processor --locked --no-install-project
elif [ -f uv.lock ]; then
uv sync --locked --no-install-project
else
uv venv .venv
Expand All @@ -125,6 +133,7 @@ jobs:
pytest ${{ matrix.module.target }} \
--cov-config=${{ github.workspace }}/modules/.coveragerc \
--cov=${{ matrix.module.cov_source }} \
${{ matrix.module.cov_extra }} \
--cov-report=xml:coverage.xml \
--junit-xml=junit.xml \
-o junit_family=legacy
Expand Down
32 changes: 31 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
- { name: "Lint", cmd: "CHANGED=$(git diff --name-only --diff-filter=d origin/${BASE_REF#refs/heads/}...HEAD -- '*.ts' '*.tsx' | head -200); if [ -n \"$CHANGED\" ]; then echo \"$CHANGED\" | xargs -d '\\n' pnpm eslint --; else echo 'No TS/TSX files changed'; fi" }
- { name: "Typecheck", cmd: "pnpm tsc" }
- { name: "Unit Tests", cmd: "pnpm test run --coverage --passWithNoTests" }
- { name: "Browser Tests", cmd: "NODE_ENV=production CI=1 pnpm build && pnpm test:browser" }

steps:
- name: Checkout
Expand All @@ -51,6 +52,27 @@ jobs:
- name: Install Dependencies
run: pnpm install --frozen-lockfile

- name: Setup Python for runtime integration
if: matrix.task.name == 'Unit Tests' || matrix.task.name == 'Browser Tests'
uses: actions/setup-python@v6
with:
python-version: '3.10'

- name: Setup uv for runtime integration
if: matrix.task.name == 'Unit Tests' || matrix.task.name == 'Browser Tests'
uses: astral-sh/setup-uv@v4
with:
enable-cache: true

- name: Install locked runtime integration dependencies
if: matrix.task.name == 'Unit Tests' || matrix.task.name == 'Browser Tests'
working-directory: modules/piezo-processor
run: uv sync --locked --no-install-project

- name: Install browser
if: matrix.task.name == 'Browser Tests'
run: pnpm exec playwright install --with-deps chromium

- name: Cache Assets (Vitest)
id: asset-cache
uses: actions/cache@v5
Expand Down Expand Up @@ -89,4 +111,12 @@ jobs:
fail_ci_if_error: false
slug: sleepypod/core
files: test-results/junit.xml
flags: javascript
flags: javascript

- name: Upload browser evidence
if: matrix.task.name == 'Browser Tests' && !cancelled()
uses: actions/upload-artifact@v6
with:
name: calibration-browser-results
path: test-results/browser
retention-days: 7
12 changes: 6 additions & 6 deletions app/api/raw/[filename]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,26 @@ export async function GET(
}

const resolved = path.resolve(RAW_DIR, filename)
if (!resolved.startsWith(path.resolve(RAW_DIR))) {
if (!resolved.startsWith(path.resolve(/* turbopackIgnore: true */ RAW_DIR))) {
return NextResponse.json({ error: 'Path traversal detected' }, { status: 400 })
}

try {
// Reject symlinks to prevent escaping RAW_DIR
const lstats = await lstat(resolved)
const lstats = await lstat(/* turbopackIgnore: true */ resolved)
if (lstats.isSymbolicLink()) {
return NextResponse.json({ error: 'Path traversal detected' }, { status: 400 })
}

// Verify canonical path is still inside RAW_DIR
const canonicalFile = await realpath(resolved)
const canonicalDir = await realpath(RAW_DIR)
const canonicalFile = await realpath(/* turbopackIgnore: true */ resolved)
const canonicalDir = await realpath(/* turbopackIgnore: true */ RAW_DIR)
if (!canonicalFile.startsWith(canonicalDir)) {
return NextResponse.json({ error: 'Path traversal detected' }, { status: 400 })
}

const s = await stat(resolved)
const stream = createReadStream(resolved)
const s = await stat(/* turbopackIgnore: true */ resolved)
const stream = createReadStream(/* turbopackIgnore: true */ resolved)
const webStream = Readable.toWeb(stream) as ReadableStream

return new Response(webStream, {
Expand Down
43 changes: 43 additions & 0 deletions app/api/raw/[filename]/tests/route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// @vitest-environment node
import { Readable } from 'node:stream'
import { beforeEach, expect, it, vi } from 'vitest'
import { GET } from '../route'
const fs = vi.hoisted(() => ({ lstat: vi.fn(), realpath: vi.fn(), stat: vi.fn(), createReadStream: vi.fn() }))
vi.mock('node:fs/promises', () => ({ lstat: fs.lstat, realpath: fs.realpath, stat: fs.stat }))
vi.mock('node:fs', () => ({ createReadStream: fs.createReadStream }))
const request = (filename = 'capture.RAW') => GET(new Request('http://localhost/api/raw/' + filename), { params: Promise.resolve({ filename }) })
beforeEach(() => {
vi.resetAllMocks()
fs.lstat.mockResolvedValue({ isSymbolicLink: () => false })
fs.realpath.mockImplementation(async (path: string) => path)
fs.stat.mockResolvedValue({ size: 3 })
fs.createReadStream.mockImplementation(() => Readable.from([Buffer.from([1, 2, 3])]))
})
it('streams the original bytes with explicit download metadata', async () => {
const response = await request()
expect(response.status).toBe(200)
expect(response.headers.get('Content-Type')).toBe('application/octet-stream')
expect(response.headers.get('Content-Disposition')).toBe('attachment; filename="capture.RAW"')
expect(response.headers.get('Content-Length')).toBe('3')
expect(new Uint8Array(await response.arrayBuffer())).toEqual(new Uint8Array([1, 2, 3]))
})
it.each(['../private.RAW', 'nested/capture.RAW', 'capture.txt', 'capture.RAW\n'])('rejects invalid filename %s before filesystem access', async (filename) => {
expect((await request(filename)).status).toBe(400)
expect(fs.lstat).not.toHaveBeenCalled()
expect(fs.createReadStream).not.toHaveBeenCalled()
})
it('refuses symbolic links and canonical paths outside the configured directory', async () => {
fs.lstat.mockResolvedValueOnce({ isSymbolicLink: () => true })
expect((await request()).status).toBe(400)
expect(fs.realpath).not.toHaveBeenCalled()
fs.realpath.mockResolvedValueOnce('/outside/capture.RAW').mockResolvedValueOnce('/owned')
expect((await request()).status).toBe(400)
expect(fs.createReadStream).not.toHaveBeenCalled()
})
it.each([['ENOENT', 404, 'File not found'], ['EACCES', 500, 'Failed to read file']] as const)('sanitizes %s filesystem failures', async (code, status, error) => {
fs.lstat.mockRejectedValue(Object.assign(new Error('/private/source'), { code }))
const response = await request()
expect(response.status).toBe(status)
expect(await response.json()).toEqual({ error })
expect(fs.createReadStream).not.toHaveBeenCalled()
})
16 changes: 16 additions & 0 deletions docs/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,19 @@ sudo bash /tmp/sleepypod-install --purge-free-sleep
It also flushes the running iptables and ip6tables rules; the install then reconfigures iptables itself. Without the flag, run the cleanup block the installer prints, reboot, and re-run the installer. free-sleep's `unblock_internet_access.sh` is not a substitute: it flushes runtime rules only and leaves the persisted files in place.

Run `sp-uninstall` if you want to revert (the script flushes iptables and removes sleepypod's units); the purge is not undone, so reinstall free-sleep from its own installer.


### Runtime filesystem reads and standalone tracing

Pod data (RAW captures, saved alarm state, HomeKit identity seeds, archive-push
configuration, procfs and storage-accounting paths) is read on the destination
pod. These dynamic filesystem reads use `turbopackIgnore` call annotations so
Next does not infer that the build machine's whole checkout belongs in the
standalone output. The annotations affect packaging only; existing validation,
file access and error handling still run on the pod.

The biometrics worker location probes are also excluded from automatic tracing:
`scripts/build/biometrics-worker.mjs` explicitly generates `worker.cjs`,
`coordinator.cjs` and `shadow-host.cjs` in the standalone bundle after Next builds.
Keep that packaging step and its dependency-graph checks when changing tracing.
Do not apply these annotations to shipped assets that need inclusion in a release.
Loading
Loading