Skip to content

Add dashboard ClickHouse read models - #1253

Merged
Asherlc merged 6 commits into
mainfrom
Asherlc/clickhouse-dashboard-views
Jun 5, 2026
Merged

Asherlc merged 6 commits into
mainfrom
Asherlc/clickhouse-dashboard-views

Conversation

@Asherlc

@Asherlc Asherlc commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add dbt-owned ClickHouse read models for healthspan, recovery, and strain so dashboard APIs read compact serving tables instead of recomputing dashboard metrics per request.
  • Switch recovery, stress, sleep need, mobile dashboard, and healthspan query paths to the new read models, using FINAL where freshness depends on ReplacingMergeTree.
  • Add ClickHouse table creation, dbt safe-model ordering, test helper coverage, and docs for the dashboard serving layer.

Verification

  • set -a; . ./.env.local; set +a; pnpm lint
  • pnpm tsc --noEmit
  • cd packages/server && pnpm tsc --noEmit
  • cd packages/web && pnpm tsc --noEmit
  • set -a; . ./.env.local; set +a; pnpm test --run

Summary by cubic

Adds ClickHouse dashboard read models for recovery, strain, and healthspan, and switches related routes to read compact tables to cut latency and database load. Incremental builds are now micro-batched and bounded per user/day/week to avoid backfills; healthspan uses VO2 started date and strain date filters are timezone-safe.

  • New Features

    • Added read models: analytics.recovery_read_model, analytics.strain_read_model, analytics.healthspan_read_model using ReplacingMergeTree(refresh_version) with bounded incremental materialization; routes use FINAL where needed.
    • Switched recovery, stress (incl. strength-stress), sleep-need, mobile dashboard, and healthspan APIs to these models; strain queries pass the user timezone; simplified the healthspan query path.
  • Migration

    • Run ClickHouse migrations, including 0026_create_named_dashboard_read_model_tables.ts, then rebuild dbt models with the updated safe model list.
    • No API surface changes or client updates required.

Written for commit dec58db. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added dedicated precomputed dashboard models serving Recovery, Strain, and Healthspan metrics for route-facing queries.
  • Performance Improvements
    • Dashboard metrics for recovery, strain, and healthspan now load from compact read models, reducing request-time aggregation and latency.
  • Documentation
    • Updated analytics and deploy docs and added a production incident entry describing the change and mitigation.
  • Tests & Maintenance
    • Added migration and test coverage for the new read models and updated integration/unit tests.

Copilot AI review requested due to automatic review settings June 4, 2026 23:41
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @Asherlc, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds three incremental ClickHouse dbt read models (recovery_read_model, strain_read_model, healthspan_read_model), a migration to create their tables, test/bootstrap updates to populate and validate them, and switches multiple server routes and tests to consume these read models. Documentation and the DBT safe-model list were updated.

Changes

Named Dashboard Read Models for Recovery, Strain, and Healthspan

