@geraldmaron/construct 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/.env.example +1 -1
  2. package/README.md +4 -4
  3. package/agents/prompts/cx-ai-engineer.md +6 -26
  4. package/agents/prompts/cx-architect.md +1 -0
  5. package/agents/prompts/cx-business-strategist.md +2 -0
  6. package/agents/prompts/cx-data-analyst.md +6 -26
  7. package/agents/prompts/cx-docs-keeper.md +1 -31
  8. package/agents/prompts/cx-explorer.md +1 -0
  9. package/agents/prompts/cx-orchestrator.md +40 -112
  10. package/agents/prompts/cx-platform-engineer.md +2 -22
  11. package/agents/prompts/cx-product-manager.md +2 -1
  12. package/agents/prompts/cx-qa.md +0 -20
  13. package/agents/prompts/cx-rd-lead.md +2 -0
  14. package/agents/prompts/cx-researcher.md +77 -31
  15. package/agents/prompts/cx-security.md +11 -49
  16. package/agents/prompts/cx-sre.md +9 -43
  17. package/agents/prompts/cx-ux-researcher.md +1 -0
  18. package/agents/role-manifests.json +4 -4
  19. package/bin/construct +72 -11
  20. package/bin/construct-postinstall.mjs +1 -1
  21. package/db/schema/004_recommendations.sql +46 -0
  22. package/db/schema/005_strategy.sql +21 -0
  23. package/lib/auto-docs.mjs +1 -2
  24. package/lib/beads-automation.mjs +16 -7
  25. package/lib/bootstrap/resources.mjs +2 -2
  26. package/lib/cli-commands.mjs +8 -2
  27. package/lib/document-ingest.mjs +6 -5
  28. package/lib/embed/cli.mjs +16 -3
  29. package/lib/embed/conflict-detection.mjs +26 -9
  30. package/lib/embed/customer-profiles.mjs +38 -18
  31. package/lib/embed/daemon.mjs +59 -50
  32. package/lib/embed/inbox.mjs +30 -0
  33. package/lib/embed/recommendation-store.mjs +214 -15
  34. package/lib/embed/workspaces.mjs +53 -18
  35. package/lib/evaluator-optimizer.mjs +0 -2
  36. package/lib/features.mjs +11 -0
  37. package/lib/gates-audit.mjs +3 -3
  38. package/lib/health-check.mjs +3 -5
  39. package/lib/hooks/pre-compact.mjs +3 -0
  40. package/lib/hooks/read-tracker.mjs +10 -101
  41. package/lib/host-capabilities.mjs +90 -1
  42. package/lib/init-unified.mjs +119 -3
  43. package/lib/init-update.mjs +246 -131
  44. package/lib/install/first-invocation.mjs +4 -4
  45. package/lib/intake/queue.mjs +1 -1
  46. package/lib/integrations/intake-integrations.mjs +4 -5
  47. package/lib/intent-classifier.mjs +1 -0
  48. package/lib/knowledge/layout.mjs +10 -0
  49. package/lib/knowledge/rag.mjs +16 -0
  50. package/lib/mcp/tools/telemetry.mjs +30 -78
  51. package/lib/model-cheapest-provider.mjs +231 -0
  52. package/lib/model-router.mjs +68 -9
  53. package/lib/ollama-manager.mjs +1 -1
  54. package/lib/opencode-telemetry.mjs +4 -5
  55. package/lib/orchestration-policy.mjs +9 -0
  56. package/lib/parity.mjs +124 -21
  57. package/lib/project-profile.mjs +1 -1
  58. package/lib/prompt-composer.js +106 -29
  59. package/lib/read-tracker-store.mjs +149 -0
  60. package/lib/roles/catalog.mjs +133 -0
  61. package/lib/roles/preference.mjs +74 -0
  62. package/lib/server/index.mjs +109 -47
  63. package/lib/server/insights.mjs +1 -1
  64. package/lib/server/telemetry-login.mjs +17 -25
  65. package/lib/service-manager.mjs +32 -24
  66. package/lib/services/local-postgres.mjs +15 -0
  67. package/lib/services/telemetry-backend.mjs +2 -3
  68. package/lib/setup-prompts.mjs +2 -2
  69. package/lib/setup.mjs +55 -46
  70. package/lib/status.mjs +56 -8
  71. package/lib/storage/backend.mjs +12 -2
  72. package/lib/storage/postgres-backup.mjs +1 -1
  73. package/lib/strategy-store.mjs +371 -0
  74. package/lib/telemetry/backends/local.mjs +6 -4
  75. package/lib/telemetry/client.mjs +185 -0
  76. package/lib/telemetry/ingest.mjs +13 -5
  77. package/lib/telemetry/team-rollup.mjs +9 -2
  78. package/lib/uninstall/uninstall.mjs +1 -1
  79. package/lib/worker/trace.mjs +17 -27
  80. package/package.json +5 -2
  81. package/rules/common/research.md +44 -12
  82. package/skills/docs/backlog-proposal-workflow.md +2 -2
  83. package/skills/docs/customer-profile-workflow.md +1 -1
  84. package/skills/docs/evidence-ingest-workflow.md +5 -5
  85. package/skills/docs/prfaq-workflow.md +1 -1
  86. package/skills/docs/product-intelligence-review.md +1 -1
  87. package/skills/docs/product-intelligence-workflow.md +3 -3
  88. package/skills/docs/product-signal-workflow.md +48 -18
  89. package/skills/docs/research-workflow.md +26 -14
  90. package/skills/docs/strategy-workflow.md +36 -0
  91. package/skills/roles/data-analyst.product-intelligence.md +1 -1
  92. package/skills/roles/researcher.md +28 -15
  93. package/skills/routing.md +8 -1
  94. package/templates/docs/construct_guide.md +2 -2
  95. package/templates/docs/research-brief.md +63 -9
  96. package/templates/docs/strategy.md +36 -0
  97. package/templates/homebrew/construct.rb +7 -7
