forge-openclaw-plugin 0.2.92 → 0.2.93
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.
- package/dist/server/server/migrations/064_health_workout_time_series_identity.sql +161 -0
- package/dist/server/server/src/app.js +8 -0
- package/dist/server/server/src/health-workout-analytics.js +8 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server/migrations/064_health_workout_time_series_identity.sql +161 -0
- package/skills/forge-openclaw/SKILL.md +14 -0
- package/skills/forge-openclaw/entity_conversation_playbooks.md +19 -0
- package/skills/forge-openclaw/psyche_entity_playbooks.md +35 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
CREATE TEMP TABLE health_workout_time_series_dedupe AS
|
|
2
|
+
SELECT
|
|
3
|
+
workout_id,
|
|
4
|
+
metric_key,
|
|
5
|
+
source_sample_uid,
|
|
6
|
+
MIN(rowid) AS survivor_rowid,
|
|
7
|
+
(
|
|
8
|
+
SELECT latest.rowid
|
|
9
|
+
FROM health_workout_time_series AS latest
|
|
10
|
+
WHERE latest.workout_id = grouped.workout_id
|
|
11
|
+
AND latest.metric_key = grouped.metric_key
|
|
12
|
+
AND latest.source_sample_uid = grouped.source_sample_uid
|
|
13
|
+
ORDER BY latest.updated_at DESC, latest.created_at DESC, latest.rowid DESC
|
|
14
|
+
LIMIT 1
|
|
15
|
+
) AS latest_rowid,
|
|
16
|
+
(
|
|
17
|
+
SELECT latest.series_index
|
|
18
|
+
FROM health_workout_time_series AS latest
|
|
19
|
+
WHERE latest.workout_id = grouped.workout_id
|
|
20
|
+
AND latest.metric_key = grouped.metric_key
|
|
21
|
+
AND latest.source_sample_uid = grouped.source_sample_uid
|
|
22
|
+
ORDER BY latest.updated_at DESC, latest.created_at DESC, latest.rowid DESC
|
|
23
|
+
LIMIT 1
|
|
24
|
+
) AS latest_series_index
|
|
25
|
+
FROM health_workout_time_series AS grouped
|
|
26
|
+
WHERE source_sample_uid IS NOT NULL
|
|
27
|
+
AND source_sample_uid != ''
|
|
28
|
+
GROUP BY workout_id, metric_key, source_sample_uid
|
|
29
|
+
HAVING COUNT(*) > 1;
|
|
30
|
+
|
|
31
|
+
UPDATE health_workout_time_series
|
|
32
|
+
SET
|
|
33
|
+
label = (
|
|
34
|
+
SELECT latest.label
|
|
35
|
+
FROM health_workout_time_series AS latest
|
|
36
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
37
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
38
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
39
|
+
),
|
|
40
|
+
category = (
|
|
41
|
+
SELECT latest.category
|
|
42
|
+
FROM health_workout_time_series AS latest
|
|
43
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
44
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
45
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
46
|
+
),
|
|
47
|
+
unit = (
|
|
48
|
+
SELECT latest.unit
|
|
49
|
+
FROM health_workout_time_series AS latest
|
|
50
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
51
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
52
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
53
|
+
),
|
|
54
|
+
value = (
|
|
55
|
+
SELECT latest.value
|
|
56
|
+
FROM health_workout_time_series AS latest
|
|
57
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
58
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
59
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
60
|
+
),
|
|
61
|
+
started_at = (
|
|
62
|
+
SELECT latest.started_at
|
|
63
|
+
FROM health_workout_time_series AS latest
|
|
64
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
65
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
66
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
67
|
+
),
|
|
68
|
+
ended_at = (
|
|
69
|
+
SELECT latest.ended_at
|
|
70
|
+
FROM health_workout_time_series AS latest
|
|
71
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
72
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
73
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
74
|
+
),
|
|
75
|
+
source_device = (
|
|
76
|
+
SELECT latest.source_device
|
|
77
|
+
FROM health_workout_time_series AS latest
|
|
78
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
79
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
80
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
81
|
+
),
|
|
82
|
+
source_bundle_identifier = (
|
|
83
|
+
SELECT latest.source_bundle_identifier
|
|
84
|
+
FROM health_workout_time_series AS latest
|
|
85
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
86
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
87
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
88
|
+
),
|
|
89
|
+
source_product_type = (
|
|
90
|
+
SELECT latest.source_product_type
|
|
91
|
+
FROM health_workout_time_series AS latest
|
|
92
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
93
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
94
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
95
|
+
),
|
|
96
|
+
capture_method = (
|
|
97
|
+
SELECT latest.capture_method
|
|
98
|
+
FROM health_workout_time_series AS latest
|
|
99
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
100
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
101
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
102
|
+
),
|
|
103
|
+
quality_flags_json = (
|
|
104
|
+
SELECT latest.quality_flags_json
|
|
105
|
+
FROM health_workout_time_series AS latest
|
|
106
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
107
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
108
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
109
|
+
),
|
|
110
|
+
metadata_json = (
|
|
111
|
+
SELECT latest.metadata_json
|
|
112
|
+
FROM health_workout_time_series AS latest
|
|
113
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
114
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
115
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
116
|
+
),
|
|
117
|
+
provenance_json = (
|
|
118
|
+
SELECT latest.provenance_json
|
|
119
|
+
FROM health_workout_time_series AS latest
|
|
120
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
121
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
122
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
123
|
+
),
|
|
124
|
+
updated_at = (
|
|
125
|
+
SELECT latest.updated_at
|
|
126
|
+
FROM health_workout_time_series AS latest
|
|
127
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
128
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
129
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
130
|
+
)
|
|
131
|
+
WHERE rowid IN (
|
|
132
|
+
SELECT survivor_rowid
|
|
133
|
+
FROM health_workout_time_series_dedupe
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
DELETE FROM health_workout_time_series
|
|
137
|
+
WHERE rowid IN (
|
|
138
|
+
SELECT duplicate.rowid
|
|
139
|
+
FROM health_workout_time_series AS duplicate
|
|
140
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
141
|
+
ON dedupe.workout_id = duplicate.workout_id
|
|
142
|
+
AND dedupe.metric_key = duplicate.metric_key
|
|
143
|
+
AND dedupe.source_sample_uid = duplicate.source_sample_uid
|
|
144
|
+
WHERE duplicate.rowid != dedupe.survivor_rowid
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
UPDATE health_workout_time_series
|
|
148
|
+
SET series_index = (
|
|
149
|
+
SELECT dedupe.latest_series_index
|
|
150
|
+
FROM health_workout_time_series_dedupe AS dedupe
|
|
151
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
152
|
+
)
|
|
153
|
+
WHERE rowid IN (
|
|
154
|
+
SELECT survivor_rowid
|
|
155
|
+
FROM health_workout_time_series_dedupe
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
DROP TABLE health_workout_time_series_dedupe;
|
|
159
|
+
|
|
160
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_health_workout_time_series_sample_identity
|
|
161
|
+
ON health_workout_time_series(workout_id, metric_key, source_sample_uid);
|
|
@@ -3060,6 +3060,8 @@ const AGENT_ONBOARDING_CONVERSATION_RULES = [
|
|
|
3060
3060
|
"For direct update or review requests, the next question should usually narrow the saved object, timeframe, or route family instead of reopening the whole meaning-making arc.",
|
|
3061
3061
|
"For updates, start with the smallest thing that now feels wrong, newly true, or newly visible rather than restarting the whole story.",
|
|
3062
3062
|
"For review requests, ask what practical question the user wants the read to answer before you ask for more scope.",
|
|
3063
|
+
"Treat userId, owner, and human/bot assignees as accountability and scope, not as opening form fields. Ask whose record or owner scope matters only when it changes visibility, review results, collaboration, automation behavior, or later filtering.",
|
|
3064
|
+
"For read and overview requests, ask for human or bot user scope only when the answer would meaningfully differ across owners; otherwise keep the next question focused on the user's practical question.",
|
|
3063
3065
|
"The opening question should help the user understand what they are actually trying to save, decide, review, or change, not make them perform the schema out loud.",
|
|
3064
3066
|
"If the user already named the exact correction in usable language, confirm only the missing scope, timing, or route-selecting detail that still matters, then act.",
|
|
3065
3067
|
"Keep API and architecture nouns out of user-facing questions unless the user asks about implementation. Do not ask the user about surfaces, route families, CRUD, payloads, mutation paths, or read paths; ask about the human object such as a wiki page, note, trigger report, behavior pattern, belief, mode, movement timeline, energy model, weekday pattern, flow, run, or node result.",
|
|
@@ -5268,8 +5270,12 @@ function buildAgentOnboardingPayload(request) {
|
|
|
5268
5270
|
movementTripDetail: '{"routeKey":"tripDetail","pathParams":{"id":"trip_123"}}',
|
|
5269
5271
|
movementSettings: '{"routeKey":"settings","query":{"userIds":["user_operator"]}}',
|
|
5270
5272
|
movementSettingsUpdate: '{"routeKey":"settingsUpdate","body":{"trackingEnabled":true,"publishMode":"draft_review","retentionMode":"aggregates_only"}}',
|
|
5273
|
+
movementPlaceCreate: '{"routeKey":"placeCreate","body":{"label":"Home","centerLat":46.2044,"centerLon":6.1432,"radiusMeters":120,"userId":"user_operator","note":"Primary home boundary for future time-in-place reads."}}',
|
|
5274
|
+
movementPlaceUpdate: '{"routeKey":"placeUpdate","pathParams":{"id":"place_home"},"body":{"label":"Home office","radiusMeters":90,"note":"Tighten the boundary so clinic visits do not count as home."}}',
|
|
5271
5275
|
movementMissingStayPreflight: '{"routeKey":"userBoxPreflight","body":{"kind":"stay","startedAt":"2026-05-06T13:00:00.000Z","endedAt":"2026-05-06T15:00:00.000Z","placeLabel":"Home","userId":"user_operator"}}',
|
|
5272
5276
|
movementMissingStayCreate: '{"routeKey":"userBoxCreate","body":{"kind":"stay","startedAt":"2026-05-06T13:00:00.000Z","endedAt":"2026-05-06T15:00:00.000Z","placeLabel":"Home","userId":"user_operator","note":"Manual correction after reviewing the timeline."}}',
|
|
5277
|
+
movementUserBoxUpdate: '{"routeKey":"userBoxUpdate","pathParams":{"id":"box_manual_123"},"body":{"endedAt":"2026-05-06T15:30:00.000Z","note":"Extended after checking the timeline detail."}}',
|
|
5278
|
+
movementUserBoxDelete: '{"routeKey":"userBoxDelete","pathParams":{"id":"box_manual_123"}}',
|
|
5273
5279
|
lifeForceOverview: '{"routeKey":"overview"}',
|
|
5274
5280
|
lifeForceProfile: '{"routeKey":"profile","body":{"baselineDailyAp":24,"recoveryNotes":"Clinic-admin days need a lower expected afternoon load."}}',
|
|
5275
5281
|
lifeForceWeekdayTemplate: '{"routeKey":"weekdayTemplate","pathParams":{"weekday":"monday"},"body":{"points":[{"hour":13,"freeAp":-4}]}}',
|
|
@@ -5280,6 +5286,8 @@ function buildAgentOnboardingPayload(request) {
|
|
|
5280
5286
|
workbenchUpdateFlow: '{"routeKey":"updateFlow","pathParams":{"id":"flow_research_digest"},"body":{"description":"Keep the same input contract but add a stronger evidence-check node."}}',
|
|
5281
5287
|
workbenchDeleteFlow: '{"routeKey":"deleteFlow","pathParams":{"id":"flow_research_digest"}}',
|
|
5282
5288
|
workbenchRunDetail: '{"routeKey":"runDetail","pathParams":{"id":"flow_research_digest","runId":"run_123"}}',
|
|
5289
|
+
workbenchRunNodes: '{"routeKey":"runNodes","pathParams":{"id":"flow_research_digest","runId":"run_123"}}',
|
|
5290
|
+
workbenchNodeResult: '{"routeKey":"nodeResult","pathParams":{"id":"flow_research_digest","runId":"run_123","nodeId":"node_summary"}}',
|
|
5283
5291
|
workbenchPublishedOutput: '{"routeKey":"publishedOutput","pathParams":{"id":"flow_research_digest"}}',
|
|
5284
5292
|
workbenchLatestNodeOutput: '{"routeKey":"latestNodeOutput","pathParams":{"id":"flow_research_digest","nodeId":"node_summary"}}',
|
|
5285
5293
|
workbenchRunFlow: '{"routeKey":"runFlow","pathParams":{"id":"flow_research_digest"},"body":{"input":{"topic":"question flow quality"}}}',
|
|
@@ -365,9 +365,15 @@ export function upsertWorkoutTimeSeries(input) {
|
|
|
365
365
|
quality_flags_json, metadata_json, provenance_json, created_at, updated_at
|
|
366
366
|
)
|
|
367
367
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
368
|
-
ON CONFLICT(workout_id, metric_key, source_sample_uid
|
|
369
|
-
DO UPDATE SET
|
|
368
|
+
ON CONFLICT(workout_id, metric_key, source_sample_uid)
|
|
369
|
+
DO UPDATE SET series_index = excluded.series_index,
|
|
370
|
+
value = excluded.value, started_at = excluded.started_at,
|
|
370
371
|
ended_at = excluded.ended_at, source_device = excluded.source_device,
|
|
372
|
+
label = excluded.label,
|
|
373
|
+
category = excluded.category,
|
|
374
|
+
unit = excluded.unit,
|
|
375
|
+
source_bundle_identifier = excluded.source_bundle_identifier,
|
|
376
|
+
source_product_type = excluded.source_product_type,
|
|
371
377
|
capture_method = excluded.capture_method,
|
|
372
378
|
quality_flags_json = excluded.quality_flags_json,
|
|
373
379
|
metadata_json = excluded.metadata_json,
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "forge-openclaw-plugin",
|
|
3
3
|
"name": "Forge",
|
|
4
4
|
"description": "Curated OpenClaw adapter for the Forge collaboration API, UI entrypoint, and localhost auto-start runtime.",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.93",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onStartup": true,
|
|
8
8
|
"onCapabilities": [
|
package/package.json
CHANGED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
CREATE TEMP TABLE health_workout_time_series_dedupe AS
|
|
2
|
+
SELECT
|
|
3
|
+
workout_id,
|
|
4
|
+
metric_key,
|
|
5
|
+
source_sample_uid,
|
|
6
|
+
MIN(rowid) AS survivor_rowid,
|
|
7
|
+
(
|
|
8
|
+
SELECT latest.rowid
|
|
9
|
+
FROM health_workout_time_series AS latest
|
|
10
|
+
WHERE latest.workout_id = grouped.workout_id
|
|
11
|
+
AND latest.metric_key = grouped.metric_key
|
|
12
|
+
AND latest.source_sample_uid = grouped.source_sample_uid
|
|
13
|
+
ORDER BY latest.updated_at DESC, latest.created_at DESC, latest.rowid DESC
|
|
14
|
+
LIMIT 1
|
|
15
|
+
) AS latest_rowid,
|
|
16
|
+
(
|
|
17
|
+
SELECT latest.series_index
|
|
18
|
+
FROM health_workout_time_series AS latest
|
|
19
|
+
WHERE latest.workout_id = grouped.workout_id
|
|
20
|
+
AND latest.metric_key = grouped.metric_key
|
|
21
|
+
AND latest.source_sample_uid = grouped.source_sample_uid
|
|
22
|
+
ORDER BY latest.updated_at DESC, latest.created_at DESC, latest.rowid DESC
|
|
23
|
+
LIMIT 1
|
|
24
|
+
) AS latest_series_index
|
|
25
|
+
FROM health_workout_time_series AS grouped
|
|
26
|
+
WHERE source_sample_uid IS NOT NULL
|
|
27
|
+
AND source_sample_uid != ''
|
|
28
|
+
GROUP BY workout_id, metric_key, source_sample_uid
|
|
29
|
+
HAVING COUNT(*) > 1;
|
|
30
|
+
|
|
31
|
+
UPDATE health_workout_time_series
|
|
32
|
+
SET
|
|
33
|
+
label = (
|
|
34
|
+
SELECT latest.label
|
|
35
|
+
FROM health_workout_time_series AS latest
|
|
36
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
37
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
38
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
39
|
+
),
|
|
40
|
+
category = (
|
|
41
|
+
SELECT latest.category
|
|
42
|
+
FROM health_workout_time_series AS latest
|
|
43
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
44
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
45
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
46
|
+
),
|
|
47
|
+
unit = (
|
|
48
|
+
SELECT latest.unit
|
|
49
|
+
FROM health_workout_time_series AS latest
|
|
50
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
51
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
52
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
53
|
+
),
|
|
54
|
+
value = (
|
|
55
|
+
SELECT latest.value
|
|
56
|
+
FROM health_workout_time_series AS latest
|
|
57
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
58
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
59
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
60
|
+
),
|
|
61
|
+
started_at = (
|
|
62
|
+
SELECT latest.started_at
|
|
63
|
+
FROM health_workout_time_series AS latest
|
|
64
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
65
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
66
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
67
|
+
),
|
|
68
|
+
ended_at = (
|
|
69
|
+
SELECT latest.ended_at
|
|
70
|
+
FROM health_workout_time_series AS latest
|
|
71
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
72
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
73
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
74
|
+
),
|
|
75
|
+
source_device = (
|
|
76
|
+
SELECT latest.source_device
|
|
77
|
+
FROM health_workout_time_series AS latest
|
|
78
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
79
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
80
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
81
|
+
),
|
|
82
|
+
source_bundle_identifier = (
|
|
83
|
+
SELECT latest.source_bundle_identifier
|
|
84
|
+
FROM health_workout_time_series AS latest
|
|
85
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
86
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
87
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
88
|
+
),
|
|
89
|
+
source_product_type = (
|
|
90
|
+
SELECT latest.source_product_type
|
|
91
|
+
FROM health_workout_time_series AS latest
|
|
92
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
93
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
94
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
95
|
+
),
|
|
96
|
+
capture_method = (
|
|
97
|
+
SELECT latest.capture_method
|
|
98
|
+
FROM health_workout_time_series AS latest
|
|
99
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
100
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
101
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
102
|
+
),
|
|
103
|
+
quality_flags_json = (
|
|
104
|
+
SELECT latest.quality_flags_json
|
|
105
|
+
FROM health_workout_time_series AS latest
|
|
106
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
107
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
108
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
109
|
+
),
|
|
110
|
+
metadata_json = (
|
|
111
|
+
SELECT latest.metadata_json
|
|
112
|
+
FROM health_workout_time_series AS latest
|
|
113
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
114
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
115
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
116
|
+
),
|
|
117
|
+
provenance_json = (
|
|
118
|
+
SELECT latest.provenance_json
|
|
119
|
+
FROM health_workout_time_series AS latest
|
|
120
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
121
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
122
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
123
|
+
),
|
|
124
|
+
updated_at = (
|
|
125
|
+
SELECT latest.updated_at
|
|
126
|
+
FROM health_workout_time_series AS latest
|
|
127
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
128
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
129
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
130
|
+
)
|
|
131
|
+
WHERE rowid IN (
|
|
132
|
+
SELECT survivor_rowid
|
|
133
|
+
FROM health_workout_time_series_dedupe
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
DELETE FROM health_workout_time_series
|
|
137
|
+
WHERE rowid IN (
|
|
138
|
+
SELECT duplicate.rowid
|
|
139
|
+
FROM health_workout_time_series AS duplicate
|
|
140
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
141
|
+
ON dedupe.workout_id = duplicate.workout_id
|
|
142
|
+
AND dedupe.metric_key = duplicate.metric_key
|
|
143
|
+
AND dedupe.source_sample_uid = duplicate.source_sample_uid
|
|
144
|
+
WHERE duplicate.rowid != dedupe.survivor_rowid
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
UPDATE health_workout_time_series
|
|
148
|
+
SET series_index = (
|
|
149
|
+
SELECT dedupe.latest_series_index
|
|
150
|
+
FROM health_workout_time_series_dedupe AS dedupe
|
|
151
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
152
|
+
)
|
|
153
|
+
WHERE rowid IN (
|
|
154
|
+
SELECT survivor_rowid
|
|
155
|
+
FROM health_workout_time_series_dedupe
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
DROP TABLE health_workout_time_series_dedupe;
|
|
159
|
+
|
|
160
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_health_workout_time_series_sample_identity
|
|
161
|
+
ON health_workout_time_series(workout_id, metric_key, source_sample_uid);
|
|
@@ -116,9 +116,17 @@ Concrete route-key examples for internal use:
|
|
|
116
116
|
`{"routeKey":"settings","query":{"userIds":["user_operator"]}}`
|
|
117
117
|
- Movement settings update:
|
|
118
118
|
`{"routeKey":"settingsUpdate","body":{"trackingEnabled":true,"publishMode":"draft_review","retentionMode":"aggregates_only"}}`
|
|
119
|
+
- Movement known-place creation:
|
|
120
|
+
`{"routeKey":"placeCreate","body":{"label":"Home","centerLat":46.2044,"centerLon":6.1432,"radiusMeters":120,"userId":"user_operator","note":"Primary home boundary for future time-in-place reads."}}`
|
|
121
|
+
- Movement known-place update:
|
|
122
|
+
`{"routeKey":"placeUpdate","pathParams":{"id":"place_home"},"body":{"label":"Home office","radiusMeters":90,"note":"Tighten the boundary so clinic visits do not count as home."}}`
|
|
119
123
|
- Movement missing-stay correction:
|
|
120
124
|
first `{"routeKey":"userBoxPreflight","body":{"kind":"stay","startedAt":"2026-05-06T13:00:00.000Z","endedAt":"2026-05-06T15:00:00.000Z","placeLabel":"Home","userId":"user_operator"}}`,
|
|
121
125
|
then `{"routeKey":"userBoxCreate","body":{"kind":"stay","startedAt":"2026-05-06T13:00:00.000Z","endedAt":"2026-05-06T15:00:00.000Z","placeLabel":"Home","userId":"user_operator","note":"Manual correction after reviewing the timeline."}}`
|
|
126
|
+
- Movement saved-overlay update:
|
|
127
|
+
`{"routeKey":"userBoxUpdate","pathParams":{"id":"box_manual_123"},"body":{"endedAt":"2026-05-06T15:30:00.000Z","note":"Extended after checking the timeline detail."}}`
|
|
128
|
+
- Movement saved-overlay delete:
|
|
129
|
+
`{"routeKey":"userBoxDelete","pathParams":{"id":"box_manual_123"}}`
|
|
122
130
|
- Life Force overview:
|
|
123
131
|
`{"routeKey":"overview"}`
|
|
124
132
|
- Life Force profile edit:
|
|
@@ -139,6 +147,10 @@ Concrete route-key examples for internal use:
|
|
|
139
147
|
`{"routeKey":"deleteFlow","pathParams":{"id":"flow_research_digest"}}`
|
|
140
148
|
- Workbench run detail:
|
|
141
149
|
`{"routeKey":"runDetail","pathParams":{"id":"flow_research_digest","runId":"run_123"}}`
|
|
150
|
+
- Workbench run nodes:
|
|
151
|
+
`{"routeKey":"runNodes","pathParams":{"id":"flow_research_digest","runId":"run_123"}}`
|
|
152
|
+
- Workbench node result:
|
|
153
|
+
`{"routeKey":"nodeResult","pathParams":{"id":"flow_research_digest","runId":"run_123","nodeId":"node_summary"}}`
|
|
142
154
|
- Workbench published output:
|
|
143
155
|
`{"routeKey":"publishedOutput","pathParams":{"id":"flow_research_digest"}}`
|
|
144
156
|
- Workbench latest node output:
|
|
@@ -224,6 +236,7 @@ Entity conversation rule:
|
|
|
224
236
|
before you reopen create or update intake.
|
|
225
237
|
- When updating an entity, start with what is changing, what should stay true, and what prompted the update now.
|
|
226
238
|
- When enough is clear, briefly summarize what you heard in the user's own language before asking for the last missing structural detail.
|
|
239
|
+
- Treat `userId` and human/bot assignees as accountability and scope, not as opening form fields. Ask whose human or bot record it is only when ownership changes visibility, review scope, collaboration, automation behavior, or later filtering; for read requests, ask user scope only when the answer would differ across owners.
|
|
227
240
|
- The quick intake prompts later in this file are fallback checkpoints, not a script to read aloud.
|
|
228
241
|
|
|
229
242
|
Forge data location rule:
|
|
@@ -249,6 +262,7 @@ Psyche interview rule:
|
|
|
249
262
|
- After the first real answer, choose one follow-up lane at a time: situation, sequence, meaning, protection, cost, longing/value, or tentative name.
|
|
250
263
|
- Do not minimize functional analysis, trigger chains, behavior patterns, modes, beliefs, or schema themes. Once at least one concrete example is clear, offer one careful interpretive hypothesis when it would help the user understand the function, protection, cost, belief, mode, or schema theme.
|
|
251
264
|
- Phrase interpretive hypotheses as collaborative and testable, not as verdicts. A good hypothesis says what the reaction may be protecting, predicting, relieving, or costing, then asks whether that lands or needs correction.
|
|
265
|
+
- If several Psyche containers are plausible, do not ask the user to choose from a taxonomy menu first. Reflect the lived difference, offer one careful hypothesis when a concrete example is visible, then distinguish the options in plain language: one episode as a `trigger_report`, a recurring loop as a `behavior_pattern`, one repeated move as `behavior`, one sentence as `belief_entry`, a part-state as `mode_profile` or `mode_guide_session`, or reusable future-labeling as `event_type` or `emotion_definition`.
|
|
252
266
|
- For Psyche updates, start with what feels newly true, newly visible, or newly inaccurate, then ask what should stay true before changing the formulation.
|
|
253
267
|
- If a fresh episode is what made a Psyche update visible, anchor in that episode before renaming the durable belief, pattern, mode, or value.
|
|
254
268
|
- If the user says they want help understanding a Psyche issue before saving it, ask one orienting question first instead of jumping straight into a full interpretation, diagnosis-like label, save suggestion, replacement belief, or suggested title.
|
|
@@ -204,6 +204,25 @@ When placement matters, prefer one hierarchy-aware linking question that can
|
|
|
204
204
|
select or create the right goal, project, issue, or parent work item from the
|
|
205
205
|
same search-first flow.
|
|
206
206
|
|
|
207
|
+
## Owner And User-Scope Checkpoint
|
|
208
|
+
|
|
209
|
+
Most normal stored Forge entities can carry `userId`, and many planning records can
|
|
210
|
+
also carry human or bot assignees. Treat ownership as accountability and useful
|
|
211
|
+
visibility, not as the first field in the form.
|
|
212
|
+
|
|
213
|
+
- Do not open with "who owns this?" unless the user is explicitly delegating,
|
|
214
|
+
comparing human and bot work, or creating a record for someone else.
|
|
215
|
+
- Ask whose human or bot record it is only when ownership changes accountability,
|
|
216
|
+
visibility, review scope, automation behavior, or later filtering.
|
|
217
|
+
- For collaborative planning records, ask about assignees only after the outcome,
|
|
218
|
+
hierarchy placement, and owner are clear enough.
|
|
219
|
+
- For reviews and overviews, ask which user or owner scope matters only when the
|
|
220
|
+
answer would change across humans or bots.
|
|
221
|
+
- If the user's wording already names the owner or bot actor, use that as the
|
|
222
|
+
`userId` direction internally and ask only for any ambiguity that remains.
|
|
223
|
+
- When owner scope is irrelevant, stay with the entity's meaning, timing, route, or
|
|
224
|
+
links instead of adding an administrative question.
|
|
225
|
+
|
|
207
226
|
## Operation lane checkpoint
|
|
208
227
|
|
|
209
228
|
Use this before you choose an API path or ask for more structure.
|
|
@@ -251,6 +251,41 @@ short, tentative, and correctable.
|
|
|
251
251
|
episode, or durable wiki explanation. Do not flatten schema work into a loose
|
|
252
252
|
self-observation.
|
|
253
253
|
|
|
254
|
+
## Entity Contrast Check
|
|
255
|
+
|
|
256
|
+
Use this when the user's material could fit several Psyche records. Do not ask the
|
|
257
|
+
user to choose from a taxonomy menu before you have reflected what the material is
|
|
258
|
+
doing.
|
|
259
|
+
|
|
260
|
+
- Choose `trigger_report` when the important thing is one charged episode and the
|
|
261
|
+
useful record is the sequence of situation, feeling, meaning, action, and
|
|
262
|
+
consequence.
|
|
263
|
+
- Choose `behavior_pattern` when the same cue -> body/emotion -> meaning ->
|
|
264
|
+
behavior/urge -> payoff -> cost loop repeats across situations and needs a
|
|
265
|
+
functional analysis.
|
|
266
|
+
- Choose `behavior` when one recurring move is the main object, even if the wider
|
|
267
|
+
loop is not mapped yet.
|
|
268
|
+
- Choose `belief_entry` when the live center is a sentence, rule, prediction, or
|
|
269
|
+
self/other/world assumption.
|
|
270
|
+
- Choose `mode_profile` when a recurring part-state has a stable voice, posture,
|
|
271
|
+
job, fear, or burden.
|
|
272
|
+
- Choose `mode_guide_session` when the user is inside the reaction and needs guided
|
|
273
|
+
present-moment exploration before a durable mode, belief, or pattern is clear.
|
|
274
|
+
- Choose `event_type` or `emotion_definition` when the reusable category or feeling
|
|
275
|
+
label will make future trigger reports more precise.
|
|
276
|
+
|
|
277
|
+
If two containers are genuinely plausible, say the distinction in plain language
|
|
278
|
+
after a reflection. For example: "We could save this as the episode if the meeting
|
|
279
|
+
itself is what matters, or as the recurring loop if this same freeze-and-overwork
|
|
280
|
+
sequence keeps happening. Which would help you more right now?" Ask that only after
|
|
281
|
+
the lived example is clear enough that the choice is meaningful.
|
|
282
|
+
|
|
283
|
+
After one concrete example is visible, it is often helpful to offer one careful
|
|
284
|
+
hypothesis before the contrast question: what the reaction may be protecting, what it
|
|
285
|
+
predicts, what short-term relief it creates, and what it costs. If the user corrects
|
|
286
|
+
the hypothesis, revise it once and then choose the record shape from the corrected
|
|
287
|
+
meaning.
|
|
288
|
+
|
|
254
289
|
## Therapeutic Direction Check
|
|
255
290
|
|
|
256
291
|
Before each follow-up, quickly decide what therapeutic job the next question should do.
|