Layer / File(s) Summary
Analytics Read Models: Recovery, Strain, Healthspan
analytics/models/read_models/recovery_read_model.sql, analytics/models/read_models/strain_read_model.sql, analytics/models/read_models/healthspan_read_model.sql
Three new dbt incremental models: recovery_read_model computes z-score-based recovery components and final scores; strain_read_model builds dense daily series and rolling acute/chronic metrics with workload ratio; healthspan_read_model aggregates weekly health metrics into (user_id, week_start) keys. All configured with ReplacingMergeTree(refresh_version) and incremental append behavior.
ClickHouse Migration and Registry
src/db/clickhouse-migrations/0026_create_named_dashboard_read_model_tables.ts, src/db/clickhouse-migrations/registry.ts, tests
New migration creates analytics.recovery_read_model, analytics.strain_read_model, and analytics.healthspan_read_model tables with ReplacingMergeTree(refresh_version) engines; registry and migration tests updated to include and validate the new migration.
Test Helpers & ClickHouse Bootstrap
packages/server/src/routers/clickhouse-integration-test-helpers.ts, *.test.ts
Integration-test helpers expanded: added view names, column definitions, engine/ORDER BY selection, SELECT builders (buildTestRecoveryReadModelSelectSql, buildTestStrainReadModelSelectSql, buildTestHealthspanReadModelSelectSql), and updated bootstrap to create/populate the new views during tests.
DBT Safe-set & Model Tests
entrypoint.sh, analytics/models/read_models/read_model_microbatch.sql.test.ts
DBT_SAFE_MODELS allowlist updated; microbatch tests extended to include recovery_read_model, strain_read_model, and healthspan_read_model and to assert each model’s incremental config, engine, lookback window, upstream refs, and expected outputs.
Server routing: Recovery & Stress
packages/server/src/routers/recovery.ts, packages/server/src/repositories/stress-repository.ts, *.test.ts
Routes and repository queries switched from ingredient tables to read models: readinessScore and stress paths now query analytics.recovery_read_model FINAL; related tests updated to expect the new fragments.
Server routing: Strain, Mobile, Sleep
packages/server/src/routers/recovery.ts, packages/server/src/routers/mobile-dashboard.ts, packages/server/src/routers/sleep-need.ts, *.test.ts
Workload/strain queries now read analytics.strain_read_model FINAL (pulling daily_load, acute_load_7d, chronic_load_28d, workload_ratio), simplifying/removing in-route windowing logic. Mobile and sleep routes now read yesterday/current loads from the strain read model; tests updated for timezone/parameter forwarding.
Healthspan Weekly Metrics Routing
packages/server/src/routers/healthspan-query.ts, packages/server/src/routers/healthspan.test.ts, packages/server/src/routers/healthspan-query.test.ts
fetchHealthspanRawData rewritten to query analytics.healthspan_read_model via sensorStore.query, includes a healthspanReadModelRowSchema, aggregation helpers, access-window filtering, and refactored tests that mock sensorStore.query returning compact weekly rows (VO2 handling moved to read-model fields).
Docs & Incident Report
analytics/README.md, deploy/README.md, docs/production-incident-baseline.md
Documentation updated to name route-facing read models and clarify DBT safe-models and analytics-worker behavior. An incident entry documents the 2026-06-04 dashboard latency issue and the mitigation (deploying named incremental read models).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • Asherlc/dofek#1132: Related healthspan inputs and resting-heart-rate sourcing used by the new healthspan_read_model.
  • Asherlc/dofek#1151: Also modifies fetchHealthspanRawData data paths and sleep-derived inputs.
  • Asherlc/dofek#1175: Related ClickHouse migration/registry changes for named read-model migrations.

Suggested labels

area/server, area/db, area/infra, type/feature

Suggested reviewers

  • cubic-dev-ai
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add dashboard ClickHouse read models' is directly related to the main objective of the PR: introducing three new ClickHouse read models (recovery, strain, healthspan) for dashboard serving to avoid recomputing metrics per request. It accurately summarizes the primary change across all modified files.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@cubic-dev-ai cubic-dev-ai 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.

4 issues found across 28 files

Confidence score: 2/5

  • There is high user-impact risk in packages/server/src/routers/healthspan-query.ts: week-rounding the access window can both expose out-of-window data and exclude valid in-window data for limited entitlements.
  • Timezone handling appears regressed in packages/server/src/routers/recovery.ts and packages/server/src/routers/sleep-need.ts, with UTC-based bucketing likely shifting daily load/ACWR and sleep-need results for non-UTC users.
  • Given multiple medium-to-high severity findings (6–8/10) with strong confidence, this is likely not safe to merge without targeted fixes and validation.
  • Pay close attention to packages/server/src/routers/healthspan-query.ts, packages/server/src/routers/recovery.ts, packages/server/src/routers/sleep-need.ts - entitlement window accuracy, week aggregation correctness, and user-local day boundary logic need verification.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/server/src/routers/healthspan-query.ts Outdated
Comment thread packages/server/src/routers/recovery.ts
Comment thread packages/server/src/routers/healthspan-query.ts Outdated
Comment thread packages/server/src/routers/sleep-need.ts Outdated
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Storybook previews for 3a34f1ff are ready:

This comment updates automatically on each PR push.

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@analytics/models/read_models/healthspan_read_model.sql`:
- Around line 73-80: The vo2_by_week CTE is averaging vo2max into latest_vo2max
which loses ordering; replace the order-insensitive avg(vo2max) with an
order-sensitive aggregate (e.g., argMax(vo2max, activity_date) or
anyLast(vo2max) depending on ClickHouse convention) so vo2_by_week.latest_vo2max
returns the actual last measurement per user/week from {{
ref('activity_vo2max_estimate') }}; keep GROUP BY user_id,
toMonday(activity_date) and ensure activity_date is used as the ordering
argument so later latestNonNullValue() can recover the true latest value.
- Around line 1-9: The incremental model currently appends every run because the
full week_keys set is always recomputed and cross-joined with refresh_clock;
change the SQL so when is_incremental() is true you filter week_keys to only
new/dirty rows (e.g., WHERE (user_id, week_start) NOT IN (SELECT user_id,
week_start FROM analytics.healthspan_read_model) or join against
analytics.healthspan_read_model to exclude existing keys) and only add the new
refresh_version for those rows (i.e., put the CROSS JOIN refresh_clock /
refresh_version assignment inside the {% if is_incremental() %} branch or
conditionally apply it), ensuring incremental_strategy='append' actually appends
only new targets; also update vo2_by_week so latest_vo2max uses a within-week
"latest" aggregator such as argMax(vo2max, activity_date) (replace avg(vo2max)
AS latest_vo2max) so that latestNonNullValue(...) over weeks receives a per-week
latest value instead of an average.

In `@analytics/models/read_models/recovery_read_model.sql`:
- Around line 1-9: The model analytics.recovery_read_model is configured as
incremental (materialized='incremental', incremental_strategy='append',
engine='ReplacingMergeTree(refresh_version)') but lacks an is_incremental()
guard, causing full snapshots to be appended every run; modify the model SQL to
include an incremental filter using is_incremental() that only selects/returns
rows for new or changed (user_id, date) partitions (e.g., filter by source
change timestamp or a date range of affected input rows) so you append only
deltas, or alternatively change incremental_strategy to one that enforces
single-current-row semantics (e.g., use insert_overwrite semantics or ensure a
unique key and merge behavior) and update the model body to emit only the
intended partition/key rows when not a full-refresh.

In `@analytics/models/read_models/strain_read_model.sql`:
- Around line 1-9: The incremental append currently regenerates the full
(user_id, date) history because date_series is computed from min(date) through
greatest(max(date), today()) without an is_incremental() guard; fix it by adding
an is_incremental() branch that reads the current max(date) from {{ this }}
(SELECT max(date) FROM {{ this }}) and bounds the date_series to only the
affected window (use greatest(min(date), date_sub(existing_max_date, INTERVAL
<lookback> DAY)) as the start and greatest(max(date), today()) as the end) so
each run only appends the recent window; use a 6-day lookback for acute and
27-day for chronic, keep the existing incremental_strategy='append' and
refresh_version logic unchanged.

In `@docs/production-incident-baseline.md`:
- Around line 10123-10143: This incident entry is missing the required
"Follow-Up Work" section; add a concise Follow-Up Work paragraph listing
concrete next actions such as: (1) run post-deploy Axiom/Sentry checks to verify
request latency improvements and confirm the UTC daily bucketing in
analytics.strain_read_model is acceptable, (2) add a short-term dashboard
latency alert (route-facing metrics) and a ticket to verify reads from
analytics.recovery_read_model and analytics.healthspan_read_model are stable,
(3) update the production incident baseline and runbook to reflect the new
dbt-owned incremental models and empty-table deploy step, and (4) if no further
action is planned, state "none currently" with a brief rationale.

In `@packages/server/src/routers/recovery.ts`:
- Around line 276-285: The query is converting strain.date through
toDateTime->toTimeZone which can shift day-granular dates; remove that
conversion and use the raw day-granular value instead (e.g., use strain.date
directly or toString(toDate(strain.date)) for the AS date field) in the
workload-ratio SQL fragment that builds the SELECT (the expression producing "AS
date" from strain.date), and make the same change to the similar expression
later (lines referenced around the second occurrence). Ensure the WHERE filters
still use strain.date unchanged so output dates and window semantics stay
consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9ee28f90-e6c7-499f-bd91-71d38f3d64ca

📥 Commits

Reviewing files that changed from the base of the PR and between c41844e and 1827e06.

📒 Files selected for processing (28)
  • analytics/README.md
  • analytics/models/read_models/healthspan_read_model.sql
  • analytics/models/read_models/read_model_microbatch.sql.test.ts
  • analytics/models/read_models/recovery_read_model.sql
  • analytics/models/read_models/strain_read_model.sql
  • deploy/README.md
  • docs/production-incident-baseline.md
  • entrypoint.sh
  • packages/server/src/repositories/stress-repository.test.ts
  • packages/server/src/repositories/stress-repository.ts
  • packages/server/src/routers/clickhouse-integration-test-helpers.test.ts
  • packages/server/src/routers/clickhouse-integration-test-helpers.ts
  • packages/server/src/routers/healthspan-query.test.ts
  • packages/server/src/routers/healthspan-query.ts
  • packages/server/src/routers/healthspan.test.ts
  • packages/server/src/routers/mobile-dashboard.test.ts
  • packages/server/src/routers/mobile-dashboard.ts
  • packages/server/src/routers/recovery-settings-sleep-need-sport-settings.test.ts
  • packages/server/src/routers/recovery.test.ts
  • packages/server/src/routers/recovery.ts
  • packages/server/src/routers/sleep-need.test.ts
  • packages/server/src/routers/sleep-need.ts
  • packages/server/src/routers/strength-stress.test.ts
  • packages/server/src/routers/stress.test.ts
  • src/db/clickhouse-migrations.test.ts
  • src/db/clickhouse-migrations/0026_create_named_dashboard_read_model_tables.ts
  • src/db/clickhouse-migrations/registry.test.ts
  • src/db/clickhouse-migrations/registry.ts

Comment thread analytics/models/read_models/healthspan_read_model.sql
Comment thread analytics/models/read_models/healthspan_read_model.sql Outdated
Comment thread analytics/models/read_models/recovery_read_model.sql
Comment thread analytics/models/read_models/strain_read_model.sql
Comment thread docs/production-incident-baseline.md
Comment thread packages/server/src/routers/recovery.ts

@cubic-dev-ai cubic-dev-ai 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.

0 issues found across 2 files (changes from recent commits).

Requires human review: This PR adds new ClickHouse read models and switches multiple core dashboard API routes to use them, involving new business logic, schema changes, and complex queries that could affect data accuracy and system behavior, so human review is needed to verify correctness and data integrity.

Re-trigger cubic

@cubic-dev-ai cubic-dev-ai 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.

0 issues found across 2 files (changes from recent commits).

Requires human review: This PR introduces new ClickHouse read models that precompute dashboard metrics (recovery scores, strain, healthspan) and rewrites multiple API routes to query them instead of computing on request, which involves complex business logic in SQL (z-scores, sigmoid functions, windowed loads, weekly...

Re-trigger cubic

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/server/src/routers/recovery.ts (1)

594-600: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Parse strainTarget read-model dates with dateStringSchema instead of z.string().

Line 595 uses z.string() for a DB date boundary. If a non-date string ever crosses this boundary, the daysAgo computation (Line 655) becomes NaN and silently distorts acute/chronic load outputs.

Suggested fix
       const loads = await sensorStore.query(
         z.object({
-          date: z.string(),
+          date: dateStringSchema,
           daily_load: z.coerce.number(),
         }),

As per coding guidelines, "Use dateStringSchema and timestampStringSchema from src/lib/typed-sql.ts to handle DB-to-JS type normalization."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/server/src/routers/recovery.ts` around lines 594 - 600, Replace the
loose z.string() used for the read-model date with the stricter dateStringSchema
from src/lib/typed-sql.ts so DB date strings are normalized and validated before
daysAgo math; import dateStringSchema and update the z.object that currently has
date: z.string() (the schema used for the strain/read-model row with daily_load)
to use dateStringSchema instead of z.string().
♻️ Duplicate comments (1)
packages/server/src/routers/recovery.ts (1)