package/lib/setup.mjs CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Guides users through provider selection, API key entry, and model tier
6
6
  * assignment. Writes the resulting config to ~/.cx/env and optionally to
7
- * project-level .env. Invoked by `construct setup` and on first init.
7
+ * project-level .env. Invoked by `construct init` and on first init.
8
8
  */
9
9
 
10
10
  import fs from 'node:fs';
@@ -25,10 +25,6 @@ import { createSqlClient, closeSqlClient } from './storage/backend.mjs';
25
25
  import { getEmbeddingModelInfo, warmupEmbeddingModel } from './storage/embeddings-engine.mjs';
26
26
  import { restoreConstructDb } from './storage/postgres-backup.mjs';
27
27
  import { buildPressureGuardValues, installPressureGuardLaunchAgent, loadPressureGuardLaunchAgent } from './runtime-pressure.mjs';
28
- import {
29
- isRemoteTelemetry,
30
- startManagedServices,
31
- } from './services/telemetry-backend.mjs';
32
28
  import { consentToInstall } from './setup-prompts.mjs';
33
29
 
34
30
  const ROOT_DIR = path.resolve(import.meta.dirname, '..');
@@ -43,7 +39,7 @@ function printHelp() {
43
39
  console.log(`Construct setup
44
40
 
45
41
  Usage:
46
- construct setup [--yes] [--no-docker]
42
+ construct init [--yes] [--no-docker]
47
43
 
48
44
  What it does:
49
45
  - creates ~/.construct/config.env
@@ -86,9 +82,8 @@ export function defaultVectorIndexPath(homeDir = HOME) {
86
82
  export async function buildManagedSetupValues({ homeDir = HOME, env = process.env, databaseUrl = '' } = {}) {
87
83
  const modelInfo = await getEmbeddingModelInfo({ env });
88
84
 
89
- // Local telemetry backend
90
85
  const values = {
91
- CONSTRUCT_TRACE_BACKEND: env.CONSTRUCT_TRACE_BACKEND || 'remote',
86
+ CONSTRUCT_TRACE_BACKEND: env.CONSTRUCT_TRACE_BACKEND || 'local',
92
87
  CONSTRUCT_VECTOR_INDEX_PATH: env.CONSTRUCT_VECTOR_INDEX_PATH || defaultVectorIndexPath(homeDir),
93
88
  CONSTRUCT_VECTOR_MODEL: env.CONSTRUCT_VECTOR_MODEL || modelInfo.model,
94
89
  ...buildPressureGuardValues({ env }),
@@ -533,37 +528,10 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
533
528
  console.log(` ${dockerInstallHint()}`);
534
529
  }
535
530
 
536
- // Local telemetry backend — starts Postgres via services/docker-compose.yml.
537
- // Skipped automatically when CONSTRUCT_TELEMETRY_URL points to a remote instance.
538
-
539
- let telemetryResult = { status: 'skipped', note: 'Docker not available' };
540
- if (dockerAvailable) {
541
- const svcConsent = await consentToInstall({
542
- name: 'telemetry',
543
- isYes,
544
- alreadyConfigured: isRemoteTelemetry(process.env.CONSTRUCT_TELEMETRY_URL || ''),
545
- alreadyConfiguredNote: 'CONSTRUCT_TELEMETRY_URL points to a remote instance — local stack not started.',
546
- envPath,
547
- });
548
- if (svcConsent.decision) {
549
- try {
550
- telemetryResult = await startManagedServices({
551
- rootDir,
552
- homeDir,
553
- env: process.env,
554
- composeRunner: dockerRunner,
555
- spawnDetached: spawnDetachedForSetup,
556
- });
557
- console.log(`Telemetry: ${telemetryResult.status} — ${telemetryResult.note || ''}`);
558
- } catch (err) {
559
- telemetryResult = { status: 'error', note: err?.message || 'spin-up failed' };
560
- console.log(`Telemetry: error — ${telemetryResult.note}`);
561
- }
562
- } else {
563
- telemetryResult = { status: 'skipped', note: svcConsent.note };
564
- console.log(`Telemetry: skipped (${svcConsent.note})`);
565
- }
566
- }
531
+ const telemetryResult = process.env.CONSTRUCT_TELEMETRY_URL
532
+ ? { status: 'configured', note: `remote export configured (${process.env.CONSTRUCT_TELEMETRY_URL})` }
533
+ : { status: 'local', note: 'local JSONL traces in .cx/traces; remote export optional' };
534
+ console.log(`Telemetry: ${telemetryResult.note}`);
567
535
 
568
536
  fs.mkdirSync(path.dirname(defaultVectorIndexPath(homeDir)), { recursive: true });
569
537
 
@@ -585,6 +553,50 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
585
553
  console.log(`Embeddings: warmup skipped (${err?.message || 'unknown error'}) — model will load on first use`);
586
554
  }
587
555
 
556
+ // Cheapest provider selection — opt-out by default. When user consents,
557
+ // evaluate all configured providers, pick the lowest-cost model per tier,
558
+ // and write to config.env. On subsequent runs the preference is persisted
559
+ // so the prompt is skipped.
560
+
561
+ const { isCheapestProviderEnabled, selectCheapestForAllTiers, setCheapestProviderPreference, formatCheapestProviderMessage } =
562
+ await import('./model-cheapest-provider.mjs');
563
+ const cheapestAlreadyEnabled = isCheapestProviderEnabled(envPath, { env: process.env });
564
+ if (!cheapestAlreadyEnabled) {
565
+ const cheapestConsent = await consentToInstall({
566
+ name: 'cheapest-provider',
567
+ isYes,
568
+ alreadyConfigured: false,
569
+ envPath,
570
+ defaultYes: false,
571
+ });
572
+ if (cheapestConsent.decision) {
573
+ try {
574
+ const selections = await selectCheapestForAllTiers({ env: process.env });
575
+ const applied = {};
576
+ for (const tier of ['reasoning', 'standard', 'fast']) {
577
+ if (selections[tier]?.modelId) applied[tier] = selections[tier].modelId;
578
+ }
579
+ if (Object.keys(applied).length > 0) {
580
+ applyToEnv(envPath, applied);
581
+ console.log('\nCheapest providers applied:');
582
+ for (const [tier, model] of Object.entries(applied)) {
583
+ const label = selections[tier]?.providerLabel || '';
584
+ console.log(` ${tier.padEnd(11)} ${model} (${label})`);
585
+ }
586
+ } else {
587
+ console.log('\nCheapest provider: no configured providers found — nothing to apply.');
588
+ }
589
+ setCheapestProviderPreference(envPath, true);
590
+ } catch (err) {
591
+ console.log(`Cheapest provider: skipped (${err?.message || 'unknown error'})`);
592
+ }
593
+ } else {
594
+ console.log(`Cheapest provider: skipped (${cheapestConsent.note})`);
595
+ }
596
+ } else {
597
+ console.log('Cheapest provider: already enabled — skipping prompt.');
598
+ }
599
+
588
600
  const hooksResult = ensureGitHooksPath({ cwd: process.cwd() });
589
601
  if (hooksResult.status === 'set') console.log(`Git hooks: ${hooksResult.message}`);
590
602
  else if (hooksResult.status === 'ok') console.log('Git hooks: wired');
@@ -705,7 +717,7 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
705
717
  console.log(` Trace backend: ${managedValues.CONSTRUCT_TRACE_BACKEND}${managedValues.CONSTRUCT_TELEMETRY_URL ? ` (${managedValues.CONSTRUCT_TELEMETRY_URL})` : ''}`);
706
718
  console.log(` Pressure guard: swap ${managedValues.CONSTRUCT_PRESSURE_GUARD_SWAP_GB} GiB, opencode max ${managedValues.CONSTRUCT_PRESSURE_GUARD_MAX_OPENCODE}`);
707
719
  console.log('\nFor unattended setup, including local Postgres when Docker is running:');
708
- console.log(' construct setup --yes');
720
+ console.log(' construct init --yes');
709
721
  }
710
722
 
711
723
  runConstruct(['sync']);
@@ -746,18 +758,15 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
746
758
  // Local-service summary — surfaces running local services. Same pattern
747
759
  // as `supabase start`, which prints all local URLs + keys after spin-up.
748
760
 
749
- const hasLocalTelemetry = telemetryResult?.status === 'started' || telemetryResult?.status === 'degraded';
750
761
  const hasLocalPostgres = serviceResult?.status === 'ok' && serviceResult?.databaseUrl?.includes('127.0.0.1');
751
762
  const vectorBackend = hasLocalPostgres
752
763
  ? { label: 'Postgres + pgvector', detail: `${serviceResult.databaseUrl} (384d embeddings, ${managedValues.CONSTRUCT_VECTOR_MODEL})` }
753
764
  : { label: 'JSON fallback', detail: `${managedValues.CONSTRUCT_VECTOR_INDEX_PATH} (${managedValues.CONSTRUCT_VECTOR_MODEL})` };
754
765
 
755
766
  console.log('\nLocal services:');
756
- if (hasLocalTelemetry) {
757
- console.log(` Telemetry: ${telemetryResult.note || 'started'}`);
758
- } else {
759
- console.log(` Telemetry: not started`);
760
- }
767
+ console.log(' Traces: local JSONL (.cx/traces)');
768
+ if (process.env.CONSTRUCT_TELEMETRY_URL) console.log(` Telemetry: remote export (${process.env.CONSTRUCT_TELEMETRY_URL})`);
769
+ else console.log(' Telemetry: remote export not configured');
761
770
  if (hasLocalPostgres) {
762
771
  console.log(` Postgres: ${serviceResult.databaseUrl}`);
763
772
  } else if (process.env.DATABASE_URL) {
package/lib/status.mjs CHANGED
@@ -24,6 +24,7 @@ import { describeSqlStore, sqlStoreHealth } from './storage/sql-store.mjs';
24
24
  import { describeVectorStore } from './storage/vector-store.mjs';
25
25
  import { describeSqlStoreHealth } from './storage/sql-store.mjs';
26
26
  import { triggerAutoBackfillIfSparse } from './telemetry/backfill.mjs';
27
+ import { resolveTraceBackend, telemetryProviderLabel } from './telemetry/client.mjs';
27
28
  const TOTAL_BYTES_WARNING_THRESHOLD = 750_000;
28
29
 
29
30
  function readJSON(path) {
@@ -317,20 +318,29 @@ async function defaultProbeService(service) {
317
318
  }
318
319
 
319
320
  async function fetchTelemetryStatus(env, { timeout = 2500 } = {}) {
321
+ const backend = resolveTraceBackend(env);
322
+ if (backend === 'local') return { status: 'healthy', summary: 'Local JSONL tracing enabled · remote export not configured', backend, provider: telemetryProviderLabel(env) };
323
+ if (backend === 'none') return { status: 'disabled', summary: 'Remote telemetry disabled · local JSONL tracing preserved', backend, provider: telemetryProviderLabel(env) };
324
+ if (backend === 'otel') {
325
+ const endpoint = (env.CONSTRUCT_OTEL_EXPORTER_OTLP_ENDPOINT ?? '').replace(/\/$/, '');
326
+ if (!endpoint) return { status: 'unavailable', summary: 'OTLP endpoint not configured', backend, provider: telemetryProviderLabel(env) };
327
+ return { status: 'configured', summary: `OTLP export configured · ${endpoint}`, backend, provider: telemetryProviderLabel(env) };
328
+ }
320
329
  const baseUrl = (env.CONSTRUCT_TELEMETRY_URL ?? '').replace(/\/$/, '');
321
330
  const key = env.CONSTRUCT_TELEMETRY_PUBLIC_KEY;
322
331
  const secret = env.CONSTRUCT_TELEMETRY_SECRET_KEY;
323
- if (!key || !secret) return { status: 'unavailable', summary: 'Telemetry credentials not configured' };
332
+ if (backend === 'langfuse' && (!key || !secret)) return { status: 'unavailable', summary: 'Langfuse credentials not configured', backend, provider: telemetryProviderLabel(env) };
324
333
  if (!baseUrl) return { status: 'unavailable', summary: 'CONSTRUCT_TELEMETRY_URL not set' };
325
334
  const headers = {
326
- Authorization: `Basic ${Buffer.from(`${key}:${secret}`).toString('base64')}`,
335
+ ...(key && secret ? { Authorization: `Basic ${Buffer.from(`${key}:${secret}`).toString('base64')}` } : {}),
327
336
  };
328
337
  const controller = new AbortController();
329
338
  const timer = setTimeout(() => controller.abort(), timeout);
330
339
  try {
331
- const res = await fetch(`${baseUrl}/api/public/traces?limit=25`, { headers, signal: controller.signal });
340
+ const tracesPath = backend === 'http' ? '/traces?limit=25' : '/api/public/traces?limit=25';
341
+ const res = await fetch(`${baseUrl}${tracesPath}`, { headers, signal: controller.signal });
332
342
  if (res.status === 401 || res.status === 403) {
333
- return { status: 'credentials-invalid', summary: `Telemetry credentials rejected (HTTP ${res.status}) — run: construct setup` };
343
+ return { status: 'credentials-invalid', summary: `Telemetry credentials rejected (HTTP ${res.status}) — run: construct init` };
334
344
  }
335
345
  if (!res.ok) return { status: 'degraded', summary: `Telemetry HTTP ${res.status}` };
336
346
  const json = await res.json().catch(() => ({}));
@@ -359,6 +369,8 @@ async function fetchTelemetryStatus(env, { timeout = 2500 } = {}) {
359
369
 
360
370
  return {
361
371
  status,
372
+ backend,
373
+ provider: telemetryProviderLabel(env),
362
374
  total,
363
375
  rich: counts.rich,
364
376
  partial: counts.partial,
@@ -478,6 +490,40 @@ export function buildPublicHealthSurface({
478
490
  }
479
491
 
480
492
  function traceBackendDefinition(env) {
493
+ const backend = resolveTraceBackend(env);
494
+ const provider = telemetryProviderLabel(env);
495
+ if (backend === 'local' || backend === 'none') {
496
+ return {
497
+ id: 'telemetry',
498
+ name: 'Telemetry',
499
+ url: 'local://.cx/traces',
500
+ runtime: 'local',
501
+ note: backend === 'none' ? 'Remote disabled; local JSONL preserved' : 'Local JSONL traces',
502
+ healthyMessage: 'Local trace capture enabled',
503
+ impactsOverall: false,
504
+ selfCheck: {
505
+ status: backend === 'none' ? 'disabled' : 'healthy',
506
+ message: backend === 'none' ? 'Remote telemetry disabled' : 'Writing .cx/traces/*.jsonl',
507
+ },
508
+ };
509
+ }
510
+ if (backend === 'otel') {
511
+ const endpoint = (env.CONSTRUCT_OTEL_EXPORTER_OTLP_ENDPOINT ?? '').replace(/\/$/, '');
512
+ return {
513
+ id: 'telemetry',
514
+ name: 'Telemetry',
515
+ url: endpoint || '(not configured)',
516
+ probeUrl: '',
517
+ runtime: 'remote',
518
+ note: `${provider} trace export`,
519
+ healthyMessage: 'Configured',
520
+ impactsOverall: false,
521
+ selfCheck: {
522
+ status: endpoint ? 'configured' : 'unavailable',
523
+ message: endpoint ? `OTLP export configured: ${endpoint}` : 'CONSTRUCT_OTEL_EXPORTER_OTLP_ENDPOINT not set',
524
+ },
525
+ };
526
+ }
481
527
  const url = (env.CONSTRUCT_TELEMETRY_URL ?? '').replace(/\/$/, '');
482
528
  const pubKey = env.CONSTRUCT_TELEMETRY_PUBLIC_KEY || '';
483
529
  const secKey = env.CONSTRUCT_TELEMETRY_SECRET_KEY || '';
@@ -486,11 +532,12 @@ function traceBackendDefinition(env) {
486
532
  id: 'telemetry',
487
533
  name: 'Telemetry',
488
534
  url: url || '(not configured)',
489
- probeUrl: url ? `${url}/api/public/traces?limit=1` : '',
535
+ probeUrl: url ? `${url}${backend === 'http' ? '/traces?limit=1' : '/api/public/traces?limit=1'}` : '',
490
536
  probeHeaders: auth ? { Authorization: auth } : undefined,
491
537
  runtime: 'live',
492
- note: 'Trace backend',
538
+ note: `${provider} trace export`,
493
539
  healthyMessage: 'Reachable',
540
+ impactsOverall: false,
494
541
  };
495
542
  }
496
543
 
@@ -588,6 +635,7 @@ function serviceDefinitions(env, dashboardPort, selfDashboard) {
588
635
  runtime: 'live',
589
636
  note: 'MCP-managed',
590
637
  healthyMessage: 'Reachable',
638
+ impactsOverall: false,
591
639
  },
592
640
  {
593
641
  id: 'opencode',
@@ -772,8 +820,8 @@ export async function buildStatus({
772
820
  }
773
821
 
774
822
  function serviceIcon(status) {
775
- if (status === 'healthy') return '✓';
776
- if (status === 'degraded' || status === 'configured') return '⚠';
823
+ if (status === 'healthy' || status === 'configured') return '✓';
824
+ if (status === 'degraded') return '⚠';
777
825
  return '✗';
778
826
  }
779
827
 
@@ -1,17 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * lib/storage/backend.mjs — shared storage backend helpers for SQL/vector wiring.
4
+ *
5
+ * postgres is an optional peer dep. Top-level await catches the case where it is not
6
+ * installed (fresh installs, test temp dirs) so the module loads cleanly everywhere.
4
7
  */
5
- import postgres from 'postgres';
6
8
  import { resolveDatabaseUrl } from '../env-config.mjs';
7
9
 
10
+ let postgres = null;
11
+ try {
12
+ const mod = await import('postgres');
13
+ postgres = mod.default ?? mod;
14
+ } catch {
15
+ // postgres not installed — SQL features unavailable; createSqlClient returns null
16
+ }
17
+
8
18
  function cleanUrl(url) {
9
19
  return String(url || '').trim();
10
20
  }
11
21
 
12
22
  export function createSqlClient(env = process.env) {
13
23
  const databaseUrl = cleanUrl(resolveDatabaseUrl(env));
14
- if (!databaseUrl) return null;
24
+ if (!databaseUrl || !postgres) return null;
15
25
  return postgres(databaseUrl, {
16
26
  max: Number.parseInt(env.CONSTRUCT_DB_POOL_SIZE || '5', 10),
17
27
  idle_timeout: Number.parseInt(env.CONSTRUCT_DB_IDLE_TIMEOUT_MS || '30000', 10),
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Mirrors the telemetry backup pattern in service-manager.mjs. Entry points:
6
6
  * - construct down → stashConstructDb (dump before container stop)
7
- * - construct setup → restoreConstructDb (reload after fresh container start)
7
+ * - construct init → restoreConstructDb (reload after fresh container start)
8
8
  *
9
9
  * Dumps are stored in ~/.construct/backups/postgres/ as pg_dump custom-format
10
10
  * files. The N most recent are kept; older ones are pruned automatically.