forge-openclaw-plugin 0.2.91 → 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 +21 -0
- package/dist/server/server/src/health-workout-analytics.js +8 -2
- package/dist/server/server/src/health.js +92 -0
- 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 +64 -23
- 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);
|
|
@@ -2960,6 +2960,7 @@ const AGENT_ONBOARDING_ENTITY_CATALOG = [
|
|
|
2960
2960
|
],
|
|
2961
2961
|
searchHints: [
|
|
2962
2962
|
"Clarify whether the user wants a behavioral query, one trip or place, a missing-gap overlay, a manual add or update, or a link before choosing the route.",
|
|
2963
|
+
"For known-place creation or cleanup, ask what label, boundary, and future use should make the place recognizable before calling the dedicated place route.",
|
|
2963
2964
|
"If the user already named a concrete missing span, confirm only the remaining time or place ambiguity, then use the movement overlay route and read the timeline back.",
|
|
2964
2965
|
"If the user wants to revise or remove an already-saved correction, identify whether it is a user-defined box, automatic box, recorded stay, recorded trip, or trip point before choosing the repair or delete route."
|
|
2965
2966
|
],
|
|
@@ -2975,6 +2976,7 @@ const AGENT_ONBOARDING_ENTITY_CATALOG = [
|
|
|
2975
2976
|
],
|
|
2976
2977
|
searchHints: [
|
|
2977
2978
|
"Clarify whether the user wants explanation, durable model changes, or a real-time tired or recovered signal before choosing the route.",
|
|
2979
|
+
"For profile or weekday-template edits, ask what future planning behavior should change, such as workload, recovery time, timeboxes, meeting load, or task choice.",
|
|
2978
2980
|
"Separate durable profile assumptions, weekday-template edits, and right-now fatigue signals before choosing the mutation path.",
|
|
2979
2981
|
"When the user is trying to understand the practical result of a change, read the overview again after the write instead of stopping at the mutation response."
|
|
2980
2982
|
],
|
|
@@ -2990,6 +2992,7 @@ const AGENT_ONBOARDING_ENTITY_CATALOG = [
|
|
|
2990
2992
|
],
|
|
2991
2993
|
searchHints: [
|
|
2992
2994
|
"Clarify whether the user wants flow discovery, editing, execution, published output, run inspection, or node-level output before choosing the route.",
|
|
2995
|
+
"For one-off execution, ask whether the input contract should stay temporary or become a reusable saved flow before creating anything durable.",
|
|
2993
2996
|
"Distinguish flow contract, published output, run history, latest-node-output, and chat follow-up questions before reaching for a route.",
|
|
2994
2997
|
"If the user is still deciding how to run or edit a flow, read flow detail or the box catalog before asking them for structured input details."
|
|
2995
2998
|
],
|
|
@@ -3057,6 +3060,8 @@ const AGENT_ONBOARDING_CONVERSATION_RULES = [
|
|
|
3057
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.",
|
|
3058
3061
|
"For updates, start with the smallest thing that now feels wrong, newly true, or newly visible rather than restarting the whole story.",
|
|
3059
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.",
|
|
3060
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.",
|
|
3061
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.",
|
|
3062
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.",
|
|
@@ -4776,6 +4781,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4776
4781
|
],
|
|
4777
4782
|
routeSelectionQuestions: [
|
|
4778
4783
|
"Is the user asking for a day, month, all-time, timeline, place, trip detail, selected-span, or settings answer?",
|
|
4784
|
+
"If this is known-place creation or cleanup, what label, boundary, or future-use distinction is still missing?",
|
|
4779
4785
|
"Is this a missing-gap overlay, a saved-overlay repair, or an edit to one already-recorded stay, trip, or trip point?",
|
|
4780
4786
|
"If this is about operating behavior, is the change about passive tracking, publish mode, retention, or companion readiness?",
|
|
4781
4787
|
"If the target is already known, what one time, place, or saved-object detail is still missing before acting?"
|
|
@@ -4837,6 +4843,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4837
4843
|
"Route-selection questions are internal. User-facing questions should ask for the useful time window, place, selected span, stay, or trip instead of reciting day/month/all-time/timeline/selection route keys.",
|
|
4838
4844
|
"Use /api/v1/movement/day, /month, /all-time, /timeline, or /selection when the user wants behavioral answers such as how long they stayed at home, when they traveled, which places dominated a period, or what happened across a selected span.",
|
|
4839
4845
|
"Use GET /api/v1/movement/settings and PATCH /api/v1/movement/settings when the user wants to inspect or change passive capture, publish mode, retention mode, or companion readiness. Do not route settings changes through stays, trips, places, or batch CRUD.",
|
|
4846
|
+
"For known-place creation or cleanup, ask for the place label, boundary, and future use, then use POST /api/v1/movement/places or PATCH /api/v1/movement/places/:id instead of tags or generic entity writes.",
|
|
4840
4847
|
"Use the movement write routes when the user wants to add a place or manual overlay, update a specific stay or trip, repair one recorded movement span, or attach movement context to another Forge record. If the user is filling a missing-data gap, the usual write path is a user-defined overlay box rather than a raw stay or trip patch.",
|
|
4841
4848
|
"If the user is revising or removing an existing correction, first identify whether the saved object is a user-defined box, automatic box, recorded stay, recorded trip, or trip point so the repair or delete path stays truthful.",
|
|
4842
4849
|
"For an explicit statement like 'that missing block was me staying home', do not reopen broad intake. Preflight only if timing overlap is unclear, then create a user-defined `stay` box for that interval and read the updated timeline back."
|
|
@@ -4849,6 +4856,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4849
4856
|
routeKeys: ["overview", "profile", "weekdayTemplate", "fatigueSignal"],
|
|
4850
4857
|
routeSelectionQuestions: [
|
|
4851
4858
|
"Is the user trying to understand the overview, change durable profile assumptions, change a weekday curve, or log a right-now fatigue signal?",
|
|
4859
|
+
"What planning decision should the overview or correction change: workload, recovery, timeboxes, meetings, or task choice?",
|
|
4852
4860
|
"Are they describing a repeatable weekly shape or a one-off current state?",
|
|
4853
4861
|
"If the lane is already clear, what one weekday, profile field, or signal detail is still missing?"
|
|
4854
4862
|
],
|
|
@@ -4871,6 +4879,8 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4871
4879
|
"Route-selection questions are internal. User-facing questions should ask whether this is a current read, durable assumption, repeated weekday rhythm, or right-now state instead of reciting overview/profile/template/signal route keys.",
|
|
4872
4880
|
"Use GET /api/v1/life-force for the current overview payload with stats, drains, recommendations, and current-curve state.",
|
|
4873
4881
|
"Patch the profile only for durable personal settings, update weekday templates only for the curve itself, and post fatigue signals for real-time tired or recovered observations.",
|
|
4882
|
+
"If the user only needs an explanation or planning read, use the overview first and do not turn the conversation into a profile or template mutation.",
|
|
4883
|
+
"For profile or weekday-template edits, ask what future planning behavior should change, such as workload, recovery time, timeboxes, meeting load, or task choice, so the write is not just a nicer description.",
|
|
4874
4884
|
"If the user says something like 'I always dip on Tuesdays after lunch', treat that as a weekday-template change rather than a one-off fatigue signal.",
|
|
4875
4885
|
"If the user is asking what changed after a profile, template, or fatigue write, read the overview back so the effect stays visible.",
|
|
4876
4886
|
"If the user already knows they want a profile change, weekday-template edit, or right-now fatigue signal, skip the broad lane question and ask only for the missing weekday, profile field, or signal detail."
|
|
@@ -4883,6 +4893,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4883
4893
|
routeKeys: ["overview", "profile", "weekdayTemplate", "fatigueSignal"],
|
|
4884
4894
|
routeSelectionQuestions: [
|
|
4885
4895
|
"Is the user trying to understand the overview, change durable profile assumptions, change a weekday curve, or log a right-now fatigue signal?",
|
|
4896
|
+
"What planning decision should the overview or correction change: workload, recovery, timeboxes, meetings, or task choice?",
|
|
4886
4897
|
"Are they describing a repeatable weekly shape or a one-off current state?",
|
|
4887
4898
|
"If the lane is already clear, what one weekday, profile field, or signal detail is still missing?"
|
|
4888
4899
|
],
|
|
@@ -4906,6 +4917,8 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4906
4917
|
"Route-selection questions are internal. User-facing questions should ask whether this is a current read, durable assumption, repeated weekday rhythm, or right-now state instead of reciting overview/profile/template/signal route keys.",
|
|
4907
4918
|
"Use GET /api/v1/life-force for the current overview payload with stats, drains, recommendations, and current-curve state.",
|
|
4908
4919
|
"Patch the profile only for durable personal settings, update weekday templates only for the curve itself, and post fatigue signals for real-time tired or recovered observations.",
|
|
4920
|
+
"If the user only needs an explanation or planning read, use the overview first and do not turn the conversation into a profile or template mutation.",
|
|
4921
|
+
"For profile or weekday-template edits, ask what future planning behavior should change, such as workload, recovery time, timeboxes, meeting load, or task choice, so the write is not just a nicer description.",
|
|
4909
4922
|
"If the user says something like 'I always dip on Tuesdays after lunch', treat that as a weekday-template change rather than a one-off fatigue signal.",
|
|
4910
4923
|
"If the user is asking what changed after a profile, template, or fatigue write, read the overview back so the effect stays visible.",
|
|
4911
4924
|
"If the user already knows they want a profile change, weekday-template edit, or right-now fatigue signal, skip the broad lane question and ask only for the missing weekday, profile field, or signal detail."
|
|
@@ -4935,6 +4948,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4935
4948
|
],
|
|
4936
4949
|
routeSelectionQuestions: [
|
|
4937
4950
|
"Is the job flow discovery, flow creation, flow editing, flow deletion, execution, run history, published output, run detail, node result, latest node output, or flow chat follow-up?",
|
|
4951
|
+
"If this is execution, is it a known saved flow, a one-off input run, or a flow that should become reusable?",
|
|
4938
4952
|
"Does the user need a stable public contract or one execution artifact?",
|
|
4939
4953
|
"For flow CRUD, what stable input contract, expected output, or lifecycle effect must stay true?",
|
|
4940
4954
|
"For flow chat follow-up, which saved flow should receive the message and what should the message accomplish?",
|
|
@@ -4984,6 +4998,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4984
4998
|
"Use the flow routes when the agent needs stable public input contracts, published outputs, node-level results, or reusable execution history.",
|
|
4985
4999
|
"If the user is still figuring out inputs or editable structure, read flow detail or box catalog before asking them to reconstruct structured inputs from memory.",
|
|
4986
5000
|
"For flow creation, clarify what the flow should reliably produce, which input contract it should accept, and which first node or box anchors the flow before asking for structured input details.",
|
|
5001
|
+
"For one-off execution, do not create a saved flow unless the user wants reuse. Ask whether the input contract should stay temporary or become durable, then use POST /api/v1/workbench/run for the temporary case.",
|
|
4987
5002
|
"For flow edits, ask what behavior should change while preserving the public contract unless the user explicitly wants the contract changed.",
|
|
4988
5003
|
"For flow deletion, confirm the saved flow and whether published outputs or run history need preservation elsewhere before using the delete route.",
|
|
4989
5004
|
"For saved flow chat follow-ups, use POST /api/v1/workbench/flows/:id/chat only when the user wants to continue a flow-specific conversation. Do not turn that into a new run, note, or generic entity update unless the user asks.",
|
|
@@ -5255,8 +5270,12 @@ function buildAgentOnboardingPayload(request) {
|
|
|
5255
5270
|
movementTripDetail: '{"routeKey":"tripDetail","pathParams":{"id":"trip_123"}}',
|
|
5256
5271
|
movementSettings: '{"routeKey":"settings","query":{"userIds":["user_operator"]}}',
|
|
5257
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."}}',
|
|
5258
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"}}',
|
|
5259
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"}}',
|
|
5260
5279
|
lifeForceOverview: '{"routeKey":"overview"}',
|
|
5261
5280
|
lifeForceProfile: '{"routeKey":"profile","body":{"baselineDailyAp":24,"recoveryNotes":"Clinic-admin days need a lower expected afternoon load."}}',
|
|
5262
5281
|
lifeForceWeekdayTemplate: '{"routeKey":"weekdayTemplate","pathParams":{"weekday":"monday"},"body":{"points":[{"hour":13,"freeAp":-4}]}}',
|
|
@@ -5267,6 +5286,8 @@ function buildAgentOnboardingPayload(request) {
|
|
|
5267
5286
|
workbenchUpdateFlow: '{"routeKey":"updateFlow","pathParams":{"id":"flow_research_digest"},"body":{"description":"Keep the same input contract but add a stronger evidence-check node."}}',
|
|
5268
5287
|
workbenchDeleteFlow: '{"routeKey":"deleteFlow","pathParams":{"id":"flow_research_digest"}}',
|
|
5269
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"}}',
|
|
5270
5291
|
workbenchPublishedOutput: '{"routeKey":"publishedOutput","pathParams":{"id":"flow_research_digest"}}',
|
|
5271
5292
|
workbenchLatestNodeOutput: '{"routeKey":"latestNodeOutput","pathParams":{"id":"flow_research_digest","nodeId":"node_summary"}}',
|
|
5272
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,
|
|
@@ -2735,6 +2735,97 @@ function mobileSyncSessionProgress(syncSessionId) {
|
|
|
2735
2735
|
receivedBytes: chunks.reduce((sum, chunk) => sum + chunk.byte_count, 0)
|
|
2736
2736
|
};
|
|
2737
2737
|
}
|
|
2738
|
+
function finiteNumberFromUnknown(value) {
|
|
2739
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2740
|
+
return value;
|
|
2741
|
+
}
|
|
2742
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
2743
|
+
const parsed = Number(value);
|
|
2744
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
2745
|
+
}
|
|
2746
|
+
return null;
|
|
2747
|
+
}
|
|
2748
|
+
function nestedRecord(value) {
|
|
2749
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
2750
|
+
? value
|
|
2751
|
+
: {};
|
|
2752
|
+
}
|
|
2753
|
+
function expectedWorkoutEvidenceCounts(derived) {
|
|
2754
|
+
const syncCursor = nestedRecord(derived.syncCursor);
|
|
2755
|
+
const captureQuality = nestedRecord(derived.captureQuality);
|
|
2756
|
+
const syncTimeSeriesCount = finiteNumberFromUnknown(syncCursor.timeSeriesSampleCount);
|
|
2757
|
+
const captureHeartRateCount = finiteNumberFromUnknown(captureQuality.heartRateSamples);
|
|
2758
|
+
const syncRoutePointCount = finiteNumberFromUnknown(syncCursor.routePointCount);
|
|
2759
|
+
const captureRoutePointCount = finiteNumberFromUnknown(captureQuality.routePoints);
|
|
2760
|
+
const expectedTimeSeriesSamples = Math.max(0, Math.ceil(Math.max(syncTimeSeriesCount ?? 0, captureHeartRateCount ?? 0)));
|
|
2761
|
+
const expectedRoutePoints = Math.max(0, Math.ceil(Math.max(syncRoutePointCount ?? 0, captureRoutePointCount ?? 0)));
|
|
2762
|
+
return {
|
|
2763
|
+
expectedTimeSeriesSamples,
|
|
2764
|
+
expectedRoutePoints,
|
|
2765
|
+
hasEvidenceMetadata: syncTimeSeriesCount !== null ||
|
|
2766
|
+
captureHeartRateCount !== null ||
|
|
2767
|
+
syncRoutePointCount !== null ||
|
|
2768
|
+
captureRoutePointCount !== null
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
function mobileHealthWorkoutImportState(userId) {
|
|
2772
|
+
const rows = getDatabase()
|
|
2773
|
+
.prepare(`WITH time_series_counts AS (
|
|
2774
|
+
SELECT workout_id, COUNT(*) AS time_series_count
|
|
2775
|
+
FROM health_workout_time_series
|
|
2776
|
+
GROUP BY workout_id
|
|
2777
|
+
),
|
|
2778
|
+
route_counts AS (
|
|
2779
|
+
SELECT workout_id, COUNT(*) AS route_point_count
|
|
2780
|
+
FROM health_workout_routes
|
|
2781
|
+
GROUP BY workout_id
|
|
2782
|
+
)
|
|
2783
|
+
SELECT
|
|
2784
|
+
w.external_uid,
|
|
2785
|
+
w.derived_json,
|
|
2786
|
+
COALESCE(time_series_counts.time_series_count, 0) AS time_series_count,
|
|
2787
|
+
COALESCE(route_counts.route_point_count, 0) AS route_point_count
|
|
2788
|
+
FROM health_workout_sessions w
|
|
2789
|
+
LEFT JOIN time_series_counts ON time_series_counts.workout_id = w.id
|
|
2790
|
+
LEFT JOIN route_counts ON route_counts.workout_id = w.id
|
|
2791
|
+
WHERE w.user_id = ?
|
|
2792
|
+
AND w.source = 'apple_health'
|
|
2793
|
+
AND w.external_uid IS NOT NULL
|
|
2794
|
+
AND w.external_uid <> ''
|
|
2795
|
+
ORDER BY w.started_at DESC`)
|
|
2796
|
+
.all(userId);
|
|
2797
|
+
const alreadyUploadedWorkoutExternalUids = [];
|
|
2798
|
+
let incompleteWorkoutCount = 0;
|
|
2799
|
+
let timeSeriesSampleCount = 0;
|
|
2800
|
+
let routePointCount = 0;
|
|
2801
|
+
for (const row of rows) {
|
|
2802
|
+
const derived = safeJsonParse(row.derived_json, {});
|
|
2803
|
+
const evidenceCounts = expectedWorkoutEvidenceCounts(derived);
|
|
2804
|
+
const actualTimeSeriesCount = Math.max(0, row.time_series_count ?? 0);
|
|
2805
|
+
const actualRoutePointCount = Math.max(0, row.route_point_count ?? 0);
|
|
2806
|
+
const evidenceComplete = evidenceCounts.hasEvidenceMetadata
|
|
2807
|
+
? actualTimeSeriesCount >= evidenceCounts.expectedTimeSeriesSamples &&
|
|
2808
|
+
actualRoutePointCount >= evidenceCounts.expectedRoutePoints
|
|
2809
|
+
: actualTimeSeriesCount + actualRoutePointCount > 0;
|
|
2810
|
+
if (evidenceComplete) {
|
|
2811
|
+
alreadyUploadedWorkoutExternalUids.push(row.external_uid.toLowerCase());
|
|
2812
|
+
timeSeriesSampleCount += actualTimeSeriesCount;
|
|
2813
|
+
routePointCount += actualRoutePointCount;
|
|
2814
|
+
}
|
|
2815
|
+
else {
|
|
2816
|
+
incompleteWorkoutCount += 1;
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
return {
|
|
2820
|
+
alreadyUploadedWorkoutExternalUids,
|
|
2821
|
+
alreadyUploadedWorkoutCount: alreadyUploadedWorkoutExternalUids.length,
|
|
2822
|
+
existingWorkoutCount: rows.length,
|
|
2823
|
+
incompleteWorkoutCount,
|
|
2824
|
+
timeSeriesSampleCount,
|
|
2825
|
+
routePointCount,
|
|
2826
|
+
capturedAt: nowIso()
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2738
2829
|
function mobileSyncSessionUploadPayload(session, receivedChunkIds) {
|
|
2739
2830
|
return {
|
|
2740
2831
|
syncSessionId: session.id,
|
|
@@ -2747,6 +2838,7 @@ function mobileSyncSessionUploadPayload(session, receivedChunkIds) {
|
|
|
2747
2838
|
supportsCompression: true,
|
|
2748
2839
|
acceptedFamilies: safeJsonParse(session.requested_families_json, []),
|
|
2749
2840
|
receivedChunkIds,
|
|
2841
|
+
workoutImportState: mobileHealthWorkoutImportState(session.user_id),
|
|
2750
2842
|
progress: mobileSyncSessionProgress(session.id)
|
|
2751
2843
|
};
|
|
2752
2844
|
}
|
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.
|
|
@@ -1537,9 +1556,9 @@ Preferred opening question:
|
|
|
1537
1556
|
## Movement
|
|
1538
1557
|
|
|
1539
1558
|
Aim: clarify whether the user wants to understand time in place, review travel
|
|
1540
|
-
behavior, add or update a stay or trip,
|
|
1541
|
-
settings, or link movement context to another Forge record before
|
|
1542
|
-
dedicated route family.
|
|
1559
|
+
behavior, add or update a stay or trip, create or clean up a known place, change
|
|
1560
|
+
movement operating settings, or link movement context to another Forge record before
|
|
1561
|
+
choosing the dedicated route family.
|
|
1543
1562
|
|
|
1544
1563
|
Arc:
|
|
1545
1564
|
|
|
@@ -1548,17 +1567,19 @@ Arc:
|
|
|
1548
1567
|
2. Ask whether the user is trying to query behavior, add something manually, update
|
|
1549
1568
|
an existing movement item, or link movement to another Forge entity.
|
|
1550
1569
|
3. Ask whether the focus is a stay, a trip, a place, a timeline window, or a selected span.
|
|
1551
|
-
4.
|
|
1552
|
-
|
|
1553
|
-
|
|
1570
|
+
4. If this is place creation or cleanup, ask what label, boundary, and future use
|
|
1571
|
+
should make the place recognizable later.
|
|
1572
|
+
5. Ask for the time window, place, or movement item that makes the question concrete.
|
|
1573
|
+
6. Ask what they are trying to notice, preserve, or answer through that movement context.
|
|
1574
|
+
7. If the user is changing movement operating behavior, ask whether the change is
|
|
1554
1575
|
about passive tracking, publish mode, retention, or companion readiness.
|
|
1555
|
-
|
|
1576
|
+
8. Choose the dedicated day, month, all-time, timeline, places, trip-detail,
|
|
1556
1577
|
selection, or settings route once the question shape is clear.
|
|
1557
|
-
|
|
1578
|
+
9. If the truth of one uncertain span is still unclear, read the timeline or saved-box
|
|
1558
1579
|
detail before you mutate it.
|
|
1559
|
-
|
|
1580
|
+
10. Skip the meta lane question when the user already named the exact correction or
|
|
1560
1581
|
review target and only one ambiguity remains.
|
|
1561
|
-
|
|
1582
|
+
11. Use the dedicated movement route once you know whether the user needs timeline
|
|
1562
1583
|
review, overlay, place or trip detail, selection summary, settings, or repair.
|
|
1563
1584
|
|
|
1564
1585
|
Direct action rules:
|
|
@@ -1591,12 +1612,17 @@ Direct action rules:
|
|
|
1591
1612
|
interval or place if that is still ambiguous, then act.
|
|
1592
1613
|
- When you do act on a concrete missing-gap correction, create the overlay and read
|
|
1593
1614
|
the relevant timeline back instead of leaving the correction ungrounded.
|
|
1615
|
+
- For known-place creation or cleanup, ask what the place should be called, what
|
|
1616
|
+
counts inside its boundary, and how future movement reads should use it. Use the
|
|
1617
|
+
dedicated place routes, not a tag or batch entity write.
|
|
1594
1618
|
|
|
1595
1619
|
Helpful follow-up lanes:
|
|
1596
1620
|
|
|
1597
1621
|
- whether the user wants time-in-place, travel history, one specific stay or trip, a
|
|
1598
1622
|
place summary, or a link
|
|
1599
1623
|
- what time window, place, stay, trip, or selection is in scope
|
|
1624
|
+
- what label, boundary, or future-use distinction makes a known place worth saving or
|
|
1625
|
+
renaming
|
|
1600
1626
|
- whether the question is behavioral, such as time at home, travel frequency, or place
|
|
1601
1627
|
distribution, versus an edit
|
|
1602
1628
|
- whether the edit is a missing-gap overlay versus a true recorded stay/trip patch
|
|
@@ -1649,7 +1675,8 @@ Preferred opening question:
|
|
|
1649
1675
|
## Life Force
|
|
1650
1676
|
|
|
1651
1677
|
Aim: clarify whether the user wants to review current energy state, change durable
|
|
1652
|
-
profile assumptions, edit weekday curves,
|
|
1678
|
+
profile assumptions, edit weekday curves, log a real-time fatigue signal, or make a
|
|
1679
|
+
planning decision based on the energy model.
|
|
1653
1680
|
|
|
1654
1681
|
Arc:
|
|
1655
1682
|
|
|
@@ -1657,22 +1684,25 @@ Arc:
|
|
|
1657
1684
|
you reduce it to one life-force lane.
|
|
1658
1685
|
2. Ask whether the job is overview, profile change, weekday-template change, or fatigue signaling.
|
|
1659
1686
|
3. Ask what part of the current energy picture feels most important or inaccurate.
|
|
1660
|
-
4. Ask what should
|
|
1661
|
-
|
|
1687
|
+
4. Ask what planning decision should change if the model is corrected: workload,
|
|
1688
|
+
recovery, timeboxing, meeting load, or task choice.
|
|
1689
|
+
5. Ask what should stay true if they are changing profile or template assumptions.
|
|
1690
|
+
6. Ask whether the user is describing a stable weekly shape or just how today feels
|
|
1662
1691
|
when the lane is still blurred.
|
|
1663
|
-
|
|
1692
|
+
7. If the user describes a repeatable day-shape such as "Mondays crash after lunch",
|
|
1664
1693
|
treat that as a weekday-template question before you reach for profile or
|
|
1665
1694
|
fatigue-signal routes.
|
|
1666
|
-
|
|
1695
|
+
8. If the user already named the life-force lane clearly, skip the meta lane question
|
|
1667
1696
|
and ask only for the specific weekday, profile field, or signal that still matters.
|
|
1668
|
-
|
|
1697
|
+
9. If the user wants to see what changed after a write, read the overview back instead
|
|
1669
1698
|
of leaving the result implicit.
|
|
1670
|
-
|
|
1699
|
+
10. Route to the dedicated life-force path once the lane is clear.
|
|
1671
1700
|
|
|
1672
1701
|
Helpful follow-up lanes:
|
|
1673
1702
|
|
|
1674
1703
|
- whether the user wants explanation, editing, or signaling
|
|
1675
1704
|
- what part of the energy model feels off or useful
|
|
1705
|
+
- what planning decision the overview or correction should change
|
|
1676
1706
|
- what durable assumption versus real-time state is being changed
|
|
1677
1707
|
- whether the user is describing a stable weekly shape or just how today feels
|
|
1678
1708
|
|
|
@@ -1704,6 +1734,11 @@ Direct action rules:
|
|
|
1704
1734
|
instead of treating it as a one-off right-now feeling.
|
|
1705
1735
|
- If the user is describing how one weekday should usually feel, update that weekday
|
|
1706
1736
|
template instead of editing the profile.
|
|
1737
|
+
- If the user only needs an explanation or planning read, use the overview first and
|
|
1738
|
+
do not turn the conversation into a profile or template mutation.
|
|
1739
|
+
- For profile or weekday-template edits, ask what future planning behavior should
|
|
1740
|
+
change, such as workload, recovery time, timeboxes, meeting load, or task choice,
|
|
1741
|
+
so the write is not just a more polished description.
|
|
1707
1742
|
- If the user says something like "I always dip on Tuesdays after lunch", treat that
|
|
1708
1743
|
as a weekday-template edit, not as a one-off fatigue signal.
|
|
1709
1744
|
- If the user is describing right-now depletion or recovery, post a fatigue signal and
|
|
@@ -1737,18 +1772,20 @@ Arc:
|
|
|
1737
1772
|
5. If the user is creating or editing a flow, clarify the flow's job, stable inputs,
|
|
1738
1773
|
expected public output, and the smallest structural change before asking for node
|
|
1739
1774
|
details.
|
|
1740
|
-
6. If the user wants
|
|
1775
|
+
6. If the user wants one-off execution, clarify whether this should stay a one-time
|
|
1776
|
+
input run or become a reusable saved flow before creating anything durable.
|
|
1777
|
+
7. If the user wants to delete or archive a flow, ask which saved flow is affected
|
|
1741
1778
|
and what future run, published output, or public contract should no longer exist.
|
|
1742
|
-
|
|
1779
|
+
8. If the user wants to continue a saved flow chat, ask which flow should receive the
|
|
1743
1780
|
follow-up and what the message should accomplish.
|
|
1744
|
-
|
|
1781
|
+
9. If the user already named the flow and action clearly, skip the meta lane
|
|
1745
1782
|
question and ask only for the missing run, node, or output scope.
|
|
1746
|
-
|
|
1783
|
+
10. If the user wants a stable public input contract or published output, prefer those
|
|
1747
1784
|
dedicated reads instead of detouring through run history first.
|
|
1748
|
-
|
|
1785
|
+
11. If the user is debugging one failed run, ask whether the useful artifact is the run
|
|
1749
1786
|
summary, one node result, the latest node output, or the published output before
|
|
1750
1787
|
you start asking for edits.
|
|
1751
|
-
|
|
1788
|
+
12. Route to the dedicated workbench route family once the execution lane is clear.
|
|
1752
1789
|
|
|
1753
1790
|
Helpful follow-up lanes:
|
|
1754
1791
|
|
|
@@ -1756,6 +1793,7 @@ Helpful follow-up lanes:
|
|
|
1756
1793
|
- what exact flow or run is in scope
|
|
1757
1794
|
- whether they need whole-flow output or node-level detail
|
|
1758
1795
|
- whether they need a public input contract or a published output instead of a debug trace
|
|
1796
|
+
- whether a requested execution should remain one-off or become a reusable saved flow
|
|
1759
1797
|
|
|
1760
1798
|
Lane-to-route map:
|
|
1761
1799
|
|
|
@@ -1796,6 +1834,9 @@ Direct action rules:
|
|
|
1796
1834
|
- If the user wants one-off input execution without depending on a saved flow id, use
|
|
1797
1835
|
`POST /api/v1/workbench/run` through the dedicated one-off execution lane and keep
|
|
1798
1836
|
the user-facing question about the one-off input contract.
|
|
1837
|
+
- For one-off execution, do not create a saved flow unless the user wants reuse. Ask
|
|
1838
|
+
whether the input contract should be temporary or durable, then route to
|
|
1839
|
+
`POST /api/v1/workbench/run` for the temporary case.
|
|
1799
1840
|
- If the user wants to debug one failed execution, narrow whether they need the run
|
|
1800
1841
|
detail, one node result, the latest node output, or the published output before you
|
|
1801
1842
|
ask for flow changes.
|
|
@@ -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.
|