276-290: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use raw strain.date in workloadRatio output to prevent day-shift mismatches.

On Line 276, converting a day-granular Date through toDateTime(...)->toTimeZone(...)->toDate(...) can shift the day for non-UTC users, while Lines 283-284 still filter with unshifted strain.date. That can return rows inside the filter window but label them as a different calendar day.

Suggested fix
-          toString(toDate(toTimeZone(toDateTime(strain.date), {timezone:String}))) AS date,
+          toString(strain.date) AS date,
           strain.daily_load AS daily_load,
           strain.acute_load_7d AS acute_load,
           strain.chronic_load_28d AS chronic_load,
           strain.workload_ratio AS workload_ratio
@@
         {
           userId: ctx.userId,
-          timezone: ctx.timezone,
           endDate: input.endDate,
           outputWindowStart: dateWindowStartString(input.endDate, input.days),
         },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/server/src/routers/recovery.ts` around lines 276 - 290, The SELECT
is converting strain.date through toDateTime(...)->toTimeZone(...)->toDate(...)
which can shift days; change the expression used for the date output that feeds
workload_ratio to use the raw day-granular strain.date (e.g.,
toString(strain.date) or strain.date) instead of the timezone-shifted value so
the label/ordering (ORDER BY date ASC) and the WHERE filters (strain.date > ...
AND strain.date <= ...) remain consistent with workload_ratio; update the
SELECT's date alias (the expression currently using
toDate(toTimeZone(toDateTime(strain.date), {timezone:String}))) to use
strain.date and keep the rest of the query (userId, timezone, endDate,
outputWindowStart) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/server/src/routers/recovery.ts`:
- Around line 594-600: Replace the loose z.string() used for the read-model date
with the stricter dateStringSchema from src/lib/typed-sql.ts so DB date strings
are normalized and validated before daysAgo math; import dateStringSchema and
update the z.object that currently has date: z.string() (the schema used for the
strain/read-model row with daily_load) to use dateStringSchema instead of
z.string().

