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.
@@ -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, series_index)
369
- DO UPDATE SET value = excluded.value, started_at = excluded.started_at,
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,
@@ -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.92",
5
+ "version": "0.2.93",
6
6
  "activation": {
7
7
  "onStartup": true,
8
8
  "onCapabilities": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-openclaw-plugin",
3
- "version": "0.2.92",
3
+ "version": "0.2.93",
4
4
  "description": "Curated OpenClaw adapter for the Forge collaboration API, UI entrypoint, and localhost auto-start runtime.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.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);
@@ -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.