forge-openclaw-plugin 0.2.43 → 0.2.45

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.
@@ -333,6 +333,18 @@ export function buildOpenApiDocument() {
333
333
  message: { type: "string" }
334
334
  }
335
335
  };
336
+ const validationExpectedShape = {
337
+ type: "object",
338
+ additionalProperties: true,
339
+ required: ["inputShape", "requiredFields", "notes"],
340
+ properties: {
341
+ toolName: { type: "string" },
342
+ inputShape: { type: "string" },
343
+ requiredFields: arrayOf({ type: "string" }),
344
+ example: { type: ["string", "null"] },
345
+ notes: arrayOf({ type: "string" })
346
+ }
347
+ };
336
348
  const errorResponse = {
337
349
  type: "object",
338
350
  additionalProperties: true,
@@ -341,7 +353,12 @@ export function buildOpenApiDocument() {
341
353
  code: { type: "string" },
342
354
  error: { type: "string" },
343
355
  statusCode: { type: "integer" },
344
- details: arrayOf({ $ref: "#/components/schemas/ValidationIssue" })
356
+ route: { type: "string" },
357
+ validationSummary: { type: "string" },
358
+ details: arrayOf({ $ref: "#/components/schemas/ValidationIssue" }),
359
+ expectedShape: {
360
+ $ref: "#/components/schemas/ValidationExpectedShape"
361
+ }
345
362
  }
346
363
  };
347
364
  const tag = {
@@ -1808,6 +1825,43 @@ export function buildOpenApiDocument() {
1808
1825
  }
1809
1826
  }
1810
1827
  };
1828
+ const agentBootstrapPolicy = {
1829
+ type: "object",
1830
+ additionalProperties: false,
1831
+ required: [
1832
+ "mode",
1833
+ "goalsLimit",
1834
+ "projectsLimit",
1835
+ "tasksLimit",
1836
+ "habitsLimit",
1837
+ "strategiesLimit",
1838
+ "peoplePageLimit",
1839
+ "includePeoplePages"
1840
+ ],
1841
+ properties: {
1842
+ mode: {
1843
+ type: "string",
1844
+ enum: ["disabled", "active_only", "scoped", "full"]
1845
+ },
1846
+ goalsLimit: { type: "integer", minimum: 0, maximum: 100 },
1847
+ projectsLimit: { type: "integer", minimum: 0, maximum: 100 },
1848
+ tasksLimit: { type: "integer", minimum: 0, maximum: 100 },
1849
+ habitsLimit: { type: "integer", minimum: 0, maximum: 100 },
1850
+ strategiesLimit: { type: "integer", minimum: 0, maximum: 100 },
1851
+ peoplePageLimit: { type: "integer", minimum: 0, maximum: 50 },
1852
+ includePeoplePages: { type: "boolean" }
1853
+ }
1854
+ };
1855
+ const agentScopePolicy = {
1856
+ type: "object",
1857
+ additionalProperties: false,
1858
+ required: ["userIds", "projectIds", "tagIds"],
1859
+ properties: {
1860
+ userIds: arrayOf({ type: "string" }),
1861
+ projectIds: arrayOf({ type: "string" }),
1862
+ tagIds: arrayOf({ type: "string" })
1863
+ }
1864
+ };
1811
1865
  const agentTokenSummary = {
1812
1866
  type: "object",
1813
1867
  additionalProperties: false,
@@ -1822,6 +1876,8 @@ export function buildOpenApiDocument() {
1822
1876
  "autonomyMode",
1823
1877
  "approvalMode",
1824
1878
  "description",
1879
+ "bootstrapPolicy",
1880
+ "scopePolicy",
1825
1881
  "lastUsedAt",
1826
1882
  "revokedAt",
1827
1883
  "createdAt",
@@ -1848,6 +1904,8 @@ export function buildOpenApiDocument() {
1848
1904
  enum: ["approval_by_default", "high_impact_only", "none"]
1849
1905
  },
1850
1906
  description: { type: "string" },
1907
+ bootstrapPolicy: { $ref: "#/components/schemas/AgentBootstrapPolicy" },
1908
+ scopePolicy: { $ref: "#/components/schemas/AgentScopePolicy" },
1851
1909
  lastUsedAt: nullable({ type: "string", format: "date-time" }),
1852
1910
  revokedAt: nullable({ type: "string", format: "date-time" }),
1853
1911
  createdAt: { type: "string", format: "date-time" },
@@ -2762,6 +2820,10 @@ export function buildOpenApiDocument() {
2762
2820
  "recommendedTrustLevel",
2763
2821
  "recommendedAutonomyMode",
2764
2822
  "recommendedApprovalMode",
2823
+ "defaultBootstrapPolicy",
2824
+ "effectiveBootstrapPolicy",
2825
+ "defaultScopePolicy",
2826
+ "effectiveScopePolicy",
2765
2827
  "authModes",
2766
2828
  "tokenRecovery",
2767
2829
  "requiredHeaders",
@@ -2810,6 +2872,18 @@ export function buildOpenApiDocument() {
2810
2872
  type: "string",
2811
2873
  enum: ["approval_by_default", "high_impact_only", "none"]
2812
2874
  },
2875
+ defaultBootstrapPolicy: {
2876
+ $ref: "#/components/schemas/AgentBootstrapPolicy"
2877
+ },
2878
+ effectiveBootstrapPolicy: {
2879
+ $ref: "#/components/schemas/AgentBootstrapPolicy"
2880
+ },
2881
+ defaultScopePolicy: {
2882
+ $ref: "#/components/schemas/AgentScopePolicy"
2883
+ },
2884
+ effectiveScopePolicy: {
2885
+ $ref: "#/components/schemas/AgentScopePolicy"
2886
+ },
2813
2887
  authModes: {
2814
2888
  type: "object",
2815
2889
  additionalProperties: false,
@@ -4098,6 +4172,7 @@ export function buildOpenApiDocument() {
4098
4172
  components: {
4099
4173
  schemas: {
4100
4174
  ValidationIssue: validationIssue,
4175
+ ValidationExpectedShape: validationExpectedShape,
4101
4176
  ErrorResponse: errorResponse,
4102
4177
  Tag: tag,
4103
4178
  Goal: goal,
@@ -4154,6 +4229,8 @@ export function buildOpenApiDocument() {
4154
4229
  AgentRuntimeSessionEvent: agentRuntimeSessionEvent,
4155
4230
  AgentRuntimeSession: agentRuntimeSession,
4156
4231
  AgentRuntimeSessionHistory: agentRuntimeSessionHistory,
4232
+ AgentBootstrapPolicy: agentBootstrapPolicy,
4233
+ AgentScopePolicy: agentScopePolicy,
4157
4234
  AgentTokenSummary: agentTokenSummary,
4158
4235
  AgentTokenMutationResult: agentTokenMutationResult,
4159
4236
  Domain: domain,
@@ -7,7 +7,7 @@ import { recordActivityEvent } from "./activity-events.js";
7
7
  import { recordEventLog } from "./event-log.js";
8
8
  import { resolveGoogleCalendarOauthPublicConfig } from "../services/google-calendar-oauth-config.js";
9
9
  import { buildConnectionAgentIdentity, FORGE_DEFAULT_AGENT_ID, listAiModelConnections, syncForgeManagedWikiProfile } from "./model-settings.js";
10
- import { createAgentTokenSchema, agentIdentitySchema, customThemeSchema, settingsPayloadSchema, updateSettingsSchema } from "../types.js";
10
+ import { agentBootstrapPolicySchema, agentScopePolicySchema, createAgentTokenSchema, legacyAgentBootstrapPolicy, defaultAgentScopePolicy, agentIdentitySchema, customThemeSchema, settingsPayloadSchema, updateSettingsSchema } from "../types.js";
11
11
  const settingsFileSchema = settingsPayloadSchema.deepPartial();
12
12
  let settingsFileSyncDepth = 0;
13
13
  let lastSettingsFileStatus = {
@@ -319,6 +319,12 @@ function mapAgent(row) {
319
319
  });
320
320
  }
321
321
  function mapToken(row) {
322
+ const bootstrapPolicy = agentBootstrapPolicySchema.parse(row.bootstrap_policy_json
323
+ ? JSON.parse(row.bootstrap_policy_json)
324
+ : legacyAgentBootstrapPolicy);
325
+ const scopePolicy = agentScopePolicySchema.parse(row.scope_policy_json
326
+ ? JSON.parse(row.scope_policy_json)
327
+ : defaultAgentScopePolicy);
322
328
  return {
323
329
  id: row.id,
324
330
  label: row.label,
@@ -330,6 +336,8 @@ function mapToken(row) {
330
336
  autonomyMode: row.autonomy_mode,
331
337
  approvalMode: row.approval_mode,
332
338
  description: row.description,
339
+ bootstrapPolicy,
340
+ scopePolicy,
333
341
  lastUsedAt: row.last_used_at,
334
342
  revokedAt: row.revoked_at,
335
343
  createdAt: row.created_at,
@@ -409,6 +417,8 @@ export function listAgentTokens() {
409
417
  agent_tokens.label,
410
418
  agent_tokens.token_prefix,
411
419
  agent_tokens.scopes_json,
420
+ agent_tokens.bootstrap_policy_json,
421
+ agent_tokens.scope_policy_json,
412
422
  agent_tokens.agent_id,
413
423
  agent_identities.label AS agent_label,
414
424
  agent_tokens.trust_level,
@@ -807,9 +817,9 @@ export function createAgentToken(input, activity) {
807
817
  const tokenPrefix = `${token.slice(0, 10)}••••`;
808
818
  getDatabase()
809
819
  .prepare(`INSERT INTO agent_tokens (
810
- id, label, token_hash, token_prefix, scopes_json, agent_id, trust_level, autonomy_mode, approval_mode, description, created_at, updated_at
811
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
812
- .run(id, parsed.label, hashToken(token), tokenPrefix, JSON.stringify(parsed.scopes), agent.id, parsed.trustLevel, parsed.autonomyMode, parsed.approvalMode, parsed.description, now, now);
820
+ id, label, token_hash, token_prefix, scopes_json, bootstrap_policy_json, scope_policy_json, agent_id, trust_level, autonomy_mode, approval_mode, description, created_at, updated_at
821
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
822
+ .run(id, parsed.label, hashToken(token), tokenPrefix, JSON.stringify(parsed.scopes), JSON.stringify(parsed.bootstrapPolicy), JSON.stringify(parsed.scopePolicy), agent.id, parsed.trustLevel, parsed.autonomyMode, parsed.approvalMode, parsed.description, now, now);
813
823
  const tokenSummary = listAgentTokens().find((entry) => entry.id === id);
814
824
  if (activity) {
815
825
  recordActivityEvent({
@@ -823,7 +833,11 @@ export function createAgentToken(input, activity) {
823
833
  metadata: {
824
834
  agentId: agent.id,
825
835
  agentLabel: agent.label,
826
- scopes: parsed.scopes.join(",")
836
+ scopes: parsed.scopes.join(","),
837
+ bootstrapMode: parsed.bootstrapPolicy.mode,
838
+ scopedUserIds: parsed.scopePolicy.userIds.join(","),
839
+ scopedProjectIds: parsed.scopePolicy.projectIds.join(","),
840
+ scopedTagIds: parsed.scopePolicy.tagIds.join(",")
827
841
  }
828
842
  });
829
843
  recordEventLog({
@@ -836,7 +850,11 @@ export function createAgentToken(input, activity) {
836
850
  agentId: agent.id,
837
851
  trustLevel: parsed.trustLevel,
838
852
  autonomyMode: parsed.autonomyMode,
839
- approvalMode: parsed.approvalMode
853
+ approvalMode: parsed.approvalMode,
854
+ bootstrapMode: parsed.bootstrapPolicy.mode,
855
+ scopeUserCount: parsed.scopePolicy.userIds.length,
856
+ scopeProjectCount: parsed.scopePolicy.projectIds.length,
857
+ scopeTagCount: parsed.scopePolicy.tagIds.length
840
858
  }
841
859
  });
842
860
  }
@@ -929,6 +947,8 @@ export function verifyAgentToken(token) {
929
947
  agent_tokens.label,
930
948
  agent_tokens.token_prefix,
931
949
  agent_tokens.scopes_json,
950
+ agent_tokens.bootstrap_policy_json,
951
+ agent_tokens.scope_policy_json,
932
952
  agent_tokens.agent_id,
933
953
  agent_identities.label AS agent_label,
934
954
  agent_tokens.trust_level,
@@ -213,6 +213,77 @@ export const approvalModeSchema = z.enum([
213
213
  "high_impact_only",
214
214
  "none"
215
215
  ]);
216
+ export const agentBootstrapModeSchema = z.enum([
217
+ "disabled",
218
+ "active_only",
219
+ "scoped",
220
+ "full"
221
+ ]);
222
+ export const defaultAgentBootstrapPolicy = {
223
+ mode: "active_only",
224
+ goalsLimit: 5,
225
+ projectsLimit: 8,
226
+ tasksLimit: 10,
227
+ habitsLimit: 6,
228
+ strategiesLimit: 4,
229
+ peoplePageLimit: 4,
230
+ includePeoplePages: true
231
+ };
232
+ export const legacyAgentBootstrapPolicy = {
233
+ mode: "full",
234
+ goalsLimit: 25,
235
+ projectsLimit: 25,
236
+ tasksLimit: 25,
237
+ habitsLimit: 20,
238
+ strategiesLimit: 20,
239
+ peoplePageLimit: 12,
240
+ includePeoplePages: true
241
+ };
242
+ export const agentBootstrapPolicySchema = z.object({
243
+ mode: agentBootstrapModeSchema.default(defaultAgentBootstrapPolicy.mode),
244
+ goalsLimit: z.coerce
245
+ .number()
246
+ .int()
247
+ .min(0)
248
+ .max(100)
249
+ .default(defaultAgentBootstrapPolicy.goalsLimit),
250
+ projectsLimit: z.coerce
251
+ .number()
252
+ .int()
253
+ .min(0)
254
+ .max(100)
255
+ .default(defaultAgentBootstrapPolicy.projectsLimit),
256
+ tasksLimit: z.coerce
257
+ .number()
258
+ .int()
259
+ .min(0)
260
+ .max(100)
261
+ .default(defaultAgentBootstrapPolicy.tasksLimit),
262
+ habitsLimit: z.coerce
263
+ .number()
264
+ .int()
265
+ .min(0)
266
+ .max(100)
267
+ .default(defaultAgentBootstrapPolicy.habitsLimit),
268
+ strategiesLimit: z.coerce
269
+ .number()
270
+ .int()
271
+ .min(0)
272
+ .max(100)
273
+ .default(defaultAgentBootstrapPolicy.strategiesLimit),
274
+ peoplePageLimit: z.coerce
275
+ .number()
276
+ .int()
277
+ .min(0)
278
+ .max(50)
279
+ .default(defaultAgentBootstrapPolicy.peoplePageLimit),
280
+ includePeoplePages: z.boolean().default(defaultAgentBootstrapPolicy.includePeoplePages)
281
+ });
282
+ export const defaultAgentScopePolicy = {
283
+ userIds: [],
284
+ projectIds: [],
285
+ tagIds: []
286
+ };
216
287
  export const agentRuntimeProviderSchema = z.enum([
217
288
  "openclaw",
218
289
  "hermes",
@@ -393,6 +464,13 @@ const uniqueStringArraySchema = z
393
464
  seen.add(value);
394
465
  });
395
466
  });
467
+ export const agentScopePolicySchema = z.object({
468
+ userIds: uniqueStringArraySchema.default(() => [...defaultAgentScopePolicy.userIds]),
469
+ projectIds: uniqueStringArraySchema.default(() => [
470
+ ...defaultAgentScopePolicy.projectIds
471
+ ]),
472
+ tagIds: uniqueStringArraySchema.default(() => [...defaultAgentScopePolicy.tagIds])
473
+ });
396
474
  const integerMinuteSchema = z
397
475
  .number()
398
476
  .int()
@@ -2013,6 +2091,8 @@ export const agentTokenSummarySchema = z.object({
2013
2091
  autonomyMode: autonomyModeSchema,
2014
2092
  approvalMode: approvalModeSchema,
2015
2093
  description: z.string(),
2094
+ bootstrapPolicy: agentBootstrapPolicySchema,
2095
+ scopePolicy: agentScopePolicySchema,
2016
2096
  lastUsedAt: z.string().nullable(),
2017
2097
  revokedAt: z.string().nullable(),
2018
2098
  createdAt: z.string(),
@@ -3522,7 +3602,9 @@ export const createAgentTokenSchema = z.object({
3522
3602
  trustLevel: agentTrustLevelSchema.default("standard"),
3523
3603
  autonomyMode: autonomyModeSchema.default("approval_required"),
3524
3604
  approvalMode: approvalModeSchema.default("approval_by_default"),
3525
- scopes: uniqueStringArraySchema.default(["read", "write", "insights"])
3605
+ scopes: uniqueStringArraySchema.default(["read", "write", "insights"]),
3606
+ bootstrapPolicy: agentBootstrapPolicySchema.default(defaultAgentBootstrapPolicy),
3607
+ scopePolicy: agentScopePolicySchema.default(defaultAgentScopePolicy)
3526
3608
  });
3527
3609
  export const activityArchiveQuerySchema = activityListQuerySchema.extend({
3528
3610
  groupBy: z.enum(["day", "entity"]).optional(),
@@ -138,7 +138,22 @@ export const createAgentTokenSchema = z.object({
138
138
  trustLevel: z.enum(["standard", "trusted", "autonomous"]),
139
139
  autonomyMode: z.enum(["approval_required", "scoped_write", "autonomous"]),
140
140
  approvalMode: z.enum(["approval_by_default", "high_impact_only", "none"]),
141
- scopes: z.array(z.string().trim().min(1)).min(1)
141
+ scopes: z.array(z.string().trim().min(1)).min(1),
142
+ bootstrapPolicy: z.object({
143
+ mode: z.enum(["disabled", "active_only", "scoped", "full"]),
144
+ goalsLimit: z.coerce.number().int().min(0).max(100),
145
+ projectsLimit: z.coerce.number().int().min(0).max(100),
146
+ tasksLimit: z.coerce.number().int().min(0).max(100),
147
+ habitsLimit: z.coerce.number().int().min(0).max(100),
148
+ strategiesLimit: z.coerce.number().int().min(0).max(100),
149
+ peoplePageLimit: z.coerce.number().int().min(0).max(50),
150
+ includePeoplePages: z.boolean()
151
+ }),
152
+ scopePolicy: z.object({
153
+ userIds: z.array(z.string().trim().min(1)),
154
+ projectIds: z.array(z.string().trim().min(1)),
155
+ tagIds: z.array(z.string().trim().min(1))
156
+ })
142
157
  });
143
158
  export const createInsightSchema = z.object({
144
159
  originType: z.enum(["system", "user", "agent"]),
@@ -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.43",
5
+ "version": "0.2.45",
6
6
  "skills": [
7
7
  "./skills"
8
8
  ],
@@ -34,6 +34,10 @@
34
34
  "help": "Optional acting user label for provenance. Leave blank to inherit the local operator session label, or set one when a child agent should announce itself under a specific user.",
35
35
  "placeholder": "Inherited from Forge operator session"
36
36
  },
37
+ "injectBootstrapContext": {
38
+ "label": "Inject Bootstrap Context",
39
+ "help": "Enabled by default. Turn this off when you want OpenClaw sessions to start without a preseeded Forge BOOTSTRAP.md context file to conserve token budget."
40
+ },
37
41
  "timeoutMs": {
38
42
  "label": "Request Timeout (ms)",
39
43
  "help": "Maximum time to wait before the plugin aborts an upstream Forge request.",
@@ -71,6 +75,11 @@
71
75
  "default": "",
72
76
  "description": "Optional acting user label recorded in Forge provenance headers. Leave blank to inherit the local operator session label automatically."
73
77
  },
78
+ "injectBootstrapContext": {
79
+ "type": "boolean",
80
+ "default": true,
81
+ "description": "Whether OpenClaw should inject a preseeded Forge BOOTSTRAP.md file into new agent sessions. Disable this to conserve context or model-token budget."
82
+ },
74
83
  "timeoutMs": {
75
84
  "type": "integer",
76
85
  "default": 15000,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-openclaw-plugin",
3
- "version": "0.2.43",
3
+ "version": "0.2.45",
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": "MIT",
@@ -0,0 +1,2 @@
1
+ ALTER TABLE agent_tokens
2
+ ADD COLUMN bootstrap_policy_json TEXT NOT NULL DEFAULT '{"mode":"full","goalsLimit":25,"projectsLimit":25,"tasksLimit":25,"habitsLimit":20,"strategiesLimit":20,"peoplePageLimit":12,"includePeoplePages":true}';
@@ -0,0 +1,2 @@
1
+ ALTER TABLE agent_tokens
2
+ ADD COLUMN scope_policy_json TEXT NOT NULL DEFAULT '{"userIds":[],"projectIds":[],"tagIds":[]}';
@@ -61,6 +61,9 @@ Forge correctly, and gather only the structure that still matters.
61
61
  and then act.
62
62
  - Once the route family is clear, say it plainly enough that another agent could follow
63
63
  the same path without guessing.
64
+ - For meaning-bearing updates, especially in Psyche-adjacent work, briefly say what
65
+ feels newly true before you ask for the one structural detail that still changes the
66
+ save.
64
67
  - Do not read schema fields out loud unless the user explicitly wants a checklist.
65
68
  - One focused question is the default. Ask two only when both questions serve the same
66
69
  job and the user is steady enough for it.
@@ -780,6 +783,7 @@ Arc:
780
783
  2. Confirm the actor only if it is not already obvious.
781
784
  3. Ask whether the run should be planned or unlimited only if that changes the action.
782
785
  4. Start the run instead of turning it into intake.
786
+ 5. Use the dedicated task-run tool for start, heartbeat, focus, complete, and release work. Do not bounce to the Forge UI, a browser session, or a generic web route for those actions unless the user explicitly wants the visual surface.
783
787
 
784
788
  Ready to start when:
785
789
 
@@ -1011,9 +1015,11 @@ Arc:
1011
1015
  3. Ask whether the focus is a stay, a trip, a place, a timeline window, or a selected span.
1012
1016
  4. Ask for the time window, place, or movement item that makes the question concrete.
1013
1017
  5. Ask what they are trying to notice, preserve, or answer through that movement context.
1014
- 6. Skip the meta lane question when the user already named the exact correction or
1018
+ 6. Choose the dedicated day, month, all-time, timeline, places, trip-detail, or
1019
+ selection route once the question shape is clear.
1020
+ 7. Skip the meta lane question when the user already named the exact correction or
1015
1021
  review target and only one ambiguity remains.
1016
- 7. Route to the dedicated movement read or write path once the surface is clear.
1022
+ 8. Route to the dedicated movement read or write path once the surface is clear.
1017
1023
 
1018
1024
  Direct action rules:
1019
1025
 
@@ -1101,7 +1107,9 @@ Arc:
1101
1107
  when the lane is still blurred.
1102
1108
  6. If the user already named the life-force lane clearly, skip the meta lane question
1103
1109
  and ask only for the specific weekday, profile field, or signal that still matters.
1104
- 7. Route to the dedicated life-force path once the lane is clear.
1110
+ 7. If the user wants to see what changed after a write, read the overview back instead
1111
+ of leaving the result implicit.
1112
+ 8. Route to the dedicated life-force path once the lane is clear.
1105
1113
 
1106
1114
  Helpful follow-up lanes:
1107
1115
 
@@ -1156,7 +1164,9 @@ Arc:
1156
1164
  4. Ask whether they need the flow contract, a run result, a published output, or a node result.
1157
1165
  5. If the user already named the flow and action clearly, skip the meta lane
1158
1166
  question and ask only for the missing run, node, or output scope.
1159
- 6. Route to the dedicated workbench route family once the execution lane is clear.
1167
+ 6. If the user wants a stable public input contract or published output, prefer those
1168
+ dedicated reads instead of detouring through run history first.
1169
+ 7. Route to the dedicated workbench route family once the execution lane is clear.
1160
1170
 
1161
1171
  Helpful follow-up lanes:
1162
1172