---

Duplicate comments:
In `@packages/server/src/routers/recovery.ts`:
- Around line 276-290: The SELECT is converting strain.date through
toDateTime(...)->toTimeZone(...)->toDate(...) which can shift days; change the
expression used for the date output that feeds workload_ratio to use the raw
day-granular strain.date (e.g., toString(strain.date) or strain.date) instead of
the timezone-shifted value so the label/ordering (ORDER BY date ASC) and the
WHERE filters (strain.date > ... AND strain.date <= ...) remain consistent with
workload_ratio; update the SELECT's date alias (the expression currently using
toDate(toTimeZone(toDateTime(strain.date), {timezone:String}))) to use
strain.date and keep the rest of the query (userId, timezone, endDate,
outputWindowStart) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fae38c72-2e0f-4fa5-bab1-8f8a5a3ea24e

📥 Commits

Reviewing files that changed from the base of the PR and between 1827e06 and 330de12.

📒 Files selected for processing (7)
  • analytics/models/read_models/healthspan_read_model.sql
  • analytics/models/read_models/read_model_microbatch.sql.test.ts
  • analytics/models/read_models/recovery_read_model.sql
  • analytics/models/read_models/strain_read_model.sql
  • docs/production-incident-baseline.md
  • packages/server/src/routers/recovery.test.ts
  • packages/server/src/routers/recovery.ts

@cubic-dev-ai cubic-dev-ai 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.

3 issues found across 5 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread analytics/models/read_models/strain_read_model.sql
Comment thread analytics/models/read_models/recovery_read_model.sql
Comment thread analytics/models/read_models/healthspan_read_model.sql Outdated

@cubic-dev-ai cubic-dev-ai 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.

0 issues found across 4 files (changes from recent commits).

Requires human review: This PR adds new dbt models with business logic (z-scores, sigmoid scoring), creates ClickHouse tables, and switches multiple API routes to read from them, which requires careful review of SQL correctness, migration ordering, and potential data integrity issues.

Re-trigger cubic

@Asherlc
Asherlc merged commit f3bcdd7 into main Jun 5, 2026
74 checks passed
@Asherlc
Asherlc deleted the Asherlc/clickhouse-dashboard-views branch June 5, 2026 05:10
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.

2 participants