@isparling/engram-coach 0.1.0 → 0.2.0

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 (51) hide show
  1. package/README.md +85 -18
  2. package/SETUP.md +559 -0
  3. package/SKILL_PACK.md +75 -0
  4. package/analyses/catalog.md +257 -0
  5. package/analysis-tools/hrv-trend.ts +592 -0
  6. package/analysis-tools/migrate-structured-capture.ts +234 -0
  7. package/analysis-tools/race-context.ts +96 -0
  8. package/analysis-tools/stream-analyze.ts +1008 -0
  9. package/analysis-tools/tsb-predict.ts +117 -0
  10. package/capture-handler.ts +301 -0
  11. package/config.json.example +21 -0
  12. package/engram-coach-ambient-capture.ts +336 -0
  13. package/engram-coach-capture-types.ts +185 -0
  14. package/engram-coach-config.ts +268 -0
  15. package/engram-coach-domain.ts +7 -2
  16. package/engram-coach-keys.ts +189 -0
  17. package/engram-coach-materialization.ts +638 -0
  18. package/engram-coach-migration.ts +1078 -0
  19. package/engram-coach-pack.ts +17 -12
  20. package/engram-coach-presentation.ts +10 -1
  21. package/engram-coach-reconciliation.ts +305 -2
  22. package/engram-coach-structured-capture.ts +622 -0
  23. package/package.json +39 -6
  24. package/personas/aggressive-monitoring.md +121 -0
  25. package/personas/aggressive.json +85 -0
  26. package/personas/conservative-monitoring.md +133 -0
  27. package/personas/conservative.json +93 -0
  28. package/personas/polarized-monitoring.md +112 -0
  29. package/personas/polarized.json +72 -0
  30. package/personas/volume-monitoring.md +85 -0
  31. package/personas/volume.json +108 -0
  32. package/shared/retrieval.md +71 -0
  33. package/shared/setup.md +207 -0
  34. package/skills/.gitkeep +0 -0
  35. package/skills/adapt-plan/SKILL.md +263 -0
  36. package/skills/block-review/SKILL.md +275 -0
  37. package/skills/consult/SKILL.md +176 -0
  38. package/skills/intake/SKILL.md +315 -0
  39. package/skills/lactate-analyze/SKILL.md +230 -0
  40. package/skills/lessons-rollup/SKILL.md +196 -0
  41. package/skills/monitoring-rollup/SKILL.md +208 -0
  42. package/skills/race-analysis/SKILL.md +219 -0
  43. package/skills/season-retrospective/SKILL.md +200 -0
  44. package/skills/set-goal/SKILL.md +297 -0
  45. package/templates/base.md +55 -0
  46. package/templates/build-1.md +57 -0
  47. package/templates/build-2.md +62 -0
  48. package/templates/race-report.md +51 -0
  49. package/templates/race-specificity.md +62 -0
  50. package/templates/season-review.md +40 -0
  51. package/engram-coach-extractor.ts +0 -295
@@ -0,0 +1,230 @@
1
+ ---
2
+ name: lactate-analyze
3
+ description: Query lactate analysis results for an athlete. Use this when you need LT1/LT2/FTP/FTHR values and their confidence intervals for training decisions. Also use when analyzing spot test results or comparing to historical tests.
4
+ ---
5
+
6
+ # Lactate Analysis
7
+
8
+ ## Overview
9
+
10
+ This skill provides access to the lactate analysis subsystem for querying threshold values, FTP/FTHR estimates, and spot test analysis. Engram Coach uses this during Orient phase to inform training decisions.
11
+
12
+ ## Data Flow
13
+
14
+ ```
15
+ Prescription YAML (spot test orders)
16
+
17
+
18
+ Athlete performs test
19
+
20
+
21
+ lactate import (CLI)
22
+
23
+
24
+ Analysis Engine (multiple methods + CI)
25
+
26
+
27
+ Results stored in JSON
28
+
29
+
30
+ lactate query (CLI or API)
31
+
32
+
33
+ Engram Coach adapts training
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ### Import Ramp Test Data
39
+
40
+ ```bash
41
+ cd lactate
42
+ npm install
43
+ npm run build
44
+
45
+ # Import a ramp test from CSV
46
+ lactate import -f /path/to/test.csv -t ramp -d 2026-03-01 -s cycling
47
+
48
+ # CSV format:
49
+ # power,heartrate,lactate
50
+ # 150,120,1.2
51
+ # 175,135,1.5
52
+ # 200,150,2.1
53
+ # ...
54
+ ```
55
+
56
+ ### Query Latest Analysis
57
+
58
+ ```bash
59
+ lactate query --latest
60
+ lactate query --latest --compare # Compare to previous test
61
+ ```
62
+
63
+ ### Query via API (for Engram Coach integration)
64
+
65
+ ```typescript
66
+ import { createLactateAPI } from './lactate';
67
+
68
+ // Create API instance
69
+ const api = createLactateAPI('athlete-id', '/path/to/data/dir');
70
+
71
+ // Query latest analysis
72
+ const result = await api.query({ compareToPrevious: true });
73
+
74
+ console.log(result.currentAnalysis.lt2.power); // e.g., 235
75
+ console.log(result.currentAnalysis.ftp.value); // e.g., 248
76
+ console.log(result.currentAnalysis.lt2.ci80); // e.g., [228, 242]
77
+ console.log(result.comparison?.interpretation); // "LT2 improved by 10W"
78
+ ```
79
+
80
+ ## Spot Test Workflow
81
+
82
+ ### 1. Coach Orders Spot Test (in Prescription YAML)
83
+
84
+ ```yaml
85
+ sessions:
86
+ - week: 3
87
+ day: Thu
88
+ session_name: W3_SweetSpot
89
+ total_duration_min: 120
90
+ intervals:
91
+ - duration_min: 20
92
+ power_low_pct: 88
93
+ power_high_pct: 92
94
+ count: 3
95
+ recovery_min: 8
96
+ spot_tests:
97
+ - interval_ref: 2
98
+ sample_times_min: [10, 20]
99
+ reason: "Verify sweet spot intensity is below LT2"
100
+ ```
101
+
102
+ ### 2. Athlete Performs Test and Imports Results
103
+
104
+ ```bash
105
+ # After the session, athlete imports spot readings
106
+ lactate import -f spot_readings.csv -t spot -r "W3_SweetSpot-interval2"
107
+
108
+ # CSV format:
109
+ # power,heartrate,lactate
110
+ # 220,155,1.8
111
+ # 225,160,2.1
112
+ ```
113
+
114
+ ### 3. Analyze Spot Test
115
+
116
+ ```typescript
117
+ const spotAnalysis = await api.analyzeSpotTest(
118
+ [
119
+ { power: 220, heartrate: 155, lactate: 1.8 },
120
+ { power: 225, heartrate: 160, lactate: 2.1 }
121
+ ]
122
+ );
123
+
124
+ console.log(spotAnalysis.trend); // 'improving', 'stable', 'declining'
125
+ console.log(spotAnalysis.comparisonToBaseline.delta); // e.g., -0.5
126
+ ```
127
+
128
+ ## Output Format
129
+
130
+ ### LactateAnalysis Response
131
+
132
+ ```json
133
+ {
134
+ "athleteId": "default",
135
+ "testId": "abc-123",
136
+ "testType": "ramp",
137
+ "testDate": "2026-03-01",
138
+ "sport": "cycling",
139
+ "lt1": {
140
+ "power": 185,
141
+ "heartrate": 142,
142
+ "lactate": 1.8,
143
+ "ci80": [178, 192],
144
+ "ci95": [170, 198],
145
+ "method": "log-log",
146
+ "methodType": "primary"
147
+ },
148
+ "lt2": {
149
+ "power": 235,
150
+ "heartrate": 165,
151
+ "lactate": 4.2,
152
+ "ci80": [228, 242],
153
+ "ci95": [220, 250],
154
+ "method": "dmax",
155
+ "methodType": "primary"
156
+ },
157
+ "ftp": {
158
+ "value": 248,
159
+ "ci80": [240, 255],
160
+ "ci95": [235, 262],
161
+ "method": "lt2-derived"
162
+ },
163
+ "fthr": {
164
+ "value": 168,
165
+ "ci80": [162, 172],
166
+ "method": "lt2-hr"
167
+ },
168
+ "testQuality": "good",
169
+ "confidence": "high",
170
+ "methodsUsed": ["dmax", "obla_4", "log-log"],
171
+ "dataQualityNotes": []
172
+ }
173
+ ```
174
+
175
+ ## Analysis Methods
176
+
177
+ The analyzer runs multiple detection methods and provides confidence intervals:
178
+
179
+ | Method | Best For | Primary Target |
180
+ |--------|----------|----------------|
181
+ | Log-log | LT1 detection | LT1 |
182
+ | Dmax | Full curves | LT2 |
183
+ | Modified Dmax | Noisy data | LT2 |
184
+ | OBLA 4.0 | Fixed threshold | LT2 |
185
+ | OBLA 2.0 | Fixed threshold | LT1 |
186
+ | Baseline+ | Drift correction | LT1 |
187
+
188
+ **Confidence Intervals:**
189
+ - 80% CI: Likely range for training decisions
190
+ - 95% CI: Bounds for conservative planning
191
+ - Wider CI for marginal/poor data quality
192
+
193
+ ## Integration Points
194
+
195
+ ### During adapt-plan Orient Phase
196
+
197
+ 1. Check if lactate data exists: `await api.query()`
198
+ 2. If available, incorporate thresholds into signal synthesis:
199
+ - Use LT1 power for upper Z2 boundary
200
+ - Use LT2 power for threshold zone (Z4)
201
+ - Use FTP for training zones
202
+ - Reference confidence intervals for decision uncertainty
203
+ 3. If spot tests available, analyze trend
204
+
205
+ ### QMD Knowledge Integration
206
+
207
+ After significant analysis results (new baseline test), create a knowledge record:
208
+
209
+ ```
210
+ {coaching_docs_dir}/lactate/YYYY-MM-DD-ramp-analysis.md
211
+ ```
212
+
213
+ Include:
214
+ - LT1/LT2 power and HR with CIs
215
+ - FTP/FTHR values
216
+ - Comparison to previous test
217
+ - Methods used
218
+ - Data quality notes
219
+
220
+ ---
221
+
222
+ ## Error Handling
223
+
224
+ | Scenario | Response |
225
+ |----------|----------|
226
+ | No tests found | Return null, skip lactate analysis in orient |
227
+ | Only 1-2 points | Reduce confidence to medium/low, warn user |
228
+ | LT1 not detected | Use OBLA 2.0 as fallback |
229
+ | LT2 not detected | Use OBLA 4.0 as fallback |
230
+ | CI unavailable | Report point estimate only, note reduced confidence |
@@ -0,0 +1,196 @@
1
+ ---
2
+ name: lessons-rollup
3
+ description: Append calibration points to lessons-log.md, then propose claim records and re-render ATHLETE_PROFILE.md and COACH_PROFILE.md through the engram harness rollup gate. Auto-tails block-review, race-analysis, and season-retrospective; can also run standalone for re-curation. Requires config.json configured and a registered harness space.
4
+ ---
5
+
6
+ # Lessons Rollup
7
+
8
+ ## Overview
9
+
10
+ Rigid four-phase workflow for maintaining the athlete-lifetime learning record. Appends new calibration points to an append-only log, proposes claim records for the changes, and re-renders the `ATHLETE_PROFILE.md` and `COACH_PROFILE.md` working summaries that `adapt-plan`, `consult`, `block-review`, and `season-retrospective` read as reasoning context. Diffing, the approval gate, sequential application, and the qmd refresh/embedding pass belong to the harness; this skill supplies coaching judgment and nothing else.
11
+
12
+ **This skill is RIGID — phases execute in exact order. Do not skip, reorder, or combine phases.**
13
+
14
+ ## Workflow
15
+
16
+ ```dot
17
+ digraph lessons_rollup {
18
+ "Phase 1: Append to log" [shape=box];
19
+ "Phase 2: Propose candidates" [shape=box];
20
+ "Phase 3: rollup preview" [shape=box];
21
+ "approval_required?" [shape=diamond];
22
+ "Show diff to athlete" [shape=box];
23
+ "Approved?" [shape=diamond];
24
+ "Phase 3: rollup approve" [shape=box];
25
+ "Phase 4: Re-render profiles" [shape=box];
26
+ "Skip write" [shape=box];
27
+
28
+ "Phase 1: Append to log" -> "Phase 2: Propose candidates";
29
+ "Phase 2: Propose candidates" -> "Phase 3: rollup preview";
30
+ "Phase 3: rollup preview" -> "approval_required?";
31
+ "approval_required?" -> "Show diff to athlete" [label="yes"];
32
+ "approval_required?" -> "Phase 3: rollup approve" [label="no (additive)"];
33
+ "Show diff to athlete" -> "Approved?";
34
+ "Approved?" -> "Phase 3: rollup approve" [label="yes"];
35
+ "Approved?" -> "Skip write" [label="no"];
36
+ "Phase 3: rollup approve" -> "Phase 4: Re-render profiles";
37
+ }
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Invocation Modes
43
+
44
+ This skill is called in two modes:
45
+
46
+ **Auto-tail (called by other skills):** Caller passes `--source={tag}` (e.g., `block-review:example-build`, `race:example-endurance-event`, `season:2027-example-season`) and an optional explicit list of bullets to append. Non-additive diffs still gate on approval.
47
+
48
+ **Standalone (manual):** No `--source` flag. Skips Phase 1 (no append), runs Phase 2 only — re-curates the profile from the existing log. Use after manually editing `lessons-log.md` or after a persona change.
49
+
50
+ ---
51
+
52
+ ### Pre-Phase Setup _(no user input — run silently)_
53
+
54
+ Follow **`${CLAUDE_PLUGIN_ROOT}/shared/setup.md`** — the shared configuration
55
+ preamble (paths, config, profile, persona, athlete profile).
56
+
57
+ **Optional steps this skill declares:** none — config, persona, and athlete profile only
58
+
59
+ Do not proceed past a stop condition defined there.
60
+
61
+
62
+ ### Phase 1 — Append to log _(skipped in standalone mode)_
63
+
64
+ For each new bullet provided by the caller, append a new entry to `lessons-log.md` in this format:
65
+
66
+ ```markdown
67
+ ## {YYYY-MM-DD} — {source-tag}
68
+ - {bullet 1}
69
+ - {bullet 2}
70
+ - {bullet 3}
71
+ ```
72
+
73
+ Use today's date. Source tag is the exact value passed in (e.g., `race:gravel-classic-2026`).
74
+
75
+ The append is literal — never rewrite or merge with prior entries. Multiple invocations on the same day under the same source tag append multiple sections (the curation step in Phase 2 dedupes the working profile).
76
+
77
+ Announce: "Appended {N} bullets to lessons-log.md under {source-tag}."
78
+
79
+ ---
80
+
81
+ ### Phase 2 — Propose claim candidates
82
+
83
+ Read the entire `lessons-log.md`. The profile is no longer hand-written: it is a
84
+ **rendered artifact** produced from claim records under `{coaching_docs_dir}/claims/`.
85
+ This phase produces *candidates*; the harness owns diffing, approval, and writing.
86
+
87
+ Reason across all log entries to decide what should change:
88
+
89
+ 1. **Group by theme:** the space's configured theme vocabulary, read from
90
+ `{coaching_docs_dir}/pack-config.json`. Adding a theme is a configuration change,
91
+ not a prose change.
92
+
93
+ 2. **Dedupe with source merging:** when 2+ entries express the same pattern, propose one
94
+ claim carrying every source tag. A `supersede` candidate inherits the retired claim's
95
+ sources automatically — never restate them by hand.
96
+
97
+ 3. **Supersede contradicted entries:** when a newer entry contradicts an older one,
98
+ propose `disposition: "supersede"` naming the predecessor. The harness retires the
99
+ predecessor and keeps its trace; there is no `## Retired` section to maintain.
100
+
101
+ 4. **Persona-fit is special:** the bridge between data and persona-change decisions.
102
+ Write each as: pattern observed → does it match the active persona's expectations →
103
+ recommendation.
104
+
105
+ Emit one batch file of candidate envelopes:
106
+
107
+ ```json
108
+ { "schema_version": 0, "candidates": [ { "id": "...", "kind": "claim", "disposition": "new" } ] }
109
+ ```
110
+
111
+ ---
112
+
113
+ ### Phase 3 — Review and approve through the harness
114
+
115
+ The diff gate, the additive/non-additive classification, and the approval binding are the
116
+ harness's, not this skill's. Preview the batch:
117
+
118
+ ```bash
119
+ engram rollup preview --bullets {batch}.json
120
+ ```
121
+
122
+ Show the athlete the rendered diff. `approval_required: true` means the batch is
123
+ non-additive and needs an explicit yes. On approval, re-supply the same batch file with
124
+ the previewed hash:
125
+
126
+ ```bash
127
+ engram rollup approve --bullets {batch}.json --expect {rollup_hash}
128
+ ```
129
+
130
+ Approval is stateless and bound to the plan: if any record changed since the preview, the
131
+ approval is refused as `stale_approval` and nothing is written. A batch is fail-stop, not
132
+ atomic — earlier commits stand, and the response names the stopping candidate and every
133
+ untried one. Re-preview before retrying.
134
+
135
+ Never run `qmd update` or `qmd embed` here. The harness refreshes the bound collection and
136
+ runs exactly one embedding pass after any durable write, scoped to this space. A bare qmd
137
+ command indexes every collection on the machine and is prohibited.
138
+
139
+ ---
140
+
141
+ ### Phase 4 — Re-render the profiles
142
+
143
+ Both profiles are generated from the same claim records; neither is edited by hand.
144
+
145
+ ```bash
146
+ engram render --view athlete-profile --audience athlete \
147
+ --delivery profile-markdown --model orchestrator/manual
148
+ engram render --view athlete-profile --audience coach \
149
+ --delivery profile-markdown --model orchestrator/manual
150
+ ```
151
+
152
+ Write the athlete render to `{coaching_docs_dir}/ATHLETE_PROFILE.md` and the coach render
153
+ to `{coaching_docs_dir}/COACH_PROFILE.md`.
154
+
155
+ There is no privacy, visibility, or persona-fit split between these renders. The
156
+ presentation pack authorizes `athlete`, `coach`, and `self-coach` identically — every
157
+ active, retrieval-eligible engram-coach record, unfiltered — and projects the same facts,
158
+ uncertainty, actions, and recommendation IDs into all three. `ATHLETE_PROFILE.md` and
159
+ `COACH_PROFILE.md` are the same content under a different title. `self-coach` is that same
160
+ audience again, for an athlete acting as their own coach; this skill does not keep it as a
161
+ standing file, but it renders on demand identically to the other two:
162
+
163
+ ```bash
164
+ engram render --view athlete-profile --audience self-coach \
165
+ --delivery profile-markdown --model orchestrator/manual
166
+ ```
167
+
168
+ A fourth audience, `clinician`, is the only one that actually filters: monitoring
169
+ captures, lactate tests, and any record (workout adaptations included) carrying a
170
+ health-relevant training signal (HRV, resting HR, lactate threshold, sleep, stress,
171
+ illness, injury). It is rendered on demand rather than kept as a standing file, because a
172
+ doctor-prep summary is episodic:
173
+
174
+ ```bash
175
+ engram render --view athlete-profile --audience clinician \
176
+ --delivery profile-markdown --model orchestrator/manual
177
+ ```
178
+
179
+ The clinician render carries the same facts/uncertainty/actions shape as the other three,
180
+ narrowed to that health-relevant record set. There is no configurable clinical-theme list
181
+ and no dedicated persona-fit field: any authorized persona-fit record is rendered as a
182
+ normal record-derived fact.
183
+
184
+ ---
185
+
186
+ ## Key Constraints
187
+
188
+ | Rule | Detail |
189
+ |------|--------|
190
+ | Append-only log | `lessons-log.md` is never rewritten or pruned by this skill |
191
+ | Source tagging | Every log entry carries `source-tag`; the harness renders a claim's merged sources into the profile, so tags are never typed by hand |
192
+ | Non-additive gate | Enforced by `rollup preview`/`approve`, not by this skill; approval is bound to the previewed plan and refused as `stale_approval` if anything moved |
193
+ | Standalone is read-only on log | Standalone mode skips Phase 1 — no append, only re-curation |
194
+ | Profiles are generated | `ATHLETE_PROFILE.md` and `COACH_PROFILE.md` are rendered artifacts; hand edits are overwritten by the next render. Change a claim record, not the file |
195
+ | Profiles are identical | `ATHLETE_PROFILE.md` and `COACH_PROFILE.md` carry the same record-derived content — the `athlete`, `coach`, and `self-coach` audiences authorize identically, so there is no private-vs-full split to reconcile |
196
+ | qmd is the harness's | Never run `qmd update`/`qmd embed`; a bare invocation indexes every collection on the machine |
@@ -0,0 +1,208 @@
1
+ ---
2
+ name: monitoring-rollup
3
+ description: Capture due monitoring-concern observations as typed Engram records and let the apply pipeline regenerate each concern's monitoring log and Doctor-Prep Summary. Runs in CONTRIBUTION mode (returns state_changes/events to a parent skill's single change set) or STANDALONE mode (its own preview → approval → apply). Requires config.json and a tracking/concerns.yaml registry.
4
+ ---
5
+
6
+ # Monitoring Rollup
7
+
8
+ ## Overview
9
+
10
+ Rigid phased workflow for longitudinal tracking of declared **monitoring
11
+ concerns** — chronic symptoms/issues tracked over time so an eventual
12
+ clinician visit is well-armed with data. Observations are captured as
13
+ structured Engram records; each concern's monitoring log and its curated
14
+ Doctor-Prep Summary are **generated compatibility views** that the apply
15
+ pipeline renders deterministically from those records.
16
+
17
+ **Generic:** all athlete-specific declarations live in
18
+ `{coaching_docs_dir}/tracking/concerns.yaml`. Each entry declares at minimum:
19
+ `id`, `active`, `cadence_days`, the concern's `log` view path, `fields`
20
+ (the controlled columns for entries), optional `record_negatives`, and
21
+ optional `escalation_triggers`. This skill contains no concern-specific
22
+ knowledge.
23
+
24
+ **This skill is RIGID — phases execute in exact order. Do not skip, reorder,
25
+ or combine phases.**
26
+
27
+ Monitoring logs and Doctor-Prep Summaries carry the byte-exact warning header
28
+ (`GENERATED FROM ENGRAM ACTIVE RECORDS. DO NOT EDIT DIRECTLY.`) and are never
29
+ edited directly by this skill or any other. The ONLY durable mutation path is
30
+ an approved capture plan applied through the tools.
31
+
32
+ ## Invocation Modes
33
+
34
+ Pick exactly one:
35
+
36
+ 1. **CONTRIBUTION MODE** — invoked by a parent skill (`consult`,
37
+ `adapt-plan`) during that parent's Phase 3. Read the registry, gather due
38
+ concerns, and **return typed monitoring items only**. Never previews,
39
+ never applies, never writes.
40
+ 2. **STANDALONE MODE** — manual invocation (athlete asks to "log monitoring"
41
+ or run a roll-up). Iterate all active concerns regardless of staleness and
42
+ run the full preview → approval → apply protocol itself.
43
+ 3. **RE-CURATE** — athlete asks to re-curate after reviewing data. Read-only
44
+ on records: no capture, no new items. Regeneration happens through
45
+ materialization only (see Re-curate below).
46
+
47
+ ---
48
+
49
+ ### Pre-Phase Setup _(no user input — run silently)_
50
+
51
+ Follow **`${CLAUDE_PLUGIN_ROOT}/shared/setup.md`** — the shared configuration
52
+ preamble (paths, config, profile, persona, athlete profile).
53
+
54
+ **Optional steps this skill declares:** MONITORING
55
+
56
+ Do not proceed past a stop condition defined there.
57
+
58
+ ### Phase 1 — Detect _(all modes except RE-CURATE)_
59
+
60
+ For each ACTIVE concern in the registry, find the most recent observation
61
+ date among its records (or its generated log view) and compute
62
+ `days_since = today − last_date`.
63
+
64
+ Mark a concern **due** when EITHER `days_since ≥ cadence_days`, OR the skill
65
+ is in STANDALONE mode.
66
+
67
+ Announce one line per concern:
68
+ `{name}: last logged {date} ({N}d ago) — {DUE | current}`.
69
+
70
+ If no concern is active or due, CONTRIBUTION MODE returns empty arrays to the
71
+ caller (see below); STANDALONE MODE exits cleanly as a no-op.
72
+
73
+ ### Phase 2 — Gather _(skipped in RE-CURATE; one question at a time)_
74
+
75
+ For each **due** concern (STANDALONE: all active), ask `concern.prompt`.
76
+ Capture structured values for `concern.fields`, using the log view's legend
77
+ as the soft vocabulary.
78
+
79
+ Branches:
80
+ - **Nothing to report** and `record_negatives: true` → record a negative
81
+ observation: date = today, status/note = "checked — asymptomatic".
82
+ - **Notable flare** → capture the full row; fold extra detail into the note.
83
+
84
+ **Ask one question at a time. Wait for each answer before asking the next.**
85
+
86
+ ---
87
+
88
+ ### Contribution Mode
89
+
90
+ Used by `consult` and `adapt-plan` inside their Phase 3. After Phase 2
91
+ gathering, convert every captured observation into typed items and **return
92
+ them to the caller** — nothing else. This mode never previews, never
93
+ applies, and never writes any file. Return two arrays:
94
+
95
+ 1. **Current state — one per due concern/signal pair** (`state_changes`):
96
+ - `entity_type`: `"monitoring"`
97
+ - `key_components`: `{ "concern_id": "<registry id>", "signal": "<field>" }`
98
+ — the pack derives the canonical key `monitoring:<concern-id>:<signal>`
99
+ - `effective_at`: the observation date
100
+ - `statement`: concise current-status sentence
101
+ - `details`: `{ concernId, signal, status, note }`
102
+
103
+ 2. **Observation history — one per captured observation** (`events`):
104
+ - `entity_type`: `"monitoring-event"`
105
+ - `effective_at`: the observation's effective time
106
+ - `statement`: what was observed
107
+ - `action_targets`: `[]`
108
+ - `details`: `{ concernId, signal, status?, note?, source, observedAt }`
109
+ where `source` is the caller tag (e.g. `consult:2026-06-17`,
110
+ `adapt:build-1-w3-thu`) and `observedAt` the effective time
111
+
112
+ These are **append-only monitoring events**: they never replace or supersede
113
+ anything. Only the keyed state item participates in supersession.
114
+
115
+ The parent merges both arrays into ITS OWN `StructuredChangeSet` BEFORE its
116
+ Phase 4 preview, so ONE plan hash and ONE approval cover the prescription
117
+ change, the coaching event, and all due monitoring changes together. If no
118
+ concern is active or due, return EMPTY arrays — the parent merges them and
119
+ continues with a single preview; no second preview is ever produced.
120
+
121
+ ---
122
+
123
+ ### Standalone Mode
124
+
125
+ #### Phase 3 — Preview
126
+
127
+ Assemble the gathered items into one `StructuredChangeSet` using exactly the
128
+ shapes documented under Contribution Mode (same `entity_type`, key
129
+ components, statement, and details contracts). Then call
130
+ `engram_capture_preview` with it.
131
+
132
+ If the preview returns blocked, STOP: Phase 4 cannot proceed until the input
133
+ is corrected and a preview succeeds.
134
+
135
+ #### Phase 4 — Approval _(requires explicit approval)_
136
+
137
+ Present TOGETHER, in one message:
138
+
139
+ 1. What was captured — per concern: the observation(s) and the resulting
140
+ current status.
141
+ 2. The record plan from the preview: which monitoring states will be created
142
+ or superseded, which events will append.
143
+ 3. The generated compatibility view paths that will regenerate (the concern
144
+ logs and the Doctor-Prep Summary).
145
+ 4. The exact `plan_hash` from the preview result.
146
+
147
+ Ask for approval. Approval must explicitly cover BOTH the recorded
148
+ observations AND the record/artifact plan identified by that exact
149
+ `plan_hash`.
150
+
151
+ Wait for explicit approval, rejection, or modification. On modification,
152
+ rebuild the change set, re-run `engram_capture_preview`, and present the new
153
+ hash.
154
+
155
+ #### Phase 5 — Apply _(with approved hash)_
156
+
157
+ Call `engram_capture_apply` with ONLY the exact approved `plan_hash`. Never
158
+ edit any file directly — the apply pipeline commits the records, refreshes
159
+ the guarded index, and regenerates every monitoring log view and the
160
+ Doctor-Prep Summary.
161
+
162
+ Outcome handling:
163
+
164
+ - **stale**: the records changed since preview. Return to Phase 3:
165
+ re-run `engram_capture_preview`, present the fresh plan and new
166
+ `plan_hash`, and obtain fresh approval before applying again.
167
+ - **apply failure**: stop. No view is written and none may be edited by
168
+ hand; diagnose and retry through the tools.
169
+ - **committed with stale views**: the records ARE authoritative. Re-calling
170
+ `engram_capture_apply` with the SAME committed `plan_hash` in the same
171
+ session reruns ONLY materialization — it never re-approves or re-applies
172
+ record mutations.
173
+ - **committed clean**: report the applied record IDs and regenerated paths.
174
+
175
+ #### Escalation scan _(both contribution and standalone, after capture)_
176
+
177
+ Evaluate each declared trigger in `escalation_triggers` generically against
178
+ the concern's recent observations: trend triggers flag when a numeric field
179
+ moves adversely across recent points (fewer than three parseable points → no
180
+ flag, never a false alarm); boolean/observed triggers flag when any recent
181
+ observation reports the condition. Surface any MET trigger prominently:
182
+ `⚠ {concern}: {trigger} — consider clinical evaluation.` The scan informs
183
+ the announcement only; the Doctor-Prep Summary itself is rendered
184
+ deterministically from the committed records.
185
+
186
+ ### RE-CURATE
187
+
188
+ Read-only on records. There is nothing to hand-edit: views regenerate from
189
+ records on every apply. If views are stale (e.g. a prior apply reported
190
+ stale artifacts), re-run `engram_capture_apply` with that session's last
191
+ committed `plan_hash` — materialization retries idempotently. Never rewrite
192
+ a log or summary by hand.
193
+
194
+ ---
195
+
196
+ ## Key Constraints
197
+
198
+ | Rule | Detail |
199
+ |------|--------|
200
+ | Generic skill | No concern-specific knowledge here; everything is driven by `concerns.yaml` |
201
+ | No-op safety | Absent/empty registry or nothing due → clean no-op; never blocks a calling skill |
202
+ | Contribution mode returns, never writes | Typed `state_changes`/`events` go back to the caller; no preview, no apply, no file writes |
203
+ | One approval | Parent-skill contributions merge into the parent's SINGLE change set — one preview, one `plan_hash` |
204
+ | Keyed state + append-only events | Due signals use `monitoring:<concern-id>:<signal>` for current state plus distinct append-only monitoring events carrying source and effective time |
205
+ | Generated views | Monitoring logs and Doctor-Prep Summaries are regenerated compatibility views; never edited directly |
206
+ | Record negatives | When `record_negatives: true`, "checked — asymptomatic" observations are logged (clinically meaningful) |
207
+ | One question at a time | Phase 2 never batches questions |
208
+ | Stale apply | Always returns to preview for a fresh hash and fresh approval |