@geraldmaron/construct 1.0.18 → 1.0.20

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 (93) hide show
  1. package/README.md +8 -4
  2. package/bin/construct +105 -3
  3. package/db/schema/003_observation_reconciliation.sql +14 -0
  4. package/lib/bootstrap/resources.mjs +0 -1
  5. package/lib/cli-commands.mjs +57 -6
  6. package/lib/comment-lint.mjs +44 -0
  7. package/lib/config/schema.mjs +31 -1
  8. package/lib/contracts/validate.mjs +106 -0
  9. package/lib/decisions/enforced-baseline.json +25 -0
  10. package/lib/decisions/golden.mjs +87 -0
  11. package/lib/decisions/precedence.mjs +46 -0
  12. package/lib/decisions/registry.mjs +469 -0
  13. package/lib/deployment/parity-contract.mjs +148 -0
  14. package/lib/document-ingest.mjs +71 -6
  15. package/lib/embed/cli.mjs +11 -0
  16. package/lib/embed/conflict-detection.mjs +4 -4
  17. package/lib/embed/customer-profiles.mjs +1 -1
  18. package/lib/embed/reconcile.mjs +60 -0
  19. package/lib/embedded-contract/capability.mjs +3 -3
  20. package/lib/embedded-contract/contract-version.mjs +1 -1
  21. package/lib/embedded-contract/execution.mjs +184 -0
  22. package/lib/embedded-contract/index.mjs +16 -0
  23. package/lib/embedded-contract/ingest.mjs +61 -7
  24. package/lib/gates-audit.mjs +2 -2
  25. package/lib/hooks/_lib/output-mode.mjs +101 -0
  26. package/lib/hooks/config-protection.mjs +22 -3
  27. package/lib/hooks/guard-bash.mjs +1 -1
  28. package/lib/hooks/session-start.mjs +13 -1
  29. package/lib/ingest/provider-extract.mjs +200 -0
  30. package/lib/ingest/strategy.mjs +95 -0
  31. package/lib/init-docs.mjs +1 -0
  32. package/lib/mcp/server.mjs +72 -4
  33. package/lib/mcp/tools/embedded-contract.mjs +17 -1
  34. package/lib/mode-commands.mjs +6 -8
  35. package/lib/observation-store.mjs +16 -2
  36. package/lib/opencode-telemetry.mjs +1 -1
  37. package/lib/orchestration/run-store.mjs +82 -0
  38. package/lib/orchestration/runtime.mjs +240 -0
  39. package/lib/roles/cli.mjs +10 -2
  40. package/lib/roles/gateway.mjs +50 -1
  41. package/lib/scheduler/index.mjs +31 -0
  42. package/lib/server/index.mjs +13 -3
  43. package/lib/server/static/index.html +1 -1
  44. package/lib/setup.mjs +6 -0
  45. package/lib/storage/hybrid-query.mjs +49 -38
  46. package/lib/storage/rrf.mjs +42 -0
  47. package/lib/storage/vector-client.mjs +18 -3
  48. package/lib/telemetry/backends/local.mjs +1 -1
  49. package/lib/telemetry/skill-calls.mjs +1 -1
  50. package/lib/templates/visual-requirements.mjs +84 -0
  51. package/package.json +10 -1
  52. package/rules/common/comments.md +3 -0
  53. package/rules/common/no-fabrication.md +3 -0
  54. package/rules/common/precedence.md +19 -0
  55. package/rules/common/research-sources.md +32 -0
  56. package/rules/common/research.md +59 -2
  57. package/skills/roles/data-engineer.pipeline.md +13 -1
  58. package/skills/roles/debugger.md +9 -0
  59. package/skills/roles/designer.accessibility.md +13 -3
  60. package/skills/roles/designer.md +10 -0
  61. package/skills/roles/engineer.platform.md +8 -0
  62. package/skills/roles/operator.md +10 -1
  63. package/skills/roles/operator.release.md +8 -0
  64. package/skills/roles/operator.sre.md +10 -1
  65. package/skills/roles/orchestrator.md +9 -2
  66. package/skills/roles/product-manager.business-strategy.md +10 -1
  67. package/skills/roles/researcher.explorer.md +12 -1
  68. package/skills/roles/researcher.ux.md +13 -1
  69. package/skills/roles/reviewer.devil-advocate.md +14 -2
  70. package/skills/roles/reviewer.evaluator.md +17 -4
  71. package/skills/roles/reviewer.trace.md +12 -1
  72. package/skills/roles/security.legal-compliance.md +8 -0
  73. package/skills/roles/security.md +11 -0
  74. package/specialists/contracts.json +18 -0
  75. package/specialists/prompts/cx-researcher.md +4 -2
  76. package/templates/docs/backlog-proposal.md +1 -1
  77. package/templates/docs/customer-profile.md +1 -1
  78. package/templates/docs/evidence-brief.md +5 -1
  79. package/templates/docs/incident-report.md +37 -21
  80. package/templates/docs/prfaq.md +2 -2
  81. package/templates/docs/product-intelligence-report.md +1 -1
  82. package/templates/docs/research-brief.md +8 -6
  83. package/templates/docs/research-finding.md +32 -7
  84. package/templates/docs/rfc.md +13 -1
  85. package/templates/docs/runbook.md +20 -1
  86. package/templates/docs/signal-brief.md +4 -1
  87. package/templates/docs/skill-artifact.md +27 -7
  88. package/templates/docs/strategy.md +23 -2
  89. package/lib/bootstrap/lazy-install.mjs +0 -161
  90. package/lib/embed/jobs/vector-sync.mjs +0 -198
  91. package/lib/knowledge/postgres-search.mjs +0 -132
  92. package/lib/services/pattern-promotion-service.mjs +0 -167
  93. package/lib/storage/unified-storage.mjs +0 -550
@@ -3,44 +3,60 @@
3
3
  - **Incident ID**: {INC-NNNN}
4
4
  - **Date**: {YYYY-MM-DD}
5
5
  - **Severity**: SEV-1 | SEV-2 | SEV-3
6
- - **Duration**: {start} → {end} ({total})
7
- - **Authors**: {names}
6
+ - **Duration**: {detection} → {all-clear} ({total}; time-to-detect {ttd}, time-to-mitigate {ttm})
7
+ - **Authors**: {names — include everyone who responded}
8
8
  - **Status**: draft | final
9
9
 
10
+ <!--
11
+ Blameless postmortem. Describe systems and decisions, not people. Neutral, factual
12
+ language — no dramatic or animated descriptions. Publish within days while detail is
13
+ fresh, and share widely. See Google SRE postmortem culture:
14
+ https://sre.google/workbook/postmortem-culture/
15
+ -->
16
+
10
17
  ## Summary
11
- <!-- Two to four sentences. What happened, who was affected, how it was resolved. Blameless tone. -->
18
+ <!-- Two to four sentences: what happened, who was affected, how it was resolved. Blameless tone. -->
19
+
20
+ ## Severity rationale
21
+ <!-- Why this severity and not one higher or lower? State the criteria (user impact, data integrity, duration, blast radius). If it was reclassified mid-incident, say when and why. -->
12
22
 
13
23
  ## Impact
14
- <!-- Users affected, requests failed, revenue lost, data integrity consequences. Quantify. -->
24
+ <!-- Quantify: users affected, requests failed, error rate, revenue, data-integrity consequences. "Unknown" is acceptable; a guess is not. -->
15
25
 
16
26
  ## Timeline
17
- <!-- Times in UTC. Key events: detection, diagnosis, mitigation, resolution, all-clear. One line per event. -->
27
+ <!-- Times in UTC, one line per event. Mark the key transitions explicitly: detection, diagnosis, mitigation start, resolution, all-clear. The gaps between them are the response story. -->
18
28
 
19
- | Time (UTC) | Event |
20
- |------------|-------|
21
- | {HH:MM} | {what happened or what was done} |
29
+ | Time (UTC) | Event | Phase |
30
+ |------------|-------|-------|
31
+ | {HH:MM} | {what happened or was done} | detection / diagnosis / mitigation / resolution |
32
+
33
+ ## Trigger
34
+ <!-- The proximate cause — the specific change, event, or input that set the incident off (a deploy, a traffic spike, a dependency failure). Distinct from the root cause. -->
22
35
 
23
36
  ## Root cause
24
- <!-- The underlying cause, not just the proximate trigger. The five-whys, condensed. -->
37
+ <!-- The underlying system condition that let the trigger cause harm. The five-whys, condensed. A root cause is a system/design gap, never a person. -->
25
38
 
26
39
  ## Contributing factors
27
- <!-- Conditions that made the incident possible, worse, or harder to resolve. Include process gaps, not just code. -->
40
+ <!-- Conditions that made the incident possible, worse, or harder to resolve: missing alerts, brittle dependencies, process gaps, absent runbook. Not the root cause, but they shaped the outcome. -->
28
41
 
29
- ## What went well
30
- <!-- Detection, response, communication, tooling. Keep this honest. -->
42
+ ## Mitigators
43
+ <!-- What went right and reduced blast radius — a circuit breaker that tripped, a canary that caught it, a fast rollback. These are as instructive as the failures; preserve them. -->
31
44
 
32
- ## What went poorly
33
- <!-- Be specific. Blameless, not blameless-to-the-point-of-vague. -->
45
+ ## Detection and response
46
+ <!-- How was it detected (alert, customer report, dashboard)? Was detection fast enough? What slowed diagnosis or mitigation? Be specific and blameless. -->
34
47
 
35
- ## Corrective actions
36
- <!-- Each action: owner, deadline, ticket link. Prefer systemic fixes over "be more careful". -->
48
+ ## Action items
49
+ <!-- Each action: systemic fix preferred over "be more careful". Owner, priority, and tracking ID required. Priority: P0 (prevents recurrence, do now) → P2 (hardening). -->
37
50
 
38
- | Action | Owner | Deadline | Ticket |
39
- |--------|-------|----------|--------|
40
- | {what} | {who} | {date} | {link} |
51
+ | Action | Type (prevent/detect/mitigate) | Owner | Priority | Tracking |
52
+ |--------|--------------------------------|-------|----------|----------|
53
+ | {what} | {prevent/detect/mitigate} | {who} | P0/P1/P2 | {bd or ticket} |
41
54
 
42
55
  ## Lessons learned
43
- <!-- What this incident revealed about the system or the organization. -->
56
+ <!-- Organized by theme. What did this reveal about the system or the organization that generalizes beyond this incident? -->
57
+
58
+ ## Glossary
59
+ <!-- Define domain-specific terms, service names, and acronyms used above so a reader outside the team can follow the report. -->
44
60
 
45
61
  ## References
46
- <!-- Logs, dashboards, traces, related incidents, PRs that caused or fixed the issue. -->
62
+ <!-- Logs, dashboards, traces (with IDs preserved), related incidents, the PRs that caused and that fixed the issue. -->
@@ -16,13 +16,13 @@ or directly from evidence, but it must not invent customer demand.
16
16
  <!-- Two or three paragraphs grounded in evidence. Explain the customer problem, why now, and what is missing today. -->
17
17
 
18
18
  ## Press release
19
- <!-- Write as if the capability has shipped. Focus on customer outcome, not implementation. -->
19
+ <!-- Write as if the capability has shipped. Focus on customer outcome, not implementation. Include a spokesperson quote and a customer quote — mark a hypothetical quote as such; never invent an attributed one (rules/common/no-fabrication.md). -->
20
20
 
21
21
  ## External FAQ
22
22
  <!-- Customer-facing questions and answers. Keep answers concrete and honest about limits. -->
23
23
 
24
24
  ## Internal FAQ
25
- <!-- Questions from engineering, sales, support, security, finance, and leadership. Include unknowns as TBD with what would resolve them. -->
25
+ <!-- Questions from engineering, sales, support, security, finance, and leadership. Include unknowns as TBD with what would resolve them. Cover the launch/success metrics: what target defines success and by when. -->
26
26
 
27
27
  ## Evidence appendix
28
28
  <!-- Source links, customer quotes, related issues, research, and PRDs. -->
@@ -13,7 +13,7 @@ without becoming a backlog dump.
13
13
  <!-- Two or three paragraphs. What changed, what matters, and what decision should follow. -->
14
14
 
15
15
  ## Evidence base
16
- <!-- Summary of source types, coverage, recency, and known gaps. -->
16
+ <!-- Source types, coverage, recency, and known gaps. Grade the evidence (rules/common/research.md §10); for community/sentiment sources note corroboration. Each theme below inherits the strength of the evidence under it — do not report a strong theme on weakly-graded sources. -->
17
17
 
18
18
  ## Themes
19
19
  <!-- Each theme should include evidence strength, affected personas, representative sources, and counter-signal. -->
@@ -23,14 +23,16 @@
23
23
 
24
24
  ## Sources
25
25
 
26
- | Title / Path | Class | Date | URL | Verified | Relevance |
27
- |---|---|---|---|---|---|
28
- | {source title or file path} | primary / secondary / tertiary | {YYYY-MM-DD} | {URL or path} | yes / no / n/a | {one-line} |
26
+ | Title / Path | Class | Reliability | Credibility | Date | URL | Verified | Relevance |
27
+ |---|---|---|---|---|---|---|---|
28
+ | {source title or file path} | primary / secondary / tertiary | A–F | 1–6 | {YYYY-MM-DD} | {URL or path} | yes / no / n/a | {one-line} |
29
29
 
30
- <!-- Class definitions:
31
- - primary: peer-reviewed papers, official docs at exact version, raw source code, standards, company announcements, SEC filings
30
+ <!-- Class is relative to the claim (rules/common/research.md §2): community/forum content is
31
+ primary for sentiment/experience claims, tertiary for factual ones.
32
+ - primary: peer-reviewed papers, official docs at exact version, raw source code, standards, company announcements, SEC filings; first-hand accounts for the attitude they express
32
33
  - secondary: changelogs, migration guides, tracked issues, maintainer posts, conference talks by authors
33
- - tertiary: blog posts, forums, Q&A, analyst summaries: for discovery only, never as evidence
34
+ - tertiary: blog posts, forums, Q&A, analyst summaries: for factual claims, discovery only
35
+ Reliability (A–F) and Credibility (1–6) are the Admiralty grade (research.md §10); record both, e.g. B2.
34
36
  Mark Verified = yes only after fetching the URL and confirming content matches. -->
35
37
 
36
38
  ## Findings
@@ -1,26 +1,51 @@
1
1
  ---
2
2
  kind: research-finding
3
3
  topic: "<short topic line>"
4
- confidence: inferred
4
+ confidence: high | medium | low
5
5
  sources: []
6
6
  created: <ISO timestamp set by construct knowledge add>
7
- expiresAt: <ISO timestamp, default created + 90d>
7
+ expiresAt: <ISO timestamp, default created + 90d; shorten for fast-moving topics>
8
8
  profile: <active profile id>
9
9
  ---
10
10
 
11
+ <!--
12
+ A lightweight but rigorous knowledge-store entry. Same evidence standard as a
13
+ research brief, condensed: observation separated from inference, every finding
14
+ cited, confidence tied to source grade, an explicit refresh trigger. See
15
+ rules/common/research.md.
16
+ -->
17
+
18
+ ## SOURCES
19
+
20
+ | Title / Path | Class | Reliability | Credibility | Date | URL | Verified |
21
+ |---|---|---|---|---|---|---|
22
+ | {source} | primary / secondary / tertiary | A–F | 1–6 | {YYYY-MM-DD} | {url} | yes / no |
23
+
11
24
  ## FINDINGS
12
25
 
13
- - <Finding 1 with citation>
14
- - <Finding 2 with citation>
26
+ <!-- Observation only: what the source states. Each finding cites a source row above. -->
27
+
28
+ - <Finding 1 [source: …]>
29
+ - <Finding 2 [source: …]>
15
30
 
16
31
  ## INFERENCES
17
32
 
18
- - <Inference labeled as such>
33
+ <!-- What is concluded beyond what any single source says. Labeled as inference, not fact. -->
34
+
35
+ - <Inference, with the findings it rests on>
36
+
37
+ ## CONFIDENCE
38
+
39
+ <!-- high / medium / low, with the reasoning: tie it to the Admiralty grade of the sources (research.md §10). high only on A1/A2/B1. Name the strongest counter-evidence. -->
19
40
 
20
41
  ## GAPS
21
42
 
22
- - <What we could not confirm>
43
+ - <What could not be confirmed and would change the conclusion if known>
23
44
 
24
45
  ## RECOMMENDATION
25
46
 
26
- - <Next action based on the evidence available>
47
+ - <Next action based on the evidence available, and the threshold that would change it>
48
+
49
+ ## REFRESH
50
+
51
+ <!-- When this finding should be re-verified: the expiresAt date, or the event that would invalidate it (a new release, a superseding paper). -->
@@ -22,7 +22,19 @@ contract, API, schema, or protocol consumed by other systems.
22
22
  <!-- What problem or limitation drives this proposal? Include evidence: incidents, performance data, support tickets, developer friction. Explain why the status quo is not acceptable. -->
23
23
 
24
24
  ## Proposed design
25
- <!-- The concrete proposal. Enough detail for reviewers to evaluate feasibility and tradeoffs. Diagrams, code sketches, and data models are appropriate here. -->
25
+ <!-- The concrete proposal. Enough detail for reviewers to evaluate feasibility and tradeoffs. The sequence below shows the proposed behavior in context — include the happy path and at least one error path. -->
26
+
27
+ ```mermaid
28
+ sequenceDiagram
29
+ participant Client
30
+ participant Service
31
+ participant Store
32
+ Client->>Service: request
33
+ Service->>Store: read or write
34
+ Store-->>Service: result
35
+ Service-->>Client: response
36
+ Note over Service,Store: error path — Store unavailable → Service returns degraded response
37
+ ```
26
38
 
27
39
  ## Tradeoffs and alternatives
28
40
  <!-- The other credible designs considered. For each: what it is, why it was not chosen, and under what conditions it would be preferred. No strawmen. -->
@@ -14,8 +14,27 @@
14
14
  ## Impact
15
15
  <!-- Who is affected and how badly. Data loss? Degraded performance? Complete outage? -->
16
16
 
17
+ ## Severity and response
18
+ <!-- Map each severity to the response it triggers: page urgency, comms cadence, and error-budget consequence. A SEV-1 pages immediately and freezes related releases per the error-budget policy (https://sre.google/workbook/error-budget-policy/); a SEV-3 is handled in business hours. -->
19
+
20
+ | Severity | Trigger condition | Page within | Comms | Error budget |
21
+ |----------|-------------------|-------------|-------|--------------|
22
+ | SEV-1 | {full outage / data loss / SLO breach} | 5 min | exec + status page | breach → freeze releases |
23
+ | SEV-2 | {major degradation, workaround exists} | 15 min | team + stakeholders | partial spend |
24
+ | SEV-3 | {minor / single-tenant / cosmetic} | business hours | team channel | none |
25
+
17
26
  ## Diagnostic steps
18
- <!-- Ordered checks from cheapest to most expensive. Each step: what to check, how to check it, what the answer means. -->
27
+ <!-- Ordered checks from cheapest to most expensive. Each step: what to check, how to check it, what the answer means. Keep the decision tree below in sync with the steps. -->
28
+
29
+ ```mermaid
30
+ flowchart TD
31
+ A[Alert fires] --> B{Error rate elevated?}
32
+ B -->|Yes| C[Check upstream dependency health]
33
+ B -->|No| D{User-visible impact?}
34
+ C --> E[Apply remediation]
35
+ D -->|Yes| C
36
+ D -->|No| F[Monitor and stand down]
37
+ ```
19
38
 
20
39
  ## Remediation
21
40
  <!-- The fix. Step-by-step, with exact commands or UI paths. Include expected output for each step. -->
@@ -13,7 +13,10 @@ without pretending the requirements are ready.
13
13
  <!-- One or two paragraphs. What appears to be happening, and where the signal came from. -->
14
14
 
15
15
  ## Evidence
16
- <!-- Cite the concrete notes, tickets, customer quotes, traces, or analytics that support the signal. -->
16
+ <!-- Cite the concrete notes, tickets, customer quotes, traces, or analytics. Grade each source with its Admiralty reliability/credibility (rules/common/research.md §10); for community sources note corroboration and engagement. The signal strength above should follow from the grades, not precede them. -->
17
+
18
+ ## Counter-signal
19
+ <!-- The strongest evidence against the signal, or its absence. A signal no one tried to disconfirm is weaker than it looks. -->
17
20
 
18
21
  ## Why it matters
19
22
  <!-- What product, customer, or business decision this could affect if the signal strengthens. -->
@@ -1,9 +1,11 @@
1
1
  ---
2
2
  name: <verb-led-id>
3
3
  scope: "<one paragraph: what it covers, what it does not>"
4
- observable_outcome: <how someone outside the role tells this skill happened>
4
+ observable_outcome: <how someone outside the role tells this skill happened — a concrete artifact, not "understands X">
5
+ prerequisites:
6
+ - <skill-id or capability that must exist before this one is useful>
5
7
  provenance:
6
- - <citation: post-mortem, public framework, competency model>
8
+ - <citation: post-mortem, public framework, competency model, with URL>
7
9
  roles:
8
10
  - <role-id that uses this skill>
9
11
  ---
@@ -12,16 +14,34 @@ roles:
12
14
 
13
15
  ## What this skill produces
14
16
 
15
- <Concrete output. Bloom-style: not "knows X" but "produces Y".>
17
+ <Concrete output, Bloom-style: not "knows X" but "produces Y". Name the artifact and what makes it correct. Example: "a runbook whose diagnostic flowchart branches to a fix in ≤3 steps", not "understands incident response".>
16
18
 
17
19
  ## When to invoke it
18
20
 
19
- <Triggers. What the operator or the upstream persona is doing when this skill is the right call.>
21
+ <Triggers. What the operator or upstream persona is doing when this skill is the right call. Include the signal that distinguishes it from adjacent skills.>
20
22
 
21
- ## How it composes
23
+ ## Prerequisites and composition
22
24
 
23
- <Other skills typically chained before or after.>
25
+ <What must be true or done first, and the skills typically chained before and after. A skill with unmet prerequisites produces confident-looking but wrong output.>
26
+
27
+ ## Competency rubric
28
+
29
+ <What the skill looks like at each level. The author of an artifact should be able to place their work on this scale.>
30
+
31
+ | Level | What it looks like |
32
+ |-------|--------------------|
33
+ | Novice | <produces the artifact but misses edge cases / evidence / failure paths> |
34
+ | Competent | <covers the happy path and the common failure modes; cites sources> |
35
+ | Expert | <anticipates the non-obvious failure, calibrates confidence, leaves the next person a reproducible trail> |
36
+
37
+ ## Failure modes
38
+
39
+ <How this skill breaks when misapplied — distinct from anti-patterns. What does a wrong-but-plausible output look like, and what signal reveals it? When should the operator escalate or stop?>
24
40
 
25
41
  ## Anti-patterns
26
42
 
27
- <Ways this skill gets misapplied; what looks similar but is not this.>
43
+ <Ways this skill gets misapplied; what looks similar but is not this skill.>
44
+
45
+ ## Worked example
46
+
47
+ <A short, concrete instance: the trigger, the artifact produced, and why it meets the observable outcome. One real example removes more ambiguity than a page of guidance.>
@@ -15,8 +15,8 @@ Owning workflow: skills/docs/strategy-workflow.md.
15
15
  <!-- 1–3 sentences. Where are we going in 3–5 years? -->
16
16
 
17
17
  ## Bets
18
- <!-- Explicit choices. Each bet: what we're doing and why. -->
19
- - **Bet 1**: …
18
+ <!-- Explicit choices. Each bet: what we're doing, why, the leading indicator that tells us early if it's working, and the kill criterion that ends it. A bet with no kill criterion is a belief, not a bet. -->
19
+ - **Bet 1**: … **Why**: … **Leading indicator**: … **Kill criterion**: {what observation would make us stop}
20
20
  - **Bet 2**: …
21
21
 
22
22
  ## Non-bets
@@ -29,6 +29,27 @@ Owning workflow: skills/docs/strategy-workflow.md.
29
29
  ## North Star Metric
30
30
  <!-- Single measurable outcome. Format: Metric / Baseline / Target / Owner -->
31
31
 
32
+ ## Metrics
33
+ <!-- The North Star is lagging. Name the leading indicators that move first and predict it, so a failing bet is visible before the lagging metric confirms it. -->
34
+
35
+ | Metric | Type (leading/lagging) | Baseline | Target | Owner |
36
+ |--------|------------------------|----------|--------|-------|
37
+ | {name} | leading / lagging | {value} | {value} | {who} |
38
+
39
+ ## Milestones
40
+ <!-- What must be true by when. Each milestone is a checkpoint that confirms or kills a bet. -->
41
+
42
+ | Milestone | By when | Confirms / kills |
43
+ |-----------|---------|------------------|
44
+ | {what} | {date} | {which bet} |
45
+
46
+ ## Risks
47
+ <!-- What could invalidate the strategy. Risk register, not a worry list. -->
48
+
49
+ | Risk | Likelihood | Impact | Mitigation / leading signal |
50
+ |------|-----------|--------|------------------------------|
51
+ | {what} | high/med/low | high/med/low | {response} |
52
+
32
53
  ## Competitive Positioning
33
54
  <!-- How we differentiate. 2–4 sentences. -->
34
55
 
@@ -1,161 +0,0 @@
1
- /**
2
- * lib/bootstrap/lazy-install.mjs — on-demand resource installer with consent gating.
3
- *
4
- * When a runtime code path needs a resource that is not present, call
5
- * `lazyInstall(id)`. The function:
6
- *
7
- * 1. Reads cached operator consent from config.env (consent key set by the
8
- * resource definition in `lib/bootstrap/resources.mjs`).
9
- * 2. If consent is 'never', returns { installed: false, fallback: <text> }
10
- * without prompting or printing anything.
11
- * 3. If a TTY is available, prompts the user with the resource displayName,
12
- * estimated download size, and [y/n/never].
13
- * 4. On 'y' or an existing 'yes' consent: calls resource.install(), waits
14
- * for it to complete, updates config.env, and returns { installed: true }.
15
- * 5. On 'n': returns { installed: false, fallback: <text> } without saving.
16
- * 6. On 'never': writes 'never' to config.env and returns { installed: false }.
17
- *
18
- * Hooks that run without a TTY (most hook invocations) skip to step 2 and
19
- * use the fallback silently.
20
- *
21
- * Consent is cached per-resource in config.env so the prompt never repeats
22
- * across process restarts once the user has answered.
23
- */
24
-
25
- import fs from 'node:fs';
26
- import path from 'node:path';
27
- import os from 'node:os';
28
- import readline from 'node:readline';
29
-
30
- import { getResource, probeResource } from './resources.mjs';
31
-
32
- const CONFIG_ENV = path.join(os.homedir(), '.construct', 'config.env');
33
-
34
- function readConfigEnv() {
35
- try {
36
- return fs.readFileSync(CONFIG_ENV, 'utf8');
37
- } catch {
38
- return '';
39
- }
40
- }
41
-
42
- function readConsentFromEnv(consentKey) {
43
- const lines = readConfigEnv().split('\n');
44
- for (const line of lines) {
45
- const trimmed = line.trim();
46
- if (!trimmed || trimmed.startsWith('#')) continue;
47
- const eq = trimmed.indexOf('=');
48
- if (eq === -1) continue;
49
- const k = trimmed.slice(0, eq).trim();
50
- const v = trimmed.slice(eq + 1).trim().replace(/^['"]|['"]$/g, '');
51
- if (k === consentKey) return v;
52
- }
53
- return null;
54
- }
55
-
56
- function writeConsentToEnv(consentKey, value) {
57
- let content = readConfigEnv();
58
- const regex = new RegExp(`^${consentKey}=.*$`, 'm');
59
- const line = `${consentKey}=${value}`;
60
- if (regex.test(content)) {
61
- content = content.replace(regex, line);
62
- } else {
63
- content = content.trimEnd() + (content ? '\n' : '') + line + '\n';
64
- }
65
- fs.mkdirSync(path.dirname(CONFIG_ENV), { recursive: true });
66
- fs.writeFileSync(CONFIG_ENV, content, { mode: 0o600 });
67
- }
68
-
69
- function hasTty() {
70
- return Boolean(process.stdin.isTTY && process.stderr.isTTY);
71
- }
72
-
73
- function formatBytes(bytes) {
74
- if (!bytes) return '';
75
- if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
76
- return `${Math.round(bytes / (1024 * 1024))} MB`;
77
- }
78
-
79
- async function promptUser(question) {
80
- return new Promise((resolve) => {
81
- const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
82
- rl.question(question, (answer) => {
83
- rl.close();
84
- resolve(answer.trim().toLowerCase());
85
- });
86
- });
87
- }
88
-
89
- /**
90
- * Attempt to install a resource lazily, respecting cached consent.
91
- *
92
- * @param {string} id - resource id from the registry
93
- * @param {object} [opts]
94
- * @param {boolean} [opts.silent] - skip TTY prompt, use cached consent only
95
- * @returns {{ installed: boolean, fallback?: string, error?: string }}
96
- */
97
- export async function lazyInstall(id, { silent = false } = {}) {
98
- const resource = getResource(id);
99
- if (!resource) {
100
- return { installed: false, error: `unknown resource: ${id}` };
101
- }
102
-
103
- const probe = await probeResource(resource);
104
- if (probe.present && probe.healthy) {
105
- return { installed: true };
106
- }
107
-
108
- const { consentKey, displayName, downloadSize, fallback } = resource;
109
- const fallbackMsg = typeof fallback === 'function' ? fallback() : (fallback || '');
110
-
111
- const cached = readConsentFromEnv(consentKey);
112
-
113
- if (cached === 'never') {
114
- return { installed: false, fallback: fallbackMsg };
115
- }
116
-
117
- if (cached === 'yes') {
118
- return await performInstall(resource, fallbackMsg);
119
- }
120
-
121
- if (silent || !hasTty()) {
122
- return { installed: false, fallback: fallbackMsg };
123
- }
124
-
125
- const size = downloadSize ? ` (~${formatBytes(downloadSize)})` : '';
126
- const answer = await promptUser(
127
- `\n[construct] ${displayName} required${size}. Install now? [y/n/never] `
128
- );
129
-
130
- if (answer === 'y' || answer === 'yes') {
131
- writeConsentToEnv(consentKey, 'yes');
132
- return await performInstall(resource, fallbackMsg);
133
- }
134
-
135
- if (answer === 'never') {
136
- writeConsentToEnv(consentKey, 'never');
137
- process.stderr.write(
138
- `[construct] "${displayName}" will not be prompted again. Using fallback: ${fallbackMsg}\n`
139
- );
140
- return { installed: false, fallback: fallbackMsg };
141
- }
142
-
143
- return { installed: false, fallback: fallbackMsg };
144
- }
145
-
146
- async function performInstall(resource, fallbackMsg) {
147
- if (typeof resource.install !== 'function') {
148
- return { installed: false, fallback: fallbackMsg, error: 'resource has no install()' };
149
- }
150
- try {
151
- process.stderr.write(`[construct] Installing ${resource.displayName}...\n`);
152
- const result = await resource.install();
153
- if (result?.success) {
154
- process.stderr.write(`[construct] ${resource.displayName} installed.\n`);
155
- return { installed: true };
156
- }
157
- return { installed: false, fallback: fallbackMsg, error: result?.log };
158
- } catch (err) {
159
- return { installed: false, fallback: fallbackMsg, error: err?.message || String(err) };
160
- }
161
- }