@productbrain/mcp 0.0.1-beta.11368 → 0.0.1-beta.11378

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.
@@ -36,7 +36,7 @@ import {
36
36
 
37
37
  // src/server.ts
38
38
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
39
- import { z as z35 } from "zod/v3";
39
+ import { z as z36 } from "zod/v3";
40
40
 
41
41
  // src/lib/mcpGatewayClientMessages.ts
42
42
  function agentSessionNotActiveMessage(status2) {
@@ -233,7 +233,7 @@ function resolveEffectiveCollection(result2, preview, fallbackCollection) {
233
233
  }
234
234
 
235
235
  // src/tools/entries.ts
236
- import { z as z11 } from "zod/v3";
236
+ import { z as z12 } from "zod/v3";
237
237
 
238
238
  // src/tools/knowledge-helpers.ts
239
239
  import { z as z2 } from "zod/v3";
@@ -565,6 +565,7 @@ function clearSessionGaps() {
565
565
  }
566
566
 
567
567
  // src/compound-tool.ts
568
+ import { z as z3 } from "zod/v3";
568
569
  function backpressureResult(toolName, action, spec, detail) {
569
570
  const paramList = spec.params.length > 0 ? spec.params.join(", ") : "(no params)";
570
571
  const tip = `action '${action}' needs: ${paramList}. ${spec.description}`;
@@ -595,6 +596,14 @@ async function dispatchDiscriminated(toolName, union2, flatData, actionSpecs, ha
595
596
  }
596
597
  return handler(parsed.data);
597
598
  }
599
+ function actionEnum(actions2, moved) {
600
+ return z3.enum(actions2, {
601
+ errorMap: (issue, ctx) => {
602
+ const received = issue.received;
603
+ return { message: typeof received === "string" && Object.hasOwn(moved, received) ? moved[received] : ctx.defaultError };
604
+ }
605
+ });
606
+ }
598
607
 
599
608
  // src/lib/restsOn.ts
600
609
  var RESTS_ON_SENTENCES = {
@@ -614,10 +623,10 @@ function restsOnLine(item) {
614
623
  }
615
624
 
616
625
  // src/tools/knowledge.ts
617
- import { z as z6 } from "zod/v3";
626
+ import { z as z7 } from "zod/v3";
618
627
 
619
628
  // src/tools/smart-capture.ts
620
- import { z as z4 } from "zod/v3";
629
+ import { z as z5 } from "zod/v3";
621
630
 
622
631
  // src/lib/statusCategories.ts
623
632
  var STATUS_CATEGORIES = ["draft", "current", "past", "discarded"];
@@ -863,29 +872,29 @@ function formatRubricVerdictSection(verdict2) {
863
872
  }
864
873
 
865
874
  // src/lib/captureSinglePreviewOutput.ts
866
- import { z as z3 } from "zod/v3";
867
- var groundingDuplicateEntrySchema = z3.object({
868
- entryId: z3.string(),
869
- name: z3.string(),
870
- collectionSlug: z3.string(),
871
- overlapRatio: z3.number()
875
+ import { z as z4 } from "zod/v3";
876
+ var groundingDuplicateEntrySchema = z4.object({
877
+ entryId: z4.string(),
878
+ name: z4.string(),
879
+ collectionSlug: z4.string(),
880
+ overlapRatio: z4.number()
872
881
  });
873
- var groundingReportSchema = z3.object({
882
+ var groundingReportSchema = z4.object({
874
883
  // TASK-156: null preserves uncertainty for unavailable checks or an older server response.
875
- scanTruncated: z3.boolean().nullable(),
876
- duplicates: z3.array(groundingDuplicateEntrySchema)
884
+ scanTruncated: z4.boolean().nullable(),
885
+ duplicates: z4.array(groundingDuplicateEntrySchema)
877
886
  });
878
- var taskAlignmentSchema = z3.record(z3.unknown());
879
- var captureSinglePreviewOutputSchema = z3.object({
880
- preflight: z3.custom().optional(),
881
- entryId: z3.string(),
882
- name: z3.string(),
883
- collection: z3.string(),
884
- outcome: z3.enum(["preview", "blocked"]),
887
+ var taskAlignmentSchema = z4.record(z4.unknown());
888
+ var captureSinglePreviewOutputSchema = z4.object({
889
+ preflight: z4.custom().optional(),
890
+ entryId: z4.string(),
891
+ name: z4.string(),
892
+ collection: z4.string(),
893
+ outcome: z4.enum(["preview", "blocked"]),
885
894
  // Present only when `outcome: "blocked"` AND the server supplied a reason.
886
- blockingReason: z3.string().optional(),
895
+ blockingReason: z4.string().optional(),
887
896
  // Always present — `result.warnings ?? []`, never omitted.
888
- warnings: z3.array(z3.string()),
897
+ warnings: z4.array(z4.string()),
889
898
  groundingReport: groundingReportSchema,
890
899
  // TEN-2458: only present when the kernel computed a librarian verdict for this preview.
891
900
  taskAlignment: taskAlignmentSchema.optional()
@@ -902,116 +911,116 @@ var RELATION_BATCH_MAX = 25;
902
911
  var CAPTURE_LINK_OUTCOMES = ["written", "refused"];
903
912
  var SPINE_FLOOR_SENTENCE = "kept as a draft; it can be accepted once its spine above is accepted \u2014 the promote door checks the whole spine and names the first missing rung.";
904
913
  var SPINE_FLOOR_EXEMPT_SENTENCE = "kept as a draft, though this workspace is not yet enforcing the spine above it \u2014 it can be accepted as-is.";
905
- var captureSchema = z4.object({
906
- collection: z4.string().optional().describe("Collection slug, e.g. 'tensions', 'rules', 'glossary', 'decisions'. Optional \u2014 the kernel classifies when omitted."),
907
- name: z4.string().describe("Display name \u2014 be specific (e.g. 'Convex adjacency list won't scale for graph traversal')"),
908
- description: z4.string().describe("Full context \u2014 what's happening, why it matters, what you observed"),
909
- context: z4.string().optional().describe("Optional additional context (e.g. 'Observed during context gather calls taking 700ms+'); appended to the description, stored only when the description is."),
910
- canonicalKey: z4.string().optional().describe("Semantic type (e.g. 'decision', 'tension', 'vision'). Auto-assigned from collection if omitted."),
911
- data: z4.record(z4.unknown()).optional().describe("Explicit field values when you know the schema (e.g. canonical_key, cardinality_rule, required_fields). Merged with inferred values; user-provided wins."),
912
- why: z4.string().optional().describe("Reasoning behind this entry \u2014 hard-required for purpose/vision/strategy/objective/decision/rule/key_result/product."),
914
+ var captureSchema = z5.object({
915
+ collection: z5.string().optional().describe("Collection slug, e.g. 'tensions', 'rules', 'glossary', 'decisions'. Optional \u2014 the kernel classifies when omitted."),
916
+ name: z5.string().describe("Display name \u2014 be specific (e.g. 'Convex adjacency list won't scale for graph traversal')"),
917
+ description: z5.string().describe("Full context \u2014 what's happening, why it matters, what you observed"),
918
+ context: z5.string().optional().describe("Optional additional context (e.g. 'Observed during context gather calls taking 700ms+'); appended to the description, stored only when the description is."),
919
+ canonicalKey: z5.string().optional().describe("Semantic type (e.g. 'decision', 'tension', 'vision'). Auto-assigned from collection if omitted."),
920
+ data: z5.record(z5.unknown()).optional().describe("Explicit field values when you know the schema (e.g. canonical_key, cardinality_rule, required_fields). Merged with inferred values; user-provided wins."),
921
+ why: z5.string().optional().describe("Reasoning behind this entry \u2014 hard-required for purpose/vision/strategy/objective/decision/rule/key_result/product."),
913
922
  // TASK-380: mirrors whyPolicy.ts
914
- links: z4.array(z4.object({
915
- to: z4.string().describe("Target entry ID (e.g. '<PREFIX>-<n>')"),
916
- type: z4.string().describe("Relation type (e.g. 'governs', 'related_to', 'informs')")
923
+ links: z5.array(z5.object({
924
+ to: z5.string().describe("Target entry ID (e.g. '<PREFIX>-<n>')"),
925
+ type: z5.string().describe("Relation type (e.g. 'governs', 'related_to', 'informs')")
917
926
  })).optional().describe("Links written with the entry in one transaction; each returns written or refused with a reason; a refused link fails nothing."),
918
- sourceRef: z4.string().optional().describe("URI or path of the source document backing this entry (e.g. 'meeting-2026-03-28.md', 'import://batch-5'). Stored as top-level entry field, not in data."),
919
- sourceExcerpt: z4.string().optional().describe("Verbatim excerpt from the source that backs this entry's claims. Stored as top-level entry field, not in data."),
927
+ sourceRef: z5.string().optional().describe("URI or path of the source document backing this entry (e.g. 'meeting-2026-03-28.md', 'import://batch-5'). Stored as top-level entry field, not in data."),
928
+ sourceExcerpt: z5.string().optional().describe("Verbatim excerpt from the source that backs this entry's claims. Stored as top-level entry field, not in data."),
920
929
  // WP-316 S3: Preview gate — dry-run mode. Returns what would happen, no DB writes.
921
- preview: z4.boolean().optional().describe("If true, validates the capture without writing. Returns what would happen. Default false."),
930
+ preview: z5.boolean().optional().describe("If true, validates the capture without writing. Returns what would happen. Default false."),
922
931
  // WP-513 / WP-753 O1b (DEC-1715): explicit owning role (rung 2); its team is the one you hold it in. Unnamed: a default owner.
923
- ownerRoleEntryId: z4.string().max(200).optional().describe("Owning role entry ID/ref (rung 2); you must hold it, and its team is the one you hold it in (root first, else the shallowest)."),
932
+ ownerRoleEntryId: z5.string().max(200).optional().describe("Owning role entry ID/ref (rung 2); you must hold it, and its team is the one you hold it in (root first, else the shallowest)."),
924
933
  // TASK-681 (DEC-1976, DEC-1977): where a new role or team sits, forwarded as createEntry's teamRef; the kernel places it.
925
- team: z4.string().max(200).optional().describe("For a role: the team it is born in. For a team: its parent team. Entry ID or ref; defaults to the root team.")
934
+ team: z5.string().max(200).optional().describe("For a role: the team it is born in. For a team: its parent team. Entry ID or ref; defaults to the root team.")
926
935
  });
927
- var captureClassifierSchema = z4.object({
928
- enabled: z4.boolean(),
929
- autoRouted: z4.boolean(),
930
- agrees: z4.boolean(),
931
- abstained: z4.boolean(),
932
- topConfidence: z4.number(),
933
- confidence: z4.number(),
934
- reasons: z4.array(z4.string()),
935
- candidates: z4.array(
936
- z4.object({
937
- collection: z4.string(),
938
- signalScore: z4.number().optional(),
939
- confidence: z4.number(),
936
+ var captureClassifierSchema = z5.object({
937
+ enabled: z5.boolean(),
938
+ autoRouted: z5.boolean(),
939
+ agrees: z5.boolean(),
940
+ abstained: z5.boolean(),
941
+ topConfidence: z5.number(),
942
+ confidence: z5.number(),
943
+ reasons: z5.array(z5.string()),
944
+ candidates: z5.array(
945
+ z5.object({
946
+ collection: z5.string(),
947
+ signalScore: z5.number().optional(),
948
+ confidence: z5.number(),
940
949
  /** WP-316 S2: required-field cost — how many fields the agent must supply for this collection. */
941
- requiredFieldCount: z4.number().optional()
950
+ requiredFieldCount: z5.number().optional()
942
951
  })
943
952
  ),
944
- agentProvidedCollection: z4.string().optional(),
945
- overrideCommand: z4.string().optional(),
946
- classifiedBy: z4.string().optional(),
947
- confidenceTier: z4.string().optional(),
948
- reasoning: z4.string().optional()
953
+ agentProvidedCollection: z5.string().optional(),
954
+ overrideCommand: z5.string().optional(),
955
+ classifiedBy: z5.string().optional(),
956
+ confidenceTier: z5.string().optional(),
957
+ reasoning: z5.string().optional()
949
958
  });
950
- var captureSuccessOutputSchema = z4.object({
951
- entryId: z4.string(),
952
- collection: z4.string(),
953
- name: z4.string(),
954
- status: z4.enum(["draft", "committed", "proposed", "draft_on_failure"]),
959
+ var captureSuccessOutputSchema = z5.object({
960
+ entryId: z5.string(),
961
+ collection: z5.string(),
962
+ name: z5.string(),
963
+ status: z5.enum(["draft", "committed", "proposed", "draft_on_failure"]),
955
964
  // WP-480 S1: `qualityScore` deleted (accepted breaking output change); the server verdict stays as `qualityVerdict`.
956
- qualityVerdict: z4.record(z4.unknown()).optional(),
957
- qualityHints: z4.array(z4.record(z4.unknown())).optional(),
965
+ qualityVerdict: z5.record(z5.unknown()).optional(),
966
+ qualityHints: z5.array(z5.record(z5.unknown())).optional(),
958
967
  groundingReport: captureSinglePreviewOutputSchema.shape.groundingReport.optional(),
959
968
  classifier: captureClassifierSchema.optional(),
960
- studioUrl: z4.string().optional(),
969
+ studioUrl: z5.string().optional(),
961
970
  // never set since TASK-964 (the chains-only Cortex link left with the retired drawer); kept, never narrowed (DEC-1831)
962
- warnings: z4.array(z4.string()).optional(),
963
- normalization: z4.object({
964
- remapped: z4.record(z4.string()).optional(),
965
- rejected: z4.array(z4.string()).optional()
971
+ warnings: z5.array(z5.string()).optional(),
972
+ normalization: z5.object({
973
+ remapped: z5.record(z5.string()).optional(),
974
+ rejected: z5.array(z5.string()).optional()
966
975
  }).optional(),
967
- expectedFields: z4.array(z4.object({
968
- key: z4.string(),
969
- type: z4.string(),
970
- required: z4.boolean().optional()
976
+ expectedFields: z5.array(z5.object({
977
+ key: z5.string(),
978
+ type: z5.string(),
979
+ required: z5.boolean().optional()
971
980
  })).optional(),
972
981
  // TASK-160: the server's birth-floor verdict must survive the strict output contract.
973
- spineFloor: z4.object({ relationType: z4.string(), parentKind: z4.string(), exempt: z4.boolean() }).optional(),
982
+ spineFloor: z5.object({ relationType: z5.string(), parentKind: z5.string(), exempt: z5.boolean() }).optional(),
974
983
  // WP-751 M3a-1b (TASK-189, DEC-1700): `coherencyRefusal` and `contradictionAdvisory` are deleted with the
975
984
  // client-side auto-commit attempt that alone populated them (its gate refusal, the commit's advisory).
976
- replayed: z4.literal(true).optional(),
985
+ replayed: z5.literal(true).optional(),
977
986
  // TASK-898: a re-sent capture answered with the first capture's entry
978
987
  // TASK-897: the kernel's outcome per link, on a create and on a replay.
979
- links: z4.array(z4.object({ toEntryId: z4.string(), type: z4.string(), outcome: z4.enum(CAPTURE_LINK_OUTCOMES), reason: z4.string().optional(), line: z4.string() }).passthrough()).optional(),
980
- fallbackApplied: z4.boolean().optional()
988
+ links: z5.array(z5.object({ toEntryId: z5.string(), type: z5.string(), outcome: z5.enum(CAPTURE_LINK_OUTCOMES), reason: z5.string().optional(), line: z5.string() }).passthrough()).optional(),
989
+ fallbackApplied: z5.boolean().optional()
981
990
  // never set since TASK-964 (the kernel classifies among live collections only); kept, never narrowed (DEC-1831)
982
991
  }).strict();
983
- var captureOutputSchema = z4.union([
992
+ var captureOutputSchema = z5.union([
984
993
  captureSuccessOutputSchema,
985
994
  captureSinglePreviewOutputSchema
986
995
  ]);
987
996
  var CAPTURE_ACTIONS = ["capture"];
988
997
  var SUGGEST_ONLY_REMOVED = "suggestOnly was removed: call capture with preview: true to check a capture without writing.";
989
- var captureCompoundSchema = z4.object({
990
- action: z4.enum(CAPTURE_ACTIONS).optional().default("capture").describe("Create a single knowledge entry."),
991
- collection: z4.string().max(200).optional().describe("Collection slug, e.g. 'tensions', 'rules', 'glossary', 'decisions'. Optional \u2014 the kernel classifies when omitted."),
992
- name: z4.string().max(ENTRY_NAME_MAX_CHARS).optional().describe("Display name \u2014 required."),
993
- description: z4.string().max(ENTRY_BODY_MAX_CHARS).optional().describe("Full context, in Markdown \u2014 required."),
994
- context: z4.string().max(5e3).optional().describe("Optional additional context; appended to the description, so it is stored only when the description is (not when data carries the prose field)."),
995
- canonicalKey: z4.string().max(200).optional().describe("Semantic type (e.g. 'decision', 'tension', 'vision'). Auto-assigned from collection if omitted."),
996
- data: z4.record(z4.unknown()).optional().describe("Explicit field values; user-provided wins over inferred."),
997
- why: z4.string().max(ENTRY_BODY_MAX_CHARS).optional().describe("Why this entry exists."),
998
- links: z4.array(z4.object({
999
- to: z4.string().max(200).describe("Target entry ID (e.g. '<PREFIX>-<n>')"),
1000
- type: z4.string().max(200).describe("Relation type (e.g. 'governs', 'related_to', 'informs')")
998
+ var captureCompoundSchema = z5.object({
999
+ action: z5.enum(CAPTURE_ACTIONS).optional().default("capture").describe("Create a single knowledge entry."),
1000
+ collection: z5.string().max(200).optional().describe("Collection slug, e.g. 'tensions', 'rules', 'glossary', 'decisions'. Optional \u2014 the kernel classifies when omitted."),
1001
+ name: z5.string().max(ENTRY_NAME_MAX_CHARS).optional().describe("Display name \u2014 required."),
1002
+ description: z5.string().max(ENTRY_BODY_MAX_CHARS).optional().describe("Full context, in Markdown \u2014 required."),
1003
+ context: z5.string().max(5e3).optional().describe("Optional additional context; appended to the description, so it is stored only when the description is (not when data carries the prose field)."),
1004
+ canonicalKey: z5.string().max(200).optional().describe("Semantic type (e.g. 'decision', 'tension', 'vision'). Auto-assigned from collection if omitted."),
1005
+ data: z5.record(z5.unknown()).optional().describe("Explicit field values; user-provided wins over inferred."),
1006
+ why: z5.string().max(ENTRY_BODY_MAX_CHARS).optional().describe("Why this entry exists."),
1007
+ links: z5.array(z5.object({
1008
+ to: z5.string().max(200).describe("Target entry ID (e.g. '<PREFIX>-<n>')"),
1009
+ type: z5.string().max(200).describe("Relation type (e.g. 'governs', 'related_to', 'informs')")
1001
1010
  })).max(RELATION_BATCH_MAX).optional().describe("Links written with the entry in one transaction; each returns written or refused with a reason; a refused link fails nothing."),
1002
- sourceRef: z4.string().max(2e3).optional().describe("URI or path of the source document backing this entry."),
1003
- sourceExcerpt: z4.string().max(SOURCE_EXCERPT_MAX_CHARS).optional().describe("Verbatim excerpt from the source backing this entry's claims."),
1004
- preview: z4.boolean().optional().describe("If true, validates without writing."),
1005
- suggestOnly: z4.never({ invalid_type_error: SUGGEST_ONLY_REMOVED }).optional().describe(SUGGEST_ONLY_REMOVED),
1011
+ sourceRef: z5.string().max(2e3).optional().describe("URI or path of the source document backing this entry."),
1012
+ sourceExcerpt: z5.string().max(SOURCE_EXCERPT_MAX_CHARS).optional().describe("Verbatim excerpt from the source backing this entry's claims."),
1013
+ preview: z5.boolean().optional().describe("If true, validates without writing."),
1014
+ suggestOnly: z5.never({ invalid_type_error: SUGGEST_ONLY_REMOVED }).optional().describe(SUGGEST_ONLY_REMOVED),
1006
1015
  // TASK-608: refused, never stripped into a write.
1007
1016
  // WP-513 / WP-753 O1b: explicit owning role (rung 2); its team is the one you hold it in. TASK-681: `team` places a new role or team.
1008
- ownerRoleEntryId: z4.string().max(200).optional().describe("Owning role (rung-2); you must hold it; its team is the one you hold it in (root first, else the shallowest)."),
1009
- team: z4.string().max(200).optional().describe("For a role: the team it is born in. For a team: its parent team. Entry ID or ref; defaults to the root team.")
1017
+ ownerRoleEntryId: z5.string().max(200).optional().describe("Owning role (rung-2); you must hold it; its team is the one you hold it in (root first, else the shallowest)."),
1018
+ team: z5.string().max(200).optional().describe("For a role: the team it is born in. For a team: its parent team. Entry ID or ref; defaults to the root team.")
1010
1019
  });
1011
- var captureSingleVariant = captureSchema.extend({ action: z4.literal("capture") });
1020
+ var captureSingleVariant = captureSchema.extend({ action: z5.literal("capture") });
1012
1021
  var CAVEAT_JUDGE_LATE = "The duplicate check is still running; a close match it finds is added to this entry as a check.";
1013
1022
  var CAVEAT_SCAN_CAPPED = "The duplicate check read a capped sample; more matching entries may exist. Review existing entries before creating another.";
1014
- var captureActionUnion = z4.discriminatedUnion("action", [
1023
+ var captureActionUnion = z5.discriminatedUnion("action", [
1015
1024
  captureSingleVariant
1016
1025
  ]);
1017
1026
  var CAPTURE_ACTION_SPECS = {
@@ -1530,9 +1539,9 @@ ${recovery}`,
1530
1539
  }
1531
1540
 
1532
1541
  // src/tools/knowledge/getHistory.ts
1533
- import { z as z5 } from "zod/v3";
1534
- var getHistorySchema = z5.object({
1535
- entryId: z5.string().describe("Entry ID, e.g. '<PREFIX>-<n>'")
1542
+ import { z as z6 } from "zod/v3";
1543
+ var getHistorySchema = z6.object({
1544
+ entryId: z6.string().describe("Entry ID, e.g. '<PREFIX>-<n>'")
1536
1545
  });
1537
1546
  async function handleGetHistory({ entryId }) {
1538
1547
  const envelope = await kernelCallEnvelope("chain.listEntryHistory", { entryId });
@@ -1568,44 +1577,44 @@ ${cut}${formatted}` }],
1568
1577
  }
1569
1578
 
1570
1579
  // src/tools/knowledge.ts
1571
- var updateEntrySchema = z6.object({
1572
- entryId: z6.string().describe("Entry ID to update, e.g. '<PREFIX>-<n>'"),
1573
- name: z6.string().optional().describe("New display name"),
1580
+ var updateEntrySchema = z7.object({
1581
+ entryId: z7.string().describe("Entry ID to update, e.g. '<PREFIX>-<n>'"),
1582
+ name: z7.string().optional().describe("New display name"),
1574
1583
  // Chain-ID-leak guard (check:chain-id-leaks): the retired legacy-shim ticket number stays in
1575
1584
  // the code comment above, never in this user-visible describe() string. T8 (TASK-393, WP-779): narrowed to generated ENTRY_STATUS_WRITE_WORDS.
1576
- status: z6.enum(ENTRY_STATUS_WRITE_WORDS).optional().describe(`Lifecycle status: ${ENTRY_STATUS_WRITE_WORDS.join(" | ")}. 'accepted' is set via 'commit', never 'update'; 'active'/'deprecated' are legacy words and are refused here. Use \`workflowStatus\` for collection-specific workflow values \u2014 those are no longer accepted via \`status\`.`),
1577
- workflowStatus: z6.string().optional().describe("Collection workflow state. Valid values are collection-specific and server-owned \u2014 discover them via `collections action=describe` for the target collection. The server rejects invalid values and returns the valid set in the error."),
1578
- data: z6.record(z6.unknown()).optional().describe("Fields to update (merged with existing data)"),
1579
- order: z6.number().optional().describe("New sort order"),
1580
- canonicalKey: z6.string().optional().describe("Semantic type (e.g. 'decision', 'tension'). Only changeable on draft/uncommitted entries."),
1581
- autoPublish: z6.boolean().optional().default(false).describe("Accepted for backward compatibility but IGNORED \u2014 draft vs. published is decided by the entry's own accepted/active status, never by this flag. Removal tracked as a follow-up."),
1585
+ status: z7.enum(ENTRY_STATUS_WRITE_WORDS).optional().describe(`Lifecycle status: ${ENTRY_STATUS_WRITE_WORDS.join(" | ")}. 'accepted' is set via 'commit', never 'update'; 'active'/'deprecated' are legacy words and are refused here. Use \`workflowStatus\` for collection-specific workflow values \u2014 those are no longer accepted via \`status\`.`),
1586
+ workflowStatus: z7.string().optional().describe("Collection workflow state. Valid values are collection-specific and server-owned \u2014 discover them via `collections action=describe` for the target collection. The server rejects invalid values and returns the valid set in the error."),
1587
+ data: z7.record(z7.unknown()).optional().describe("Fields to update (merged with existing data)"),
1588
+ order: z7.number().optional().describe("New sort order"),
1589
+ canonicalKey: z7.string().optional().describe("Semantic type (e.g. 'decision', 'tension'). Only changeable on draft/uncommitted entries."),
1590
+ autoPublish: z7.boolean().optional().default(false).describe("Accepted for backward compatibility but IGNORED \u2014 draft vs. published is decided by the entry's own accepted/active status, never by this flag. Removal tracked as a follow-up."),
1582
1591
  // DEC-1716
1583
- changeReason: z6.string().optional().describe("Change reason: required when this update changes a promise field (or the name of a collection that declares one) of an accepted entry \u2014 why what the entry promises is changing. Recorded on the history event and the version. Not the same as changeNote."),
1584
- changeNote: z6.string().optional().describe("Strongly recommended: short human-readable rationale for WHY this change was made (e.g. 'Aligned description with F1-themed copy'). Surfaces in activity feed and pb get. If omitted, falls back to session purpose or auto-generated field summary."),
1585
- sourceRef: z6.string().optional().describe("URI or path of the source document backing this entry. Write-once: can only be set if currently empty."),
1586
- sourceExcerpt: z6.string().optional().describe("Verbatim excerpt from the source that backs this entry's claims. Write-once: can only be set if currently empty."),
1592
+ changeReason: z7.string().optional().describe("Change reason: required when this update changes a promise field (or the name of a collection that declares one) of an accepted entry \u2014 why what the entry promises is changing. Recorded on the history event and the version. Not the same as changeNote."),
1593
+ changeNote: z7.string().optional().describe("Strongly recommended: short human-readable rationale for WHY this change was made (e.g. 'Aligned description with F1-themed copy'). Surfaces in activity feed and pb get. If omitted, falls back to session purpose or auto-generated field summary."),
1594
+ sourceRef: z7.string().optional().describe("URI or path of the source document backing this entry. Write-once: can only be set if currently empty."),
1595
+ sourceExcerpt: z7.string().optional().describe("Verbatim excerpt from the source that backs this entry's claims. Write-once: can only be set if currently empty."),
1587
1596
  // WP-465 slice ⑤ — relay-only (TEN-2233): validation/min-length/recording live in Convex.
1588
- steeringOverrideReason: z6.string().optional().describe("Typed override (\u226512 chars) that clears a steering coherency block on a misaligned governance write \u2014 always recorded with author attribution."),
1589
- qualityOverrideReason: z6.string().optional().describe("Why (\u226512 chars) to keep an accepted spine text its quality checklist failed \u2014 recorded as an override with who wrote it."),
1597
+ steeringOverrideReason: z7.string().optional().describe("Typed override (\u226512 chars) that clears a steering coherency block on a misaligned governance write \u2014 always recorded with author attribution."),
1598
+ qualityOverrideReason: z7.string().optional().describe("Why (\u226512 chars) to keep an accepted spine text its quality checklist failed \u2014 recorded as an override with who wrote it."),
1590
1599
  // TASK-954
1591
- coherencyAcknowledgement: z6.object({
1592
- response: z6.enum(["linked", "accepted-fix", "diverged"]).describe("Explicit response to an acknowledge-required coherency challenge."),
1593
- entryId: z6.string().optional().describe("Authorizing entry being linked/accepted (required for 'linked' and 'accepted-fix')."),
1594
- reason: z6.string().optional().describe("Divergence rationale (required for 'diverged', \u226512 chars).")
1600
+ coherencyAcknowledgement: z7.object({
1601
+ response: z7.enum(["linked", "accepted-fix", "diverged"]).describe("Explicit response to an acknowledge-required coherency challenge."),
1602
+ entryId: z7.string().optional().describe("Authorizing entry being linked/accepted (required for 'linked' and 'accepted-fix')."),
1603
+ reason: z7.string().optional().describe("Divergence rationale (required for 'diverged', \u226512 chars).")
1595
1604
  }).optional().describe("Explicit response to a coherency challenge (standard/strict workspace modes). One acknowledgement per challenge per entry per session.")
1596
1605
  });
1597
- var commitEntrySchema = z6.object({
1598
- entryId: z6.string().describe("Entry ID to accept, e.g. 'TEN-abc123', '<PREFIX>-<n>'"),
1606
+ var commitEntrySchema = z7.object({
1607
+ entryId: z7.string().describe("Entry ID to accept, e.g. 'TEN-abc123', '<PREFIX>-<n>'"),
1599
1608
  // WP-316 S3: Preview gate — dry-run mode. Returns would-succeed result, no DB writes.
1600
- preview: z6.boolean().optional().describe("If true, validates the accept without writing. Returns what would happen. Default false."),
1609
+ preview: z7.boolean().optional().describe("If true, validates the accept without writing. Returns what would happen. Default false."),
1601
1610
  // WP-465 slice ⑤ — relay-only (TEN-2233): validation/recording live in Convex.
1602
- steeringOverrideReason: z6.string().optional().describe("Typed override (\u226512 chars) that clears a steering coherency block at the publish chokepoint \u2014 always recorded with author attribution."),
1603
- qualityOverrideReason: z6.string().optional().describe("Why (\u226512 chars) to accept over a failing quality checklist \u2014 recorded as an override with who wrote it."),
1611
+ steeringOverrideReason: z7.string().optional().describe("Typed override (\u226512 chars) that clears a steering coherency block at the publish chokepoint \u2014 always recorded with author attribution."),
1612
+ qualityOverrideReason: z7.string().optional().describe("Why (\u226512 chars) to accept over a failing quality checklist \u2014 recorded as an override with who wrote it."),
1604
1613
  // TASK-954
1605
- coherencyAcknowledgement: z6.object({
1606
- response: z6.enum(["linked", "accepted-fix", "diverged"]).describe("Explicit response to an acknowledge-required coherency challenge."),
1607
- entryId: z6.string().optional().describe("Authorizing entry being linked/accepted (required for 'linked' and 'accepted-fix')."),
1608
- reason: z6.string().optional().describe("Divergence rationale (required for 'diverged', \u226512 chars).")
1614
+ coherencyAcknowledgement: z7.object({
1615
+ response: z7.enum(["linked", "accepted-fix", "diverged"]).describe("Explicit response to an acknowledge-required coherency challenge."),
1616
+ entryId: z7.string().optional().describe("Authorizing entry being linked/accepted (required for 'linked' and 'accepted-fix')."),
1617
+ reason: z7.string().optional().describe("Divergence rationale (required for 'diverged', \u226512 chars).")
1609
1618
  }).optional().describe("Explicit response to a coherency challenge (standard/strict workspace modes).")
1610
1619
  });
1611
1620
  async function handleUpdateEntry({ entryId, name, status: rawStatus, workflowStatus: rawWorkflowStatus, data, order, canonicalKey, autoPublish, changeNote, changeReason, sourceRef, sourceExcerpt, steeringOverrideReason, qualityOverrideReason, coherencyAcknowledgement }, toolName = "entries") {
@@ -1923,7 +1932,7 @@ No DB writes \u2014 call without \`preview:true\` to accept for real.` }],
1923
1932
  // src/tools/verify.ts
1924
1933
  import { existsSync as existsSync2, readFileSync } from "fs";
1925
1934
  import { resolve as resolve2 } from "path";
1926
- import { z as z7 } from "zod/v3";
1935
+ import { z as z8 } from "zod/v3";
1927
1936
 
1928
1937
  // src/lib/resolve-project-root.ts
1929
1938
  import { existsSync } from "fs";
@@ -2075,12 +2084,12 @@ function formatTrustReport(collection, entryCount, mappings, refs, fixes, mode2,
2075
2084
  lines.push("", "---", `_Schema: ${schemaTableCount} tables parsed from convex/schema.ts. Project root: ${projectRoot}_`);
2076
2085
  return lines.join("\n");
2077
2086
  }
2078
- var verifySchema = z7.object({
2079
- collection: z7.string().max(200).default("glossary").describe("Collection slug to verify (default: glossary)"),
2080
- mode: z7.enum(["report", "fix"]).default("report").describe("'report' = read-only trust report. 'fix' = also update drifted codeMapping statuses.")
2087
+ var verifySchema = z8.object({
2088
+ collection: z8.string().max(200).default("glossary").describe("Collection slug to verify (default: glossary)"),
2089
+ mode: z8.enum(["report", "fix"]).default("report").describe("'report' = read-only trust report. 'fix' = also update drifted codeMapping statuses.")
2081
2090
  });
2082
- var verifyEntrySchema = z7.object({
2083
- entryId: z7.string().max(200).describe("Human entry ID (e.g. '<PREFIX>-<n>') to mark as verified")
2091
+ var verifyEntrySchema = z8.object({
2092
+ entryId: z8.string().max(200).describe("Human entry ID (e.g. '<PREFIX>-<n>') to mark as verified")
2084
2093
  });
2085
2094
  async function handleVerifyChain(server, { collection, mode: mode2 }) {
2086
2095
  const projectRoot = resolveProjectRoot();
@@ -2297,9 +2306,9 @@ async function handleVerifyEntry({ entryId }) {
2297
2306
  }
2298
2307
 
2299
2308
  // src/tools/stillTrue.ts
2300
- import { z as z8 } from "zod/v3";
2301
- var stillTrueEntrySchema = z8.object({
2302
- entryId: z8.string().max(200).describe("Human entry ID (e.g. '<PREFIX>-<n>') to confirm is still true.")
2309
+ import { z as z9 } from "zod/v3";
2310
+ var stillTrueEntrySchema = z9.object({
2311
+ entryId: z9.string().max(200).describe("Human entry ID (e.g. '<PREFIX>-<n>') to confirm is still true.")
2303
2312
  });
2304
2313
  async function handleStillTrueEntry({ entryId }) {
2305
2314
  requireWriteAccess();
@@ -2313,14 +2322,14 @@ async function handleStillTrueEntry({ entryId }) {
2313
2322
  }
2314
2323
 
2315
2324
  // src/tools/entry-move.ts
2316
- import { z as z9 } from "zod/v3";
2317
- var moveEntrySchema = z9.object({
2318
- entryId: z9.string().describe("Entry ID to move, e.g. '<PREFIX>-<n>'"),
2319
- toCollection: z9.string().describe("Target collection slug, e.g. 'decisions', 'architecture'"),
2325
+ import { z as z10 } from "zod/v3";
2326
+ var moveEntrySchema = z10.object({
2327
+ entryId: z10.string().describe("Entry ID to move, e.g. '<PREFIX>-<n>'"),
2328
+ toCollection: z10.string().describe("Target collection slug, e.g. 'decisions', 'architecture'"),
2320
2329
  // WP-707 S2 (TASK-146): optional atomic move+RETYPE — validates and persists the entry's NEW
2321
2330
  // canonicalKey and its destination collection in the SAME write (convex/agentKnowledge/
2322
2331
  // moveToCollectionOperation.ts round 3, DEC-1622). Omit for a plain same-kind relocation.
2323
- canonicalKey: z9.string().optional().describe(
2332
+ canonicalKey: z10.string().optional().describe(
2324
2333
  "Optional: also reclassify the entry to this canonical key atomically with the move (e.g. 'decision', 'objective'). Omit for a plain relocation."
2325
2334
  )
2326
2335
  });
@@ -2414,85 +2423,83 @@ function platformSearchLines(platform) {
2414
2423
  }
2415
2424
 
2416
2425
  // src/tools/entriesContract.ts
2417
- import { z as z10 } from "zod/v3";
2426
+ import { z as z11 } from "zod/v3";
2418
2427
  var ENTRIES_ACTIONS = [
2419
2428
  "update",
2420
2429
  "commit",
2421
2430
  "move",
2422
2431
  "verify",
2423
- "preflight",
2424
2432
  "still-true"
2425
2433
  ];
2426
- var coherencyAcknowledgementFlatSchema = z10.object({
2427
- response: z10.enum(["linked", "accepted-fix", "diverged"]).describe("Explicit response to an acknowledge-required coherency challenge."),
2428
- entryId: z10.string().max(200).optional().describe("Authorizing entry being linked/accepted (required for 'linked' and 'accepted-fix')."),
2429
- reason: z10.string().max(2e3).optional().describe("Divergence rationale (required for 'diverged', \u226512 chars).")
2434
+ var coherencyAcknowledgementFlatSchema = z11.object({
2435
+ response: z11.enum(["linked", "accepted-fix", "diverged"]).describe("Explicit response to an acknowledge-required coherency challenge."),
2436
+ entryId: z11.string().max(200).optional().describe("Authorizing entry being linked/accepted (required for 'linked' and 'accepted-fix')."),
2437
+ reason: z11.string().max(2e3).optional().describe("Divergence rationale (required for 'diverged', \u226512 chars).")
2430
2438
  });
2431
- var preflightLinks = z10.array(z10.object({ type: z10.string().max(200), entryId: z10.string().max(200) })).max(50).optional();
2432
- var entriesSchema = z10.object({
2433
- links: preflightLinks.describe("For preflight: proposed context links; saves nothing."),
2434
- action: z10.enum(ENTRIES_ACTIONS).describe(
2435
- "'preflight': inspect structural requirements before composing (requires collection). 'update': change fields on an existing entry (draft by default). 'commit': accept a draft entry onto the Chain. 'move': relocate an entry to a different collection (pass `canonicalKey` too for an atomic move+retype). 'verify': mark an entry as verified (lightweight \u2014 no codebase scan; see `quality action=verify-chain` for that). 'still-true': confirm accuracy (the holder of the owning role, else the nearest seated Team Lead) \u2014 distinct from 'verify'; no content, version, or updatedAt change."
2439
+ var ENTRIES_READS_MOVED = {
2440
+ preflight: "entries only writes. Check a collection's requirements with the read-only context tool: context action=preflight collection=..."
2441
+ };
2442
+ var entriesSchema = z11.object({
2443
+ action: actionEnum(ENTRIES_ACTIONS, ENTRIES_READS_MOVED).describe(
2444
+ "'update': change fields on an existing entry (draft by default). 'commit': accept a draft entry onto the Chain. 'move': relocate an entry to a different collection (pass `canonicalKey` too for an atomic move+retype). 'verify': mark an entry as verified (lightweight \u2014 no codebase scan; see `quality action=verify-chain` for that). 'still-true': confirm accuracy (the holder of the owning role, else the nearest seated Team Lead) \u2014 distinct from 'verify'; no content, version, or updatedAt change."
2436
2445
  ),
2437
- entryId: z10.string().max(200).optional().describe(
2446
+ entryId: z11.string().max(200).optional().describe(
2438
2447
  "Entry ID, e.g. '<PREFIX>-<n>'. Required for: update, commit, move, verify, still-true."
2439
2448
  ),
2440
- collection: z10.string().max(200).optional().describe("For 'preflight': the collection slug the entry would be composed into (required)."),
2441
- status: z10.string().max(200).optional().describe(
2449
+ status: z11.string().max(200).optional().describe(
2442
2450
  "For 'update': draft|archived ONLY \u2014 'accepted' is set via 'commit', never 'update'; 'active'/'deprecated' are legacy read-only words, refused on write. Workflow values use `workflowStatus` instead."
2443
2451
  ),
2444
- name: z10.string().max(ENTRY_NAME_MAX_CHARS).optional().describe("For 'update': new display name."),
2445
- workflowStatus: z10.string().max(200).optional().describe(
2452
+ name: z11.string().max(ENTRY_NAME_MAX_CHARS).optional().describe("For 'update': new display name."),
2453
+ workflowStatus: z11.string().max(200).optional().describe(
2446
2454
  "For 'update': collection workflow state. Valid values are collection-specific and server-owned \u2014 discover via `collections action=describe`. Invalid values are rejected with the valid set."
2447
2455
  ),
2448
- data: z10.record(z10.unknown()).optional().describe("For 'update': fields to update (merged with existing data)."),
2449
- order: z10.number().optional().describe("For 'update': new sort order."),
2450
- canonicalKey: z10.string().max(200).optional().describe(
2451
- "For 'update': semantic type (e.g. 'decision', 'tension'); only changeable on draft/uncommitted entries. For 'move': optional atomic move+retype target. For 'preflight': optional semantic type to check requirements for."
2456
+ data: z11.record(z11.unknown()).optional().describe("For 'update': fields to update (merged with existing data)."),
2457
+ order: z11.number().optional().describe("For 'update': new sort order."),
2458
+ canonicalKey: z11.string().max(200).optional().describe(
2459
+ "For 'update': semantic type (e.g. 'decision', 'tension'); only changeable on draft/uncommitted entries. For 'move': optional atomic move+retype target."
2452
2460
  ),
2453
2461
  // DEC-1716: draft vs. published is decided by the entry's own accepted/active status, never this flag.
2454
- autoPublish: z10.boolean().optional().default(false).describe(
2462
+ autoPublish: z11.boolean().optional().default(false).describe(
2455
2463
  "For 'update': accepted for backward compatibility but IGNORED \u2014 draft vs. published is decided by the entry's own accepted/active status, never by this flag."
2456
2464
  ),
2457
- changeReason: z10.string().max(2e3).optional().describe(
2465
+ changeReason: z11.string().max(2e3).optional().describe(
2458
2466
  "For 'update': Change reason \u2014 required when the update changes a promise field of an accepted entry. Recorded on the history event and the version."
2459
2467
  ),
2460
- changeNote: z10.string().max(2e3).optional().describe(
2468
+ changeNote: z11.string().max(2e3).optional().describe(
2461
2469
  "For 'update': short human-readable rationale for WHY this change was made. Surfaces in activity feed."
2462
2470
  ),
2463
- sourceRef: z10.string().max(2e3).optional().describe(
2471
+ sourceRef: z11.string().max(2e3).optional().describe(
2464
2472
  "For 'update': URI or path of the source document backing this entry. Write-once."
2465
2473
  ),
2466
- sourceExcerpt: z10.string().max(SOURCE_EXCERPT_MAX_CHARS).optional().describe(
2474
+ sourceExcerpt: z11.string().max(SOURCE_EXCERPT_MAX_CHARS).optional().describe(
2467
2475
  "For 'update': verbatim excerpt from the source backing this entry's claims. Write-once."
2468
2476
  ),
2469
- steeringOverrideReason: z10.string().max(2e3).optional().describe(
2477
+ steeringOverrideReason: z11.string().max(2e3).optional().describe(
2470
2478
  "For 'update'/'commit': typed override (\u226512 chars) that clears a steering coherency block."
2471
2479
  ),
2472
- qualityOverrideReason: z10.string().max(2e3).optional().describe(
2480
+ qualityOverrideReason: z11.string().max(2e3).optional().describe(
2473
2481
  "For 'update'/'commit': why (\u226512 chars) to keep a spine text its quality checklist failed; recorded as an override with who wrote it."
2474
2482
  ),
2475
2483
  coherencyAcknowledgement: coherencyAcknowledgementFlatSchema.optional().describe(
2476
2484
  "For 'update'/'commit': explicit response to a coherency challenge (standard/strict workspace modes)."
2477
2485
  ),
2478
- preview: z10.boolean().optional().describe(
2486
+ preview: z11.boolean().optional().describe(
2479
2487
  "For 'commit': if true, validates the accept without writing \u2014 returns what would happen."
2480
2488
  ),
2481
- toCollection: z10.string().max(200).optional().describe("For 'move': target collection slug, e.g. 'decisions', 'architecture'.")
2489
+ toCollection: z11.string().max(200).optional().describe("For 'move': target collection slug, e.g. 'decisions', 'architecture'.")
2482
2490
  });
2483
- var entriesUpdateVariant = updateEntrySchema.extend({ action: z10.literal("update") });
2484
- var entriesCommitVariant = commitEntrySchema.extend({ action: z10.literal("commit") });
2485
- var entriesMoveVariant = z10.object({
2486
- action: z10.literal("move"),
2487
- entryId: z10.string().max(200),
2488
- toCollection: z10.string().max(200),
2489
- canonicalKey: z10.string().max(200).optional()
2491
+ var entriesUpdateVariant = updateEntrySchema.extend({ action: z11.literal("update") });
2492
+ var entriesCommitVariant = commitEntrySchema.extend({ action: z11.literal("commit") });
2493
+ var entriesMoveVariant = z11.object({
2494
+ action: z11.literal("move"),
2495
+ entryId: z11.string().max(200),
2496
+ toCollection: z11.string().max(200),
2497
+ canonicalKey: z11.string().max(200).optional()
2490
2498
  // WP-707 S2: optional atomic move+RETYPE.
2491
2499
  });
2492
- var entriesVerifyVariant = verifyEntrySchema.extend({ action: z10.literal("verify") });
2493
- var entriesStillTrueVariant = stillTrueEntrySchema.extend({ action: z10.literal("still-true") });
2494
- var entriesActionUnion = z10.discriminatedUnion("action", [
2495
- z10.object({ action: z10.literal("preflight"), collection: z10.string().max(200), canonicalKey: z10.string().max(200).optional(), links: preflightLinks }),
2500
+ var entriesVerifyVariant = verifyEntrySchema.extend({ action: z11.literal("verify") });
2501
+ var entriesStillTrueVariant = stillTrueEntrySchema.extend({ action: z11.literal("still-true") });
2502
+ var entriesActionUnion = z11.discriminatedUnion("action", [
2496
2503
  entriesUpdateVariant,
2497
2504
  entriesCommitVariant,
2498
2505
  entriesMoveVariant,
@@ -2500,7 +2507,6 @@ var entriesActionUnion = z10.discriminatedUnion("action", [
2500
2507
  entriesStillTrueVariant
2501
2508
  ]);
2502
2509
  var ENTRIES_ACTION_SPECS = {
2503
- preflight: { params: ["collection", "canonicalKey", "links"], description: "Inspect structure before composition; no writes or permission grant." },
2504
2510
  update: { params: ["entryId", "name", "status", "workflowStatus", "data", "order", "canonicalKey", "autoPublish", "changeNote", "changeReason", "sourceRef", "sourceExcerpt", "steeringOverrideReason", "qualityOverrideReason", "coherencyAcknowledgement"], description: "entryId is required; every other field is optional." },
2505
2511
  commit: { params: ["entryId", "preview", "steeringOverrideReason", "qualityOverrideReason", "coherencyAcknowledgement"], description: "entryId is required." },
2506
2512
  move: { params: ["entryId", "toCollection", "canonicalKey"], description: "entryId and toCollection are required; canonicalKey is optional (atomic move+retype)." },
@@ -2562,16 +2568,16 @@ function renderVerificationLine(e) {
2562
2568
  }
2563
2569
  return `**Verification:** ${String(e.verificationStatus)}${who}`;
2564
2570
  }
2565
- var wqSchema = z11.enum(["rationale", "missing", "restated"]);
2566
- var entriesGetOutputSchema = z11.object({
2567
- entryId: z11.string(),
2568
- name: z11.string(),
2569
- collection: z11.string(),
2570
- status: z11.string(),
2571
- capturedAt: z11.number().optional(),
2572
- origin: z11.string().optional(),
2573
- originDetail: z11.string().optional(),
2574
- verificationStatus: z11.string().optional(),
2571
+ var wqSchema = z12.enum(["rationale", "missing", "restated"]);
2572
+ var entriesGetOutputSchema = z12.object({
2573
+ entryId: z12.string(),
2574
+ name: z12.string(),
2575
+ collection: z12.string(),
2576
+ status: z12.string(),
2577
+ capturedAt: z12.number().optional(),
2578
+ origin: z12.string().optional(),
2579
+ originDetail: z12.string().optional(),
2580
+ verificationStatus: z12.string().optional(),
2575
2581
  // Attestation-model finding (PR #341 review): the server's honest verifier label and
2576
2582
  // derived attestation strength/basis — connectors NEVER re-derive strength (spec §5),
2577
2583
  // they only render what chain.getEntry ships. Mirrors packages/cli EntryFromApi.
@@ -2580,80 +2586,76 @@ var entriesGetOutputSchema = z11.object({
2580
2586
  // attestation.ts's AttestationStrength SSOT (allowlisted in Check D —
2581
2587
  // scripts/check-collection-ssot.mjs's CHECK_D_ALLOWLIST — with the full
2582
2588
  // reasoning for why this can't just import that module). Update both together.
2583
- verifiedBy: z11.string().optional(),
2584
- attestation: z11.object({
2585
- strength: z11.enum(["human-direct", "delegated", "system", "unattested"]),
2586
- basis: z11.string().optional()
2589
+ verifiedBy: z12.string().optional(),
2590
+ attestation: z12.object({
2591
+ strength: z12.enum(["human-direct", "delegated", "system", "unattested"]),
2592
+ basis: z12.string().optional()
2587
2593
  }).optional(),
2588
- sourceRef: z11.string().optional(),
2589
- sourceExcerpt: z11.string().optional(),
2590
- why: z11.string().optional(),
2594
+ sourceRef: z12.string().optional(),
2595
+ sourceExcerpt: z12.string().optional(),
2596
+ why: z12.string().optional(),
2591
2597
  whyQuality: wqSchema.optional(),
2592
- data: z11.record(z11.unknown()).optional(),
2593
- relations: z11.array(z11.object({
2594
- entryId: z11.string().optional(),
2595
- name: z11.string(),
2596
- type: z11.string(),
2597
- direction: z11.string(),
2598
- status: z11.string().optional()
2598
+ data: z12.record(z12.unknown()).optional(),
2599
+ relations: z12.array(z12.object({
2600
+ entryId: z12.string().optional(),
2601
+ name: z12.string(),
2602
+ type: z12.string(),
2603
+ direction: z12.string(),
2604
+ status: z12.string().optional()
2599
2605
  })).optional(),
2600
- labels: z11.array(z11.string()).optional(),
2606
+ labels: z12.array(z12.string()).optional(),
2601
2607
  owner: ownerOutputShape.optional()
2602
2608
  }).passthrough();
2603
- var entriesListOutputSchema = z11.object({
2604
- entries: z11.array(z11.object({
2605
- entryId: z11.string(),
2606
- name: z11.string(),
2607
- collection: z11.string(),
2608
- status: z11.string()
2609
+ var entriesListOutputSchema = z12.object({
2610
+ entries: z12.array(z12.object({
2611
+ entryId: z12.string(),
2612
+ name: z12.string(),
2613
+ collection: z12.string(),
2614
+ status: z12.string()
2609
2615
  })),
2610
- total: z11.number()
2616
+ total: z12.number()
2611
2617
  });
2612
- var entriesSearchOutputSchema = z11.object({
2613
- results: z11.array(z11.object({
2614
- entryId: z11.string(),
2615
- name: z11.string(),
2616
- collection: z11.string(),
2617
- status: z11.string(),
2618
- score: z11.number().optional()
2618
+ var entriesSearchOutputSchema = z12.object({
2619
+ results: z12.array(z12.object({
2620
+ entryId: z12.string(),
2621
+ name: z12.string(),
2622
+ collection: z12.string(),
2623
+ status: z12.string(),
2624
+ score: z12.number().optional()
2619
2625
  })),
2620
- total: z11.number(),
2621
- query: z11.string()
2626
+ total: z12.number(),
2627
+ query: z12.string()
2622
2628
  });
2623
- var entriesBatchOutputSchema = z11.object({
2624
- entries: z11.array(z11.object({
2625
- entryId: z11.string(),
2626
- name: z11.string(),
2627
- collection: z11.string(),
2628
- status: z11.string(),
2629
- capturedAt: z11.number().optional(),
2630
- origin: z11.string().optional(),
2631
- originDetail: z11.string().optional(),
2632
- verificationStatus: z11.string().optional(),
2629
+ var entriesBatchOutputSchema = z12.object({
2630
+ entries: z12.array(z12.object({
2631
+ entryId: z12.string(),
2632
+ name: z12.string(),
2633
+ collection: z12.string(),
2634
+ status: z12.string(),
2635
+ capturedAt: z12.number().optional(),
2636
+ origin: z12.string().optional(),
2637
+ originDetail: z12.string().optional(),
2638
+ verificationStatus: z12.string().optional(),
2633
2639
  // Attestation-model finding (PR #341 review): mirror entriesGetOutputSchema — batch
2634
2640
  // entries now carry the same honest verifiedBy/attestation fields. Literal set is
2635
2641
  // the same hand-kept SSOT mirror — see entriesGetOutputSchema's attestation
2636
2642
  // comment above for the full Check D / kernel-import reasoning.
2637
- verifiedBy: z11.string().optional(),
2638
- attestation: z11.object({
2639
- strength: z11.enum(["human-direct", "delegated", "system", "unattested"]),
2640
- basis: z11.string().optional()
2643
+ verifiedBy: z12.string().optional(),
2644
+ attestation: z12.object({
2645
+ strength: z12.enum(["human-direct", "delegated", "system", "unattested"]),
2646
+ basis: z12.string().optional()
2641
2647
  }).optional(),
2642
- sourceRef: z11.string().optional(),
2643
- sourceExcerpt: z11.string().optional(),
2644
- why: z11.string().optional(),
2648
+ sourceRef: z12.string().optional(),
2649
+ sourceExcerpt: z12.string().optional(),
2650
+ why: z12.string().optional(),
2645
2651
  // TEN-2191: mirrors entriesGetOutputSchema
2646
2652
  whyQuality: wqSchema.optional(),
2647
- data: z11.record(z11.unknown()).optional()
2653
+ data: z12.record(z12.unknown()).optional()
2648
2654
  }).passthrough()),
2649
- total: z11.number()
2655
+ total: z12.number()
2650
2656
  });
2651
2657
  function registerEntriesTools(server) {
2652
2658
  const entriesHandlers = {
2653
- preflight: async (data) => {
2654
- const result2 = await kernelQuery("chain.preflightEntry", { collectionSlug: data.collection, canonicalKey: data.canonicalKey, links: data.links });
2655
- return { content: [{ type: "text", text: renderEntryPreflight(result2).join("\n") }], structuredContent: success("Structural requirements checked; nothing saved.", result2) };
2656
- },
2657
2659
  // update/commit reuse knowledge.ts's extracted handlers — "entries" routes
2658
2660
  // `next` at itself (toolName="entries") instead of the legacy tool names.
2659
2661
  update: (data) => handleUpdateEntry(data, "entries"),
@@ -2666,7 +2668,7 @@ function registerEntriesTools(server) {
2666
2668
  "entries",
2667
2669
  {
2668
2670
  title: "Entries",
2669
- description: 'Write entries on the Chain: the entry lifecycle after capture. Reads live on `context`.\n\n- **preflight**: Inspect a type and selected context before composing. No writes, capacity reservation or authority grant.\n- **update**: Change fields on an existing entry (publishes whenever the entry is not draft \u2014 includes archived, not just accepted/active; autoPublish is accepted but ignored).\n- **commit**: Accept a draft entry onto the Chain (SSOT). Runs the contradiction preflight first.\n- **move**: Relocate an entry to a different collection; pass canonicalKey too for an atomic move+retype.\n- **verify**: Mark verified (lightweight, no scan \u2014 see `quality action=verify-chain`).\n- **still-true**: confirm accuracy (the holder of the owning role; if that role is empty, the nearest seated Team Lead up the team tree).\n\nRead entries with the read-only `context` tool: `context action=get entryId="..."`, `context action=search query="..."`.',
2671
+ description: 'Write entries on the Chain: the entry lifecycle after capture. Reads live on `context`.\n\n- **update**: Change fields on an existing entry (publishes whenever the entry is not draft \u2014 includes archived, not just accepted/active; autoPublish is accepted but ignored).\n- **commit**: Accept a draft entry onto the Chain (SSOT). Runs the contradiction preflight first.\n- **move**: Relocate an entry to a different collection; pass canonicalKey too for an atomic move+retype.\n- **verify**: Mark verified (lightweight, no scan \u2014 see `quality action=verify-chain`).\n- **still-true**: confirm accuracy (the holder of the owning role; if that role is empty, the nearest seated Team Lead up the team tree).\n\nRead entries with the read-only `context` tool: `context action=get entryId="..."`, `context action=search query="..."`, `context action=preflight`.',
2670
2672
  inputSchema: entriesSchema,
2671
2673
  // DEC-1894 / DEC-74: every look-up lives on the read-only `context` tool, so this
2672
2674
  // annotation tells hosts the truth about the writes left here.
@@ -2683,6 +2685,10 @@ function registerEntriesTools(server) {
2683
2685
  })
2684
2686
  );
2685
2687
  }
2688
+ async function handleEntryPreflight(collection, canonicalKey, links) {
2689
+ const result2 = await kernelQuery("chain.preflightEntry", { collectionSlug: collection, canonicalKey, links });
2690
+ return { content: [{ type: "text", text: renderEntryPreflight(result2).join("\n") }], structuredContent: success("Structural requirements checked; nothing saved.", result2) };
2691
+ }
2686
2692
  async function handleGet(entryId) {
2687
2693
  const entry = await kernelQuery("chain.getEntry", { entryId });
2688
2694
  if (!entry) {
@@ -3010,42 +3016,42 @@ ${footer}` }],
3010
3016
  }
3011
3017
 
3012
3018
  // src/tools/relations.ts
3013
- import { z as z12 } from "zod/v3";
3019
+ import { z as z13 } from "zod/v3";
3014
3020
  var RELATIONS_ACTIONS = ["create", "batch-create", "delete"];
3015
- var relationItemSchema = z12.object({
3016
- from: z12.string().max(200),
3017
- to: z12.string().max(200),
3018
- type: z12.string().max(200)
3021
+ var relationItemSchema = z13.object({
3022
+ from: z13.string().max(200),
3023
+ to: z13.string().max(200),
3024
+ type: z13.string().max(200)
3019
3025
  });
3020
- var relationsSchema = z12.object({
3021
- action: z12.enum(RELATIONS_ACTIONS).describe(
3026
+ var relationsSchema = z13.object({
3027
+ action: z13.enum(RELATIONS_ACTIONS).describe(
3022
3028
  "'create': link two entries. 'batch-create': create multiple relations (validates every item before writing any). 'delete': remove a relation."
3023
3029
  ),
3024
- from: z12.string().max(200).optional().describe("For 'create'/'delete': source entry ID."),
3025
- to: z12.string().max(200).optional().describe("For 'create'/'delete': target entry ID."),
3026
- type: z12.string().max(200).optional().describe("For 'create'/'delete': relation type."),
3027
- relations: z12.array(relationItemSchema).min(1).max(RELATION_BATCH_MAX).optional().describe("For 'batch-create': array of {from, to, type}."),
3030
+ from: z13.string().max(200).optional().describe("For 'create'/'delete': source entry ID."),
3031
+ to: z13.string().max(200).optional().describe("For 'create'/'delete': target entry ID."),
3032
+ type: z13.string().max(200).optional().describe("For 'create'/'delete': relation type."),
3033
+ relations: z13.array(relationItemSchema).min(1).max(RELATION_BATCH_MAX).optional().describe("For 'batch-create': array of {from, to, type}."),
3028
3034
  // WP-316 S3: Preview gate — dry-run mode for action=create.
3029
- preview: z12.boolean().optional().describe("For 'create': if true, validates the relation without writing. Returns what would happen. Default false.")
3035
+ preview: z13.boolean().optional().describe("For 'create': if true, validates the relation without writing. Returns what would happen. Default false.")
3030
3036
  });
3031
- var relationsCreateVariant = z12.object({
3032
- action: z12.literal("create"),
3033
- from: z12.string().max(200),
3034
- to: z12.string().max(200),
3035
- type: z12.string().max(200),
3036
- preview: z12.boolean().optional()
3037
+ var relationsCreateVariant = z13.object({
3038
+ action: z13.literal("create"),
3039
+ from: z13.string().max(200),
3040
+ to: z13.string().max(200),
3041
+ type: z13.string().max(200),
3042
+ preview: z13.boolean().optional()
3037
3043
  });
3038
- var relationsBatchCreateVariant = z12.object({
3039
- action: z12.literal("batch-create"),
3040
- relations: z12.array(relationItemSchema).min(1).max(RELATION_BATCH_MAX)
3044
+ var relationsBatchCreateVariant = z13.object({
3045
+ action: z13.literal("batch-create"),
3046
+ relations: z13.array(relationItemSchema).min(1).max(RELATION_BATCH_MAX)
3041
3047
  });
3042
- var relationsDeleteVariant = z12.object({
3043
- action: z12.literal("delete"),
3044
- from: z12.string().max(200),
3045
- to: z12.string().max(200),
3046
- type: z12.string().max(200)
3048
+ var relationsDeleteVariant = z13.object({
3049
+ action: z13.literal("delete"),
3050
+ from: z13.string().max(200),
3051
+ to: z13.string().max(200),
3052
+ type: z13.string().max(200)
3047
3053
  });
3048
- var relationsActionUnion = z12.discriminatedUnion("action", [
3054
+ var relationsActionUnion = z13.discriminatedUnion("action", [
3049
3055
  relationsCreateVariant,
3050
3056
  relationsBatchCreateVariant,
3051
3057
  relationsDeleteVariant
@@ -3267,22 +3273,22 @@ async function handleDelete(from, to, type) {
3267
3273
  }
3268
3274
 
3269
3275
  // src/tools/context.ts
3270
- import { z as z18 } from "zod/v3";
3276
+ import { z as z20 } from "zod/v3";
3271
3277
 
3272
3278
  // src/lib/readinessNotices.ts
3273
3279
  var READINESS_PROVISIONAL_LINE = "Readiness is provisional: its sample omitted accepted entries. Counts are lower bounds, apparent gaps may already be covered.";
3274
3280
 
3275
3281
  // src/tools/documents.ts
3276
- import { z as z13 } from "zod/v3";
3282
+ import { z as z14 } from "zod/v3";
3277
3283
  var DOCUMENTS_ACTIONS = ["get-last-verified-brief"];
3278
- var documentsSchema = z13.object({
3279
- action: z13.enum(DOCUMENTS_ACTIONS).describe(
3284
+ var documentsSchema = z14.object({
3285
+ action: z14.enum(DOCUMENTS_ACTIONS).describe(
3280
3286
  "'get-last-verified-brief': fetch the most recent verified brief snapshot for a (templateId, scopeKey) pair. Returns the verified summary so agents can build delta narratives ('since you last verified, X workstreams advanced')."
3281
3287
  ),
3282
- templateId: z13.string().max(200).describe(
3288
+ templateId: z14.string().max(200).describe(
3283
3289
  "Brief template identifier \u2014 currently 'steering-brief' is the only registered template."
3284
3290
  ),
3285
- scopeKey: z13.string().max(200).describe(
3291
+ scopeKey: z14.string().max(200).describe(
3286
3292
  "Canonical scope key. Use 'workspace:<workspaceId>' for the full workspace brief, or 'initiative:<INI-ID>' for an initiative-scoped brief. The same formula is applied at write time (chainwork/docKernel/scopeKey.ts), so passing the wrong shape returns exists:false."
3287
3293
  )
3288
3294
  });
@@ -3351,27 +3357,27 @@ This row pre-dates snapshot capture (legacy backfill), so no summary is availabl
3351
3357
  }
3352
3358
 
3353
3359
  // src/tools/contextLookups.ts
3354
- import { z as z17 } from "zod/v3";
3360
+ import { z as z19 } from "zod/v3";
3355
3361
 
3356
3362
  // src/tools/graph.ts
3357
- import { z as z14 } from "zod/v3";
3363
+ import { z as z15 } from "zod/v3";
3358
3364
  var GRAPH_ACTIONS = ["find"];
3359
- var graphSchema = z14.object({
3360
- action: z14.enum(GRAPH_ACTIONS).describe(
3365
+ var graphSchema = z15.object({
3366
+ action: z15.enum(GRAPH_ACTIONS).describe(
3361
3367
  "'find': traverse relations from an entry (graph walk)."
3362
3368
  ),
3363
- entryId: z14.string().max(200).describe("Entry ID, e.g. '<PREFIX>-<n>'"),
3364
- direction: z14.enum(["incoming", "outgoing", "both"]).default("both").optional().describe("For find: 'incoming' = what references this, 'outgoing' = what this references")
3369
+ entryId: z15.string().max(200).describe("Entry ID, e.g. '<PREFIX>-<n>'"),
3370
+ direction: z15.enum(["incoming", "outgoing", "both"]).default("both").optional().describe("For find: 'incoming' = what references this, 'outgoing' = what this references")
3365
3371
  });
3366
- var graphFindOutputSchema = z14.object({
3367
- entryId: z14.string(),
3368
- relations: z14.array(z14.object({
3369
- entryId: z14.string().optional(),
3370
- name: z14.string(),
3371
- type: z14.string(),
3372
- direction: z14.enum(["outgoing", "incoming"])
3372
+ var graphFindOutputSchema = z15.object({
3373
+ entryId: z15.string(),
3374
+ relations: z15.array(z15.object({
3375
+ entryId: z15.string().optional(),
3376
+ name: z15.string(),
3377
+ type: z15.string(),
3378
+ direction: z15.enum(["outgoing", "incoming"])
3373
3379
  })),
3374
- total: z14.number()
3380
+ total: z15.number()
3375
3381
  });
3376
3382
  async function handleFind(entryId, direction) {
3377
3383
  const relations = await kernelQuery("chain.listEntryRelations", { entryId });
@@ -3472,7 +3478,7 @@ async function handleFind(entryId, direction) {
3472
3478
  }
3473
3479
 
3474
3480
  // src/tools/people.ts
3475
- import { z as z15 } from "zod/v3";
3481
+ import { z as z16 } from "zod/v3";
3476
3482
 
3477
3483
  // src/lib/seatSentence.ts
3478
3484
  var PERSON_STATUS_WORDS = {
@@ -3528,27 +3534,27 @@ var ORGANISATION_VALIDATION_REFUSALS = /* @__PURE__ */ new Set([
3528
3534
 
3529
3535
  // src/tools/people.ts
3530
3536
  var PEOPLE_ACTIONS = ["invite", "remove"];
3531
- var peopleSchema = z15.object({
3532
- action: z15.enum(PEOPLE_ACTIONS).describe("'invite': send a login link by email. 'remove': take a person out of a team."),
3533
- email: z15.string().max(320).optional().describe("'invite': where the login link goes. Required for 'invite'."),
3534
- person: z15.string().max(320).optional().describe(
3537
+ var peopleSchema = z16.object({
3538
+ action: z16.enum(PEOPLE_ACTIONS).describe("'invite': send a login link by email. 'remove': take a person out of a team."),
3539
+ email: z16.string().max(320).optional().describe("'invite': where the login link goes. Required for 'invite'."),
3540
+ person: z16.string().max(320).optional().describe(
3535
3541
  "'invite': a placeholder's person id, instead of newPersonName. 'remove': the person, by person id or exact email."
3536
3542
  ),
3537
- newPersonName: z15.string().max(120).optional().describe("'invite': the name of a new person to invite, instead of person."),
3538
- teamId: z15.string().max(200).optional().describe("'remove': the team, by chain id or entry id. Required for 'remove'.")
3543
+ newPersonName: z16.string().max(120).optional().describe("'invite': the name of a new person to invite, instead of person."),
3544
+ teamId: z16.string().max(200).optional().describe("'remove': the team, by chain id or entry id. Required for 'remove'.")
3539
3545
  });
3540
- var inviteVariant = z15.object({
3541
- action: z15.literal("invite"),
3542
- email: z15.string().max(320),
3543
- person: z15.string().max(320).optional(),
3544
- newPersonName: z15.string().max(120).optional()
3546
+ var inviteVariant = z16.object({
3547
+ action: z16.literal("invite"),
3548
+ email: z16.string().max(320),
3549
+ person: z16.string().max(320).optional(),
3550
+ newPersonName: z16.string().max(120).optional()
3545
3551
  });
3546
- var removeVariant = z15.object({
3547
- action: z15.literal("remove"),
3548
- person: z15.string().max(320),
3549
- teamId: z15.string().max(200)
3552
+ var removeVariant = z16.object({
3553
+ action: z16.literal("remove"),
3554
+ person: z16.string().max(320),
3555
+ teamId: z16.string().max(200)
3550
3556
  });
3551
- var peopleActionUnion = z15.discriminatedUnion("action", [inviteVariant, removeVariant]);
3557
+ var peopleActionUnion = z16.discriminatedUnion("action", [inviteVariant, removeVariant]);
3552
3558
  var PEOPLE_ACTION_SPECS = {
3553
3559
  invite: { params: ["email", "person", "newPersonName"], description: "email is required, plus exactly one of person (a placeholder's id) or newPersonName." },
3554
3560
  remove: { params: ["person", "teamId"], description: "person and teamId are required." }
@@ -3658,7 +3664,7 @@ function registerPeopleTools(server) {
3658
3664
  }
3659
3665
 
3660
3666
  // src/tools/outcome.ts
3661
- import { z as z16 } from "zod/v3";
3667
+ import { z as z17 } from "zod/v3";
3662
3668
 
3663
3669
  // src/lib/outcomeObjectiveLines.ts
3664
3670
  var LOOKUP_LINE = {
@@ -3687,40 +3693,39 @@ function formatOutcomeObjectiveLines(view, clean) {
3687
3693
  var OUTCOME_ACTIONS = ["record"];
3688
3694
  var OUTCOME_READS_MOVED = "outcome only records readings. Read with the read-only context tool: context action=outcome entryId=... (the derived view, its objective and sibling key results) or context action=readings entryId=... (the readings, newest first).";
3689
3695
  var READING_KINDS = ["measurement", "estimate", "correction"];
3690
- var outcomeSchema = z16.object({
3691
- // The host validates arguments before the handler runs, so the pointer to the moved reads rides the enum's own error.
3692
- action: z16.enum(OUTCOME_ACTIONS, { errorMap: () => ({ message: OUTCOME_READS_MOVED }) }).describe(
3696
+ var outcomeSchema = z17.object({
3697
+ action: actionEnum(OUTCOME_ACTIONS, { list: OUTCOME_READS_MOVED, show: OUTCOME_READS_MOVED }).describe(
3693
3698
  "'record': record a reading (evidence, not a verdict). Reads live on context: action=outcome and action=readings."
3694
3699
  ),
3695
- entryId: z16.string().max(200).optional().describe("The key result (or other outcome-bearing) entry, by its chain id. Required."),
3700
+ entryId: z17.string().max(200).optional().describe("The key result (or other outcome-bearing) entry, by its chain id. Required."),
3696
3701
  // PR #858 review round 3 (Codex P2, PRRT_kwDORS4ils6gXHj9): a JSON number that overflows to
3697
3702
  // `Infinity` (e.g. `1e400`) parses fine in JS and passes a plain `z.number()`, but
3698
3703
  // `callGateway()` serializes it with `JSON.stringify`, which turns `Infinity` into `null` —
3699
3704
  // the backend's `v.number()` validator then rejects a call this tool's own schema accepted.
3700
3705
  // `.finite()` rejects it here, matching the explicit finite-value checks the CLI/other outcome
3701
3706
  // entry surfaces already use.
3702
- value: z16.number().finite().optional().describe("'record': the measured value. Required for 'record'."),
3703
- valueText: z16.string().max(500).optional().describe("'record': verbatim as-reported form when the source was not clean, e.g. '~62%'. Display only."),
3704
- asOf: z16.string().max(64).optional().describe("'record': when the world was in this state, as a parseable date (default: now). Back-dating a backfill is legitimate \u2014 it shows up as asOf earlier than recordedAt."),
3705
- kind: z16.enum(READING_KINDS).optional().describe("'record': measurement | estimate | correction (default: measurement)."),
3706
- correctsReadingId: z16.string().max(200).optional().describe("'record': required with kind='correction' \u2014 the reading id this replaces. Refused if that row is already superseded."),
3707
- recordedByRoleId: z16.string().max(200).optional().describe("'record': the accountable role (default: the entry's reporterRoleId, then its owning role)."),
3708
- note: z16.string().max(2e3).optional().describe("'record': free-text note recorded with the reading."),
3709
- source: z16.string().max(500).optional().describe("'record': where the number came from \u2014 a dashboard name, a query, a URL.")
3707
+ value: z17.number().finite().optional().describe("'record': the measured value. Required for 'record'."),
3708
+ valueText: z17.string().max(500).optional().describe("'record': verbatim as-reported form when the source was not clean, e.g. '~62%'. Display only."),
3709
+ asOf: z17.string().max(64).optional().describe("'record': when the world was in this state, as a parseable date (default: now). Back-dating a backfill is legitimate \u2014 it shows up as asOf earlier than recordedAt."),
3710
+ kind: z17.enum(READING_KINDS).optional().describe("'record': measurement | estimate | correction (default: measurement)."),
3711
+ correctsReadingId: z17.string().max(200).optional().describe("'record': required with kind='correction' \u2014 the reading id this replaces. Refused if that row is already superseded."),
3712
+ recordedByRoleId: z17.string().max(200).optional().describe("'record': the accountable role (default: the entry's reporterRoleId, then its owning role)."),
3713
+ note: z17.string().max(2e3).optional().describe("'record': free-text note recorded with the reading."),
3714
+ source: z17.string().max(500).optional().describe("'record': where the number came from \u2014 a dashboard name, a query, a URL.")
3710
3715
  });
3711
- var recordVariant = z16.object({
3712
- action: z16.literal("record"),
3713
- entryId: z16.string().max(200),
3714
- value: z16.number().finite(),
3715
- valueText: z16.string().max(500).optional(),
3716
- asOf: z16.string().max(64).optional(),
3717
- kind: z16.enum(READING_KINDS).optional(),
3718
- correctsReadingId: z16.string().max(200).optional(),
3719
- recordedByRoleId: z16.string().max(200).optional(),
3720
- note: z16.string().max(2e3).optional(),
3721
- source: z16.string().max(500).optional()
3716
+ var recordVariant = z17.object({
3717
+ action: z17.literal("record"),
3718
+ entryId: z17.string().max(200),
3719
+ value: z17.number().finite(),
3720
+ valueText: z17.string().max(500).optional(),
3721
+ asOf: z17.string().max(64).optional(),
3722
+ kind: z17.enum(READING_KINDS).optional(),
3723
+ correctsReadingId: z17.string().max(200).optional(),
3724
+ recordedByRoleId: z17.string().max(200).optional(),
3725
+ note: z17.string().max(2e3).optional(),
3726
+ source: z17.string().max(500).optional()
3722
3727
  });
3723
- var outcomeActionUnion = z16.discriminatedUnion("action", [recordVariant]);
3728
+ var outcomeActionUnion = z17.discriminatedUnion("action", [recordVariant]);
3724
3729
  var OUTCOME_ACTION_SPECS = {
3725
3730
  record: {
3726
3731
  params: ["entryId", "value", "valueText", "asOf", "kind", "correctsReadingId", "recordedByRoleId", "note", "source"],
@@ -3915,49 +3920,276 @@ function registerOutcomeTools(server) {
3915
3920
  );
3916
3921
  }
3917
3922
 
3923
+ // src/tools/role.ts
3924
+ import { z as z18 } from "zod/v3";
3925
+ var ROLE_ACTIONS = ["seat", "unseat", "release_move", "accept_move", "refuse_move"];
3926
+ var ROLE_READS_MOVED = {
3927
+ accountable: "role only writes. Read what a role owns and who holds it with the read-only context tool: context action=accountable roleId=..."
3928
+ };
3929
+ var roleSchema = z18.object({
3930
+ action: actionEnum(ROLE_ACTIONS, ROLE_READS_MOVED).describe(
3931
+ "'seat': put a person in this role. 'unseat': take this role from a person. 'release_move' / 'accept_move' / 'refuse_move': move a role or team to a team with another Team Lead."
3932
+ ),
3933
+ roleId: z18.string().max(200).optional().describe("The role, by the human chain id `chain.getEntry` or `pb get` would print for it, or a raw entry id."),
3934
+ person: z18.string().max(200).optional().describe(
3935
+ "'seat' / 'unseat': an existing person, by person id or exact email. For 'seat', give this or newPersonName, not both."
3936
+ ),
3937
+ newPersonName: z18.string().max(120).optional().describe(
3938
+ "'seat': the name of a person who has no login yet; a placeholder person is created once and reused for the same exact name. Refused when the name belongs to someone with an account."
3939
+ ),
3940
+ teamId: z18.string().max(200).optional().describe(
3941
+ "'seat' / 'unseat': the team, by chain id or entry id. Only needed for Team Lead, which lives in every team; any other role is seated in its home team. 'release_move': the team it moves to."
3942
+ ),
3943
+ onMyAuthority: z18.boolean().optional().describe(
3944
+ "'seat', Team Lead only: as the team's Team Lead, seat a second Team Lead who holds the seat on your authority until you unseat them."
3945
+ ),
3946
+ childId: z18.string().max(200).optional().describe("'release_move' / 'accept_move' / 'refuse_move': the role or team that moves, by chain id or entry id."),
3947
+ why: z18.string().max(1e3).optional().describe("'refuse_move': why the move is refused; it becomes a tension for the old team's Team Lead.")
3948
+ });
3949
+ var seatVariant = z18.object({
3950
+ action: z18.literal("seat"),
3951
+ roleId: z18.string().max(200),
3952
+ person: z18.string().max(200).optional(),
3953
+ newPersonName: z18.string().max(120).optional(),
3954
+ teamId: z18.string().max(200).optional(),
3955
+ onMyAuthority: z18.boolean().optional()
3956
+ });
3957
+ var unseatVariant = z18.object({
3958
+ action: z18.literal("unseat"),
3959
+ roleId: z18.string().max(200),
3960
+ person: z18.string().max(200),
3961
+ teamId: z18.string().max(200).optional()
3962
+ });
3963
+ var releaseMoveVariant = z18.object({ action: z18.literal("release_move"), childId: z18.string().max(200), teamId: z18.string().max(200) });
3964
+ var acceptMoveVariant = z18.object({ action: z18.literal("accept_move"), childId: z18.string().max(200) });
3965
+ var refuseMoveVariant = z18.object({ action: z18.literal("refuse_move"), childId: z18.string().max(200), why: z18.string().max(1e3) });
3966
+ var roleActionUnion = z18.discriminatedUnion("action", [
3967
+ seatVariant,
3968
+ unseatVariant,
3969
+ releaseMoveVariant,
3970
+ acceptMoveVariant,
3971
+ refuseMoveVariant
3972
+ ]);
3973
+ var ROLE_ACTION_SPECS = {
3974
+ seat: {
3975
+ params: ["roleId", "person", "newPersonName", "teamId", "onMyAuthority"],
3976
+ description: "roleId is required, plus exactly one of person or newPersonName; teamId and onMyAuthority only for Team Lead."
3977
+ },
3978
+ unseat: { params: ["roleId", "person", "teamId"], description: "roleId and person are required; teamId only for Team Lead." },
3979
+ release_move: { params: ["childId", "teamId"], description: "childId and teamId (the team it moves to) are required." },
3980
+ accept_move: { params: ["childId"], description: "childId is required." },
3981
+ refuse_move: { params: ["childId", "why"], description: "childId and why are required." }
3982
+ };
3983
+ function formatHolders(result2) {
3984
+ if (result2.holders.length === 0) return [` holders: none${result2.holdersTruncated ? " found, but the membership scan is incomplete" : ""}`];
3985
+ return [
3986
+ " holders:",
3987
+ ...result2.holders.map((h) => {
3988
+ const word = personStatusTag(h.status);
3989
+ const tag = word ? ` (${word})` : "";
3990
+ const team = h.teamName ? ` in ${sanitizeSingleLineField(h.teamName)}` : "";
3991
+ return ` ${sanitizeSingleLineField(h.displayName)}${tag}${team}`;
3992
+ }),
3993
+ ...result2.holdersTruncated ? [" (membership scan cut at 500 rows, more holders may exist)"] : []
3994
+ ];
3995
+ }
3996
+ function formatRow(row) {
3997
+ const id = row.entryId ?? row.id;
3998
+ const statusDisplay = row.workflowStatus ? `${row.status} / ${sanitizeSingleLineField(row.workflowStatus)}` : row.status;
3999
+ const collectionSlug = row.collectionSlug ? sanitizeSingleLineField(row.collectionSlug) : "?";
4000
+ return ` ${id} ${sanitizeSingleLineField(row.name)} [${collectionSlug}/${statusDisplay}]`;
4001
+ }
4002
+ function formatSection(label, rows, truncated, cursorHint) {
4003
+ if (rows.length === 0 && !truncated) return [` ${label}: not recorded yet`];
4004
+ const lines = rows.length === 0 ? [` ${label}: not recorded yet, but the scan is incomplete`] : [` ${label}:`, ...rows.map(formatRow)];
4005
+ if (truncated) lines.push(` (scan incomplete, more may exist \u2014 ${cursorHint ?? "raise limit (capped at 200, no cursor for this list)"})`);
4006
+ return lines;
4007
+ }
4008
+ function formatRoleAccountable(result2) {
4009
+ const roleLabel2 = `${sanitizeSingleLineField(result2.role.name)} (${result2.role.entryId ?? result2.role.id})`;
4010
+ const explicitCursorHint = result2.nextCursor !== null ? `raise limit or continue with cursor ${result2.nextCursor}` : "raise limit";
4011
+ return [
4012
+ `${roleLabel2} \u2014 accountability:`,
4013
+ ...formatHolders(result2),
4014
+ ...formatSection("explicit (owns)", result2.explicit, result2.explicitTruncated, explicitCursorHint),
4015
+ ...formatSection("reporting (reports on)", result2.reporting, result2.reportingTruncated),
4016
+ ` inherited: not-computed \u2014 ${result2.inheritedNotComputedReason}`
4017
+ ].join("\n");
4018
+ }
4019
+ async function handleRoleAccountable(roleId, canonicalKey, limit, cursor) {
4020
+ try {
4021
+ const raw = await kernelQuery("chain.listByRole", { roleId, canonicalKey, limit, cursor });
4022
+ const result2 = raw;
4023
+ const message = formatRoleAccountable(result2);
4024
+ return successResult(message, message, result2);
4025
+ } catch (err) {
4026
+ return roleErrorResult(err, "accountable", { roleId, canonicalKey, limit, cursor });
4027
+ }
4028
+ }
4029
+ async function runSeatAction(action, fn, params) {
4030
+ try {
4031
+ requireWriteAccess();
4032
+ const sessionId = getAgentSessionId();
4033
+ const raw = await kernelMutation(fn, {
4034
+ roleRef: params.roleId,
4035
+ targetPersonRef: params.person,
4036
+ newPersonName: params.newPersonName,
4037
+ teamRef: params.teamId,
4038
+ sessionId,
4039
+ ...params.onMyAuthority ? { onMyAuthority: true } : {}
4040
+ });
4041
+ const result2 = raw.refusal ? { ...raw, refusal: { ...raw.refusal, requiredAction: authorityRecovery(raw.refusal) } } : raw;
4042
+ const message = formatSeatSentence(action, result2, sanitizeSingleLineField);
4043
+ if (result2.outcome === "refused") {
4044
+ return failureResult(
4045
+ message,
4046
+ "PERMISSION_DENIED",
4047
+ message,
4048
+ result2.refusal?.requiredAction ?? "Ask the team's Team Lead to seat them.",
4049
+ void 0,
4050
+ result2
4051
+ );
4052
+ }
4053
+ return successResult(message, message, result2);
4054
+ } catch (err) {
4055
+ return roleErrorResult(err, action, params);
4056
+ }
4057
+ }
4058
+ function handleRoleSeat(roleId, person, newPersonName, teamId, onMyAuthority) {
4059
+ return runSeatAction("seat", "organisation.seatInRole", { roleId, person, newPersonName, teamId, onMyAuthority });
4060
+ }
4061
+ function handleRoleUnseat(roleId, person, teamId) {
4062
+ return runSeatAction("unseat", "organisation.unseatFromRole", { roleId, person, teamId });
4063
+ }
4064
+ function callMove(action, params, sessionId) {
4065
+ if (action === "release_move") {
4066
+ return kernelMutation("organisation.hierarchy.release", { childRef: params.childId, toParentRef: params.teamId, sessionId });
4067
+ }
4068
+ if (action === "accept_move") return kernelMutation("organisation.hierarchy.accept", { childRef: params.childId, sessionId });
4069
+ return kernelMutation("organisation.hierarchy.refuse", { childRef: params.childId, why: params.why, sessionId });
4070
+ }
4071
+ async function handleMove(action, params) {
4072
+ try {
4073
+ requireWriteAccess();
4074
+ const raw = await callMove(action, params, getAgentSessionId());
4075
+ if (raw.outcome === "refused") {
4076
+ const recovery = authorityRecovery(raw.refusal) ?? "Ask the Team Lead the refusal names.";
4077
+ const message2 = `Refused: ${sanitizeSingleLineField(raw.refusal.reason)} ${recovery}`;
4078
+ const code = ORGANISATION_VALIDATION_REFUSALS.has(raw.refusal.code) ? "VALIDATION_ERROR" : "PERMISSION_DENIED";
4079
+ return failureResult(message2, code, message2, recovery, void 0, raw);
4080
+ }
4081
+ const message = raw.replaced ? `${raw.line} It replaces the release toward ${sanitizeSingleLineField(raw.replaced)}.` : raw.line;
4082
+ return successResult(message, message, raw);
4083
+ } catch (err) {
4084
+ return roleErrorResult(err, action, params);
4085
+ }
4086
+ }
4087
+ function roleErrorResult(err, action, params = {}) {
4088
+ const classified = classifyError(err);
4089
+ const definedParams = Object.fromEntries(Object.entries(params).filter(([, v]) => v !== void 0));
4090
+ const fallbackActions = retryIsFutile(classified.code) ? void 0 : [action === "accountable" ? { tool: "context", description: "Retry accountable", parameters: { action, ...definedParams } } : { tool: "role", description: `Retry ${action}`, parameters: { action, ...definedParams } }];
4091
+ return failureResult(
4092
+ classified.message,
4093
+ classified.code,
4094
+ classified.message,
4095
+ classified.recovery ?? (action === "accountable" ? "Use context action=accountable with roleId set to a real role's chain id or entry id." : `Fix the input the message names and call action='${action}' again.`),
4096
+ classified.availableActions ?? fallbackActions,
4097
+ classified.diagnostics
4098
+ );
4099
+ }
4100
+ function registerRoleTools(server) {
4101
+ const roleHandlers = {
4102
+ seat: (data) => handleRoleSeat(data.roleId, data.person, data.newPersonName, data.teamId, data.onMyAuthority),
4103
+ unseat: (data) => handleRoleUnseat(data.roleId, data.person, data.teamId),
4104
+ release_move: (data) => handleMove("release_move", data),
4105
+ accept_move: (data) => handleMove("accept_move", data),
4106
+ refuse_move: (data) => handleMove("refuse_move", data)
4107
+ };
4108
+ server.registerTool(
4109
+ "role",
4110
+ {
4111
+ title: "Role",
4112
+ description: "Workspace roles \u2014 who holds a role and where it sits. Five actions:\n\n- **seat**: put a person in this role, by person id or email, or by `newPersonName` for someone with no login yet (a placeholder person, created once per exact name). The role's home team is used; name `teamId` only for Team Lead. A held role refuses a second holder and names the holder; the person keeps their other roles in the team. Needs the team's Team Lead. With `onMyAuthority`, the team's Team Lead seats a second Team Lead on their own authority; unseating them revokes it.\n- **unseat**: take this role from a person; their other roles in the team stay. The last active Team Lead of a team is refused. Invite a person, or take them out of a team, with the `people` tool.\n- **release_move** / **accept_move** / **refuse_move**: move a role or team (`childId`) to a team whose Team Lead is another seat. Acting as Team Lead of its current team, release it toward `teamId`; acting as Team Lead of that team, accept it, or refuse it with `why` (it stays, and a tension goes to its current team's Team Lead).\n\nRead what a role owns with `context action=accountable`.",
4113
+ inputSchema: roleSchema,
4114
+ // The hints describe the whole tool, so they say the most it can do: unseat removes a role and can
4115
+ // delete a membership row (destructive), and a repeated seat still writes history and audit rows
4116
+ // (not idempotent); the one read moved to `context` (DEC-1894 step 2). Closed world: nothing here
4117
+ // reaches outside the workspace since invite moved to `people` (TASK-677, DEC-1981).
4118
+ annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: true, openWorldHint: false }
4119
+ },
4120
+ thinWrapper(async (args) => {
4121
+ const parsed = parseOrFail(roleSchema, args);
4122
+ if (!parsed.ok) return parsed.result;
4123
+ const { action } = parsed.data;
4124
+ return runWithToolContext(
4125
+ { tool: "role", action },
4126
+ () => dispatchDiscriminated("role", roleActionUnion, parsed.data, ROLE_ACTION_SPECS, roleHandlers)
4127
+ );
4128
+ })
4129
+ );
4130
+ }
4131
+
3918
4132
  // src/tools/contextLookups.ts
3919
- var LOOKUP_ACTIONS = ["get", "batch", "history", "list", "search", "find", "people", "outcome", "readings"];
3920
- var statusFilterSchema = z17.union([z17.string().max(200), z17.array(z17.enum(ENTRY_STATUS_READ_WORDS)).max(5)]);
3921
- var LOOKUP_ACTION_DESCRIBE = "'get': one entry in full by ID. 'batch': up to 20 entries by ID. 'history': audit trail for an entry. 'list': browse entries with filters (accepted by default). 'search': full-text search across entries. 'find': an entry's direct relations (direction: incoming|outgoing|both, default both). 'people': the workspace's members with name, email and status. 'outcome': a key result's derived view, its objective and sibling key results. 'readings': its readings, newest first. ";
3922
- var LOOKUP_TOOL_LINES = "- **get**: Fetch a single entry by ID \u2014 full record with data, labels, relations, history.\n- **batch**: Fetch multiple entries (max 20) in one call. Same shape as get per entry.\n- **history**: Audit trail for an entry.\n- **list**: Browse entries with optional filters (collection, status, tag, label). Use collections action=list first to discover slugs.\n- **search**: Full-text search across entries. Scope by collection or filter by status.\n- **find**: An entry's direct relations, incoming and outgoing. NOT full-text search (use search).\n- **people**: The workspace's members: name, email, status, role. Max 200.\n- **outcome**: A key result's derived view (current, progress, pace, staleness, recorded completion) with its objective and up to 5 sibling key results.\n- **readings**: A key result's readings, newest first; superseded rows are marked with their replacement. Record one with `outcome action=record`.\n";
4133
+ var LOOKUP_ACTIONS = ["get", "batch", "history", "list", "search", "find", "people", "outcome", "readings", "accountable", "preflight"];
4134
+ var statusFilterSchema = z19.union([z19.string().max(200), z19.array(z19.enum(ENTRY_STATUS_READ_WORDS)).max(5)]);
4135
+ var preflightLinks = z19.array(z19.object({ type: z19.string().max(200), entryId: z19.string().max(200) })).max(50).optional();
4136
+ var LOOKUP_ACTION_DESCRIBE = "'get': one entry in full by ID. 'batch': up to 20 entries by ID. 'history': audit trail for an entry. 'list': browse entries with filters (accepted by default). 'search': full-text search across entries. 'find': an entry's direct relations (direction: incoming|outgoing|both, default both). 'people': the workspace's members with name, email and status. 'outcome': a key result's derived view, its objective and sibling key results. 'readings': its readings, newest first. 'accountable': what a role owns and who holds it. 'preflight': a collection's requirements. ";
4137
+ var LOOKUP_TOOL_LINES = "- **get**: Fetch a single entry by ID \u2014 full record with data, labels, relations, history.\n- **batch**: Fetch multiple entries (max 20) in one call. Same shape as get per entry.\n- **history**: Audit trail for an entry.\n- **list**: Browse entries with optional filters (collection, status, tag, label). Use collections action=list first to discover slugs.\n- **search**: Full-text search across entries. Scope by collection or filter by status.\n- **find**: An entry's direct relations, incoming and outgoing. NOT full-text search (use search).\n- **people**: The workspace's members: name, email, status, role. Max 200.\n- **outcome**: A key result's derived view (current, progress, pace, staleness, recorded completion) with its objective and up to 5 sibling key results.\n- **readings**: A key result's readings, newest first; superseded rows are marked with their replacement. Record one with `outcome action=record`.\n- **accountable**: What a role owns, reports on, and who holds it.\n- **preflight**: What an entry in a collection needs before you compose it. Saves nothing.\n";
3923
4138
  var lookupParams = {
3924
- entryIds: z17.array(z17.string().max(200)).min(1).max(20).optional().describe("For 'batch': entry IDs, e.g. ['TYPE-strategy', 'STR-jljeg7']"),
3925
- collection: z17.string().max(200).optional().describe("For 'list'/'search': collection slug, e.g. 'glossary', 'tracking-events'."),
3926
- query: z17.string().min(2).max(500).optional().describe("For 'search': search text (min 2 characters)."),
4139
+ entryIds: z19.array(z19.string().max(200)).min(1).max(20).optional().describe("For 'batch': entry IDs, e.g. ['TYPE-strategy', 'STR-jljeg7']"),
4140
+ collection: z19.string().max(200).optional().describe("For 'list'/'search': collection slug, e.g. 'glossary', 'tracking-events'. Required for 'preflight'."),
4141
+ query: z19.string().min(2).max(500).optional().describe("For 'search': search text (min 2 characters)."),
3927
4142
  // 'active'/'accepted'/'current' are the same trust level (DEC-1575/DEC-1788) — all enumerated, never one substituted.
3928
4143
  status: statusFilterSchema.optional().describe(
3929
4144
  `For 'list': defaults to accepted entries (active/accepted/current); use status='draft' to discover drafts. One lifecycle or category value (${ENTRY_STATUS_READ_WORDS.join("|")}). For 'search': one value OR an array of up to 5 (e.g. ['active','accepted']).`
3930
4145
  ),
3931
- tag: z17.string().max(200).optional().describe("For 'list': filter by internal tag."),
3932
- label: z17.string().max(200).optional().describe("For 'list': filter by label slug \u2014 matches entries across all collections."),
3933
- limit: z17.number().int().positive().max(200).optional().describe("For 'readings': max rows to return (default and maximum 200).")
4146
+ tag: z19.string().max(200).optional().describe("For 'list': filter by internal tag."),
4147
+ label: z19.string().max(200).optional().describe("For 'list': filter by label slug \u2014 matches entries across all collections."),
4148
+ limit: z19.number().int().positive().max(200).optional().describe("For 'readings'/'accountable': max rows (default and maximum 200)."),
4149
+ roleId: z19.string().max(200).optional().describe("For 'accountable': the role's id (required)."),
4150
+ canonicalKey: z19.string().max(200).optional().describe("For 'accountable'/'preflight': one kind, e.g. tension."),
4151
+ cursor: z19.number().optional().describe("For 'accountable': nextCursor of an earlier page."),
4152
+ links: preflightLinks.describe("For 'preflight': proposed links.")
3934
4153
  };
3935
4154
  var lookupVariants = [
3936
- z17.object({ action: z17.literal("get"), entryId: z17.string().max(200) }),
3937
- z17.object({ action: z17.literal("batch"), entryIds: z17.array(z17.string().max(200)).min(1).max(20) }),
3938
- z17.object({ action: z17.literal("history"), entryId: z17.string().max(200) }),
3939
- z17.object({
3940
- action: z17.literal("list"),
3941
- collection: z17.string().max(200).optional(),
3942
- status: z17.string().max(200).optional(),
4155
+ z19.object({ action: z19.literal("get"), entryId: z19.string().max(200) }),
4156
+ z19.object({ action: z19.literal("batch"), entryIds: z19.array(z19.string().max(200)).min(1).max(20) }),
4157
+ z19.object({ action: z19.literal("history"), entryId: z19.string().max(200) }),
4158
+ z19.object({
4159
+ action: z19.literal("list"),
4160
+ collection: z19.string().max(200).optional(),
4161
+ status: z19.string().max(200).optional(),
3943
4162
  // one status word; the flat union serves search
3944
- tag: z17.string().max(200).optional(),
3945
- label: z17.string().max(200).optional()
4163
+ tag: z19.string().max(200).optional(),
4164
+ label: z19.string().max(200).optional()
3946
4165
  }),
3947
- z17.object({
3948
- action: z17.literal("search"),
3949
- query: z17.string().min(2).max(500),
3950
- collection: z17.string().max(200).optional(),
4166
+ z19.object({
4167
+ action: z19.literal("search"),
4168
+ query: z19.string().min(2).max(500),
4169
+ collection: z19.string().max(200).optional(),
3951
4170
  status: statusFilterSchema.optional()
3952
4171
  }),
3953
- z17.object({
3954
- action: z17.literal("find"),
3955
- entryId: z17.string().max(200),
3956
- direction: z17.enum(["incoming", "outgoing", "both"]).optional().default("both")
4172
+ z19.object({
4173
+ action: z19.literal("find"),
4174
+ entryId: z19.string().max(200),
4175
+ direction: z19.enum(["incoming", "outgoing", "both"]).optional().default("both")
4176
+ }),
4177
+ z19.object({ action: z19.literal("people") }),
4178
+ z19.object({ action: z19.literal("outcome"), entryId: z19.string().max(200) }),
4179
+ z19.object({ action: z19.literal("readings"), entryId: z19.string().max(200), limit: z19.number().int().positive().max(200).optional() }),
4180
+ z19.object({
4181
+ action: z19.literal("accountable"),
4182
+ roleId: z19.string().max(200),
4183
+ canonicalKey: z19.string().max(200).optional(),
4184
+ limit: z19.number().int().positive().max(200).optional(),
4185
+ cursor: z19.number().optional()
3957
4186
  }),
3958
- z17.object({ action: z17.literal("people") }),
3959
- z17.object({ action: z17.literal("outcome"), entryId: z17.string().max(200) }),
3960
- z17.object({ action: z17.literal("readings"), entryId: z17.string().max(200), limit: z17.number().int().positive().max(200).optional() })
4187
+ z19.object({
4188
+ action: z19.literal("preflight"),
4189
+ collection: z19.string().max(200),
4190
+ canonicalKey: z19.string().max(200).optional(),
4191
+ links: preflightLinks
4192
+ })
3961
4193
  ];
3962
4194
  var LOOKUP_ACTION_SPECS = {
3963
4195
  get: { params: ["entryId"], description: "entryId is required." },
@@ -3968,7 +4200,9 @@ var LOOKUP_ACTION_SPECS = {
3968
4200
  find: { params: ["entryId", "direction"], description: "entryId is required." },
3969
4201
  people: { params: [], description: "No params." },
3970
4202
  outcome: { params: ["entryId"], description: "entryId is required." },
3971
- readings: { params: ["entryId", "limit"], description: "entryId is required; limit is optional." }
4203
+ readings: { params: ["entryId", "limit"], description: "entryId is required; limit is optional." },
4204
+ accountable: { params: ["roleId", "canonicalKey", "limit", "cursor"], description: "roleId is required; canonicalKey/limit/cursor are optional." },
4205
+ preflight: { params: ["collection", "canonicalKey", "links"], description: "collection is required; saves nothing." }
3972
4206
  };
3973
4207
  function lookupHandlers(server) {
3974
4208
  return {
@@ -3982,7 +4216,10 @@ function lookupHandlers(server) {
3982
4216
  people: () => handlePeopleList(),
3983
4217
  outcome: (data) => handleOutcomeShow(data.entryId),
3984
4218
  // DEC-1894 step 2 (TASK-817)
3985
- readings: (data) => handleOutcomeList(data.entryId, data.limit)
4219
+ readings: (data) => handleOutcomeList(data.entryId, data.limit),
4220
+ accountable: (data) => handleRoleAccountable(data.roleId, data.canonicalKey, data.limit, data.cursor),
4221
+ // DEC-1894 step 2 (TASK-1004)
4222
+ preflight: (data) => handleEntryPreflight(data.collection, data.canonicalKey, data.links)
3986
4223
  };
3987
4224
  }
3988
4225
 
@@ -3998,90 +4235,90 @@ function epistemicCollectionHint(collectionName) {
3998
4235
  return "";
3999
4236
  }
4000
4237
  var CONTEXT_ACTIONS = [...LOOKUP_ACTIONS, "gather", "build", "neighborhood", "changes", "chain", "cross-cut", "incremental", "brief", "last-verified-brief"];
4001
- var contextSchema = z18.object({
4238
+ var contextSchema = z20.object({
4002
4239
  // provenance: neighborhood BET-142; changes/chain/cross-cut/incremental/brief BET-239 (E4, E6)
4003
- action: z18.enum(CONTEXT_ACTIONS).describe(
4240
+ action: z20.enum(CONTEXT_ACTIONS).describe(
4004
4241
  LOOKUP_ACTION_DESCRIBE + "'gather': assemble knowledge context (entry graph, task auto-load, journey mode, or graph mode). 'build': structured build spec for an entry. 'neighborhood': typed graph neighborhood for an entry \u2014 blocking chain, dependencies, parent context, tensions, staleness. 'changes': entries modified and relations created since a timestamp. Requires 'since' parameter. 'chain': directed traversal along one relation type to depth 4. Requires entryId. Optional: direction, relationType, maxHops (1-4). 'cross-cut': structural aggregation \u2014 all relations of a given type grouped by source collection. Requires 'relationType' parameter. 'incremental': delta since last brief run for a skill. Requires 'skill' parameter. Returns only entries changed since the skill's last brief. 'brief': compound intelligence query. Requires 'briefType' parameter: 'steering' (changes + structure + delta + readiness), 'confidence' (changes + active bets \u2014 use the pb CLI's `tensions list` for tensions), or 'delta' (changes + relations since timestamp). Optional 'since' for delta type. 'last-verified-brief': fetch the most recent verified brief snapshot for a (templateId, scopeKey) pair (absorbs documents action=get-last-verified-brief). Requires templateId and scopeKey."
4005
4242
  ),
4006
- entryId: z18.string().max(200).optional().describe("For 'get'/'history'/'find'/'outcome'/'readings'/'build'/'neighborhood'/'chain': entry ID. For 'gather': optional (entry-graph mode)."),
4243
+ entryId: z20.string().max(200).optional().describe("For 'get'/'history'/'find'/'outcome'/'readings'/'build'/'neighborhood'/'chain': entry ID. For 'gather': optional (entry-graph mode)."),
4007
4244
  ...lookupParams,
4008
- mapEntryId: z18.string().max(200).optional().describe(
4245
+ mapEntryId: z20.string().max(200).optional().describe(
4009
4246
  "For 'gather': journey map entry ID for journey-aware context. Returns context organised by journey stage. Takes precedence over entryId when both are supplied. Example: '<PREFIX>-<n>'."
4010
4247
  ),
4011
- task: z18.string().max(2e3).optional().describe("For 'gather': natural-language task description for loading task-relevant governance, binding constraints, and supporting context."),
4012
- since: z18.string().max(200).optional().describe(
4248
+ task: z20.string().max(2e3).optional().describe("For 'gather': natural-language task description for loading task-relevant governance, binding constraints, and supporting context."),
4249
+ since: z20.string().max(200).optional().describe(
4013
4250
  "For 'changes': ISO 8601 timestamp \u2014 returns entries/relations modified since this time. For 'brief' briefType='delta': optional custom timestamp. Example: '2026-03-24T00:00:00Z'."
4014
4251
  ),
4015
- direction: z18.enum(["outgoing", "incoming", "both"]).optional().describe("For 'chain': 'outgoing' follows relations from source, 'incoming' to source (default outgoing). For 'find': incoming|outgoing|both (default both)."),
4016
- relationType: z18.string().max(200).optional().describe(
4252
+ direction: z20.enum(["outgoing", "incoming", "both"]).optional().describe("For 'chain': 'outgoing' follows relations from source, 'incoming' to source (default outgoing). For 'find': incoming|outgoing|both (default both)."),
4253
+ relationType: z20.string().max(200).optional().describe(
4017
4254
  "Relation type filter. For 'chain': optional filter to traverse only this relation type. For 'cross-cut': required \u2014 scans all relations of this type across the workspace. Examples: 'part_of', 'informs', 'governs', 'depends_on'."
4018
4255
  ),
4019
- mode: z18.enum(["search", "graph"]).default("search").optional().describe("For gather: 'search' (default) or 'graph' (enhanced with provenance paths). Ignored when mapEntryId is provided."),
4020
- maxHops: z18.number().min(1).max(4).default(2).describe("Relation traversal depth (1=direct only, 2=default, 3=wide net, 4=deep chain walk)"),
4021
- maxResults: z18.number().min(1).max(25).default(10).optional().describe("Max entries to return in gather task mode (default 10)"),
4022
- strategy: z18.enum(["hybrid", "keyword"]).default("keyword").optional().describe("Seed strategy for task-based gather: 'keyword' (FTS only, default) or 'hybrid' (vector + FTS). Only affects task mode."),
4023
- skill: z18.string().max(200).optional().describe(
4256
+ mode: z20.enum(["search", "graph"]).default("search").optional().describe("For gather: 'search' (default) or 'graph' (enhanced with provenance paths). Ignored when mapEntryId is provided."),
4257
+ maxHops: z20.number().min(1).max(4).default(2).describe("Relation traversal depth (1=direct only, 2=default, 3=wide net, 4=deep chain walk)"),
4258
+ maxResults: z20.number().min(1).max(25).default(10).optional().describe("Max entries to return in gather task mode (default 10)"),
4259
+ strategy: z20.enum(["hybrid", "keyword"]).default("keyword").optional().describe("Seed strategy for task-based gather: 'keyword' (FTS only, default) or 'hybrid' (vector + FTS). Only affects task mode."),
4260
+ skill: z20.string().max(200).optional().describe(
4024
4261
  "Skill name for 'incremental' action \u2014 identifies which skill's brief history to compare against. Examples: 'preflight', 'shaping', 'review'. Required when action is 'incremental'."
4025
4262
  ),
4026
- briefType: z18.enum(["steering", "confidence", "delta"]).optional().describe(
4263
+ briefType: z20.enum(["steering", "confidence", "delta"]).optional().describe(
4027
4264
  "Compound query type for 'brief' action. 'steering': 7d changes + structural aggregation (part_of, depends_on, constrains) + incremental delta + workspace readiness. 'confidence': 7d changes + active bets summary (tensions: use the pb CLI's `tensions list`, not this action). 'delta': changes + relations since a custom timestamp (use 'since' param). Required when action is 'brief'."
4028
4265
  ),
4029
- templateId: z18.string().max(200).optional().describe(
4266
+ templateId: z20.string().max(200).optional().describe(
4030
4267
  "For 'last-verified-brief': brief template identifier \u2014 currently 'steering-brief' is the only registered template."
4031
4268
  ),
4032
- scopeKey: z18.string().max(200).optional().describe(
4269
+ scopeKey: z20.string().max(200).optional().describe(
4033
4270
  "For 'last-verified-brief': canonical scope key \u2014 'workspace:<workspaceId>' or 'initiative:<INI-ID>'."
4034
4271
  )
4035
4272
  });
4036
- var contextGatherVariant = z18.object({
4037
- action: z18.literal("gather"),
4038
- entryId: z18.string().max(200).optional(),
4039
- mapEntryId: z18.string().max(200).optional(),
4040
- task: z18.string().max(2e3).optional(),
4041
- mode: z18.enum(["search", "graph"]).optional().default("search"),
4042
- maxHops: z18.number().min(1).max(4).optional().default(2),
4043
- maxResults: z18.number().min(1).max(25).optional().default(10),
4044
- strategy: z18.enum(["hybrid", "keyword"]).optional().default("keyword")
4273
+ var contextGatherVariant = z20.object({
4274
+ action: z20.literal("gather"),
4275
+ entryId: z20.string().max(200).optional(),
4276
+ mapEntryId: z20.string().max(200).optional(),
4277
+ task: z20.string().max(2e3).optional(),
4278
+ mode: z20.enum(["search", "graph"]).optional().default("search"),
4279
+ maxHops: z20.number().min(1).max(4).optional().default(2),
4280
+ maxResults: z20.number().min(1).max(25).optional().default(10),
4281
+ strategy: z20.enum(["hybrid", "keyword"]).optional().default("keyword")
4045
4282
  });
4046
- var contextBuildVariant = z18.object({
4047
- action: z18.literal("build"),
4048
- entryId: z18.string().max(200),
4049
- maxHops: z18.number().min(1).max(4).optional().default(2)
4283
+ var contextBuildVariant = z20.object({
4284
+ action: z20.literal("build"),
4285
+ entryId: z20.string().max(200),
4286
+ maxHops: z20.number().min(1).max(4).optional().default(2)
4050
4287
  });
4051
- var contextNeighborhoodVariant = z18.object({
4052
- action: z18.literal("neighborhood"),
4053
- entryId: z18.string().max(200)
4288
+ var contextNeighborhoodVariant = z20.object({
4289
+ action: z20.literal("neighborhood"),
4290
+ entryId: z20.string().max(200)
4054
4291
  });
4055
- var contextChangesVariant = z18.object({
4056
- action: z18.literal("changes"),
4057
- since: z18.string().max(200)
4292
+ var contextChangesVariant = z20.object({
4293
+ action: z20.literal("changes"),
4294
+ since: z20.string().max(200)
4058
4295
  });
4059
- var contextChainVariant = z18.object({
4060
- action: z18.literal("chain"),
4061
- entryId: z18.string().max(200),
4062
- direction: z18.enum(["outgoing", "incoming"]).optional().default("outgoing"),
4063
- maxHops: z18.number().min(1).max(4).optional().default(2),
4064
- relationType: z18.string().max(200).optional()
4296
+ var contextChainVariant = z20.object({
4297
+ action: z20.literal("chain"),
4298
+ entryId: z20.string().max(200),
4299
+ direction: z20.enum(["outgoing", "incoming"]).optional().default("outgoing"),
4300
+ maxHops: z20.number().min(1).max(4).optional().default(2),
4301
+ relationType: z20.string().max(200).optional()
4065
4302
  });
4066
- var contextCrossCutVariant = z18.object({
4067
- action: z18.literal("cross-cut"),
4068
- relationType: z18.string().max(200)
4303
+ var contextCrossCutVariant = z20.object({
4304
+ action: z20.literal("cross-cut"),
4305
+ relationType: z20.string().max(200)
4069
4306
  });
4070
- var contextIncrementalVariant = z18.object({
4071
- action: z18.literal("incremental"),
4072
- skill: z18.string().max(200)
4307
+ var contextIncrementalVariant = z20.object({
4308
+ action: z20.literal("incremental"),
4309
+ skill: z20.string().max(200)
4073
4310
  });
4074
- var contextBriefVariant = z18.object({
4075
- action: z18.literal("brief"),
4076
- briefType: z18.enum(["steering", "confidence", "delta"]),
4077
- since: z18.string().max(200).optional()
4311
+ var contextBriefVariant = z20.object({
4312
+ action: z20.literal("brief"),
4313
+ briefType: z20.enum(["steering", "confidence", "delta"]),
4314
+ since: z20.string().max(200).optional()
4078
4315
  });
4079
- var contextLastVerifiedBriefVariant = z18.object({
4080
- action: z18.literal("last-verified-brief"),
4081
- templateId: z18.string().max(200),
4082
- scopeKey: z18.string().max(200)
4316
+ var contextLastVerifiedBriefVariant = z20.object({
4317
+ action: z20.literal("last-verified-brief"),
4318
+ templateId: z20.string().max(200),
4319
+ scopeKey: z20.string().max(200)
4083
4320
  });
4084
- var contextActionUnion = z18.discriminatedUnion("action", [
4321
+ var contextActionUnion = z20.discriminatedUnion("action", [
4085
4322
  ...lookupVariants,
4086
4323
  contextGatherVariant,
4087
4324
  contextBuildVariant,
@@ -5067,7 +5304,7 @@ function formatTimeAgo(ms) {
5067
5304
  }
5068
5305
 
5069
5306
  // src/tools/collections.ts
5070
- import { z as z20 } from "zod/v3";
5307
+ import { z as z22 } from "zod/v3";
5071
5308
 
5072
5309
  // src/lib/scopedCache.ts
5073
5310
  var ScopedCache = class {
@@ -5129,17 +5366,17 @@ ${entry.guidance}`).join("\n\n");
5129
5366
  }
5130
5367
 
5131
5368
  // src/tools/labels.ts
5132
- import { z as z19 } from "zod/v3";
5133
- var labelsSchema = z19.object({
5134
- action: z19.enum(["list", "create", "update", "delete", "apply", "remove"]).describe("Action: list all labels, create/update/delete a label, or apply/remove a label on an entry"),
5135
- slug: z19.string().max(200).optional().describe("Label slug (required for create/update/delete/apply/remove)"),
5136
- name: z19.string().max(ENTRY_NAME_MAX_CHARS).optional().describe("Display name (required for create)"),
5137
- color: z19.string().max(50).optional().describe("Hex color, e.g. '#ef4444'"),
5138
- description: z19.string().max(LABEL_DESCRIPTION_MAX_CHARS).optional().describe("What this label means"),
5139
- parentSlug: z19.string().max(200).optional().describe("Parent group slug for label hierarchy"),
5140
- isGroup: z19.boolean().optional().describe("True if this is a group container, not a taggable label"),
5141
- order: z19.number().optional().describe("Sort order within its group"),
5142
- entryId: z19.string().max(200).optional().describe("Entry ID for apply/remove actions")
5369
+ import { z as z21 } from "zod/v3";
5370
+ var labelsSchema = z21.object({
5371
+ action: z21.enum(["list", "create", "update", "delete", "apply", "remove"]).describe("Action: list all labels, create/update/delete a label, or apply/remove a label on an entry"),
5372
+ slug: z21.string().max(200).optional().describe("Label slug (required for create/update/delete/apply/remove)"),
5373
+ name: z21.string().max(ENTRY_NAME_MAX_CHARS).optional().describe("Display name (required for create)"),
5374
+ color: z21.string().max(50).optional().describe("Hex color, e.g. '#ef4444'"),
5375
+ description: z21.string().max(LABEL_DESCRIPTION_MAX_CHARS).optional().describe("What this label means"),
5376
+ parentSlug: z21.string().max(200).optional().describe("Parent group slug for label hierarchy"),
5377
+ isGroup: z21.boolean().optional().describe("True if this is a group container, not a taggable label"),
5378
+ order: z21.number().optional().describe("Sort order within its group"),
5379
+ entryId: z21.string().max(200).optional().describe("Entry ID for apply/remove actions")
5143
5380
  });
5144
5381
  async function handleLabelsList() {
5145
5382
  const labels = await kernelQuery("chain.listLabels");
@@ -5244,108 +5481,108 @@ var COLLECTIONS_ACTIONS = [
5244
5481
  "label-remove"
5245
5482
  ];
5246
5483
  var STATUS_CATEGORIES2 = ["draft", "current", "past", "discarded"];
5247
- var statusListEntrySchema = z20.object({ label: z20.string().max(200), category: z20.enum(STATUS_CATEGORIES2), archiveDefault: z20.boolean().optional(), resolveDefault: z20.boolean().optional() });
5248
- var qualityCriterionSchema = z20.object({
5249
- field: z20.string().max(200).describe("Entry data field key this criterion applies to, e.g. 'description', 'owner'"),
5484
+ var statusListEntrySchema = z22.object({ label: z22.string().max(200), category: z22.enum(STATUS_CATEGORIES2), archiveDefault: z22.boolean().optional(), resolveDefault: z22.boolean().optional() });
5485
+ var qualityCriterionSchema = z22.object({
5486
+ field: z22.string().max(200).describe("Entry data field key this criterion applies to, e.g. 'description', 'owner'"),
5250
5487
  // WP-480 S1: `max_length` — must mirror the Convex rule union, or the tool rejects a
5251
5488
  // valid criterion before the request ever reaches the server.
5252
- rule: z20.enum(["required", "min_length", "max_length", "pattern"]).describe("'required': field must be non-empty (blocks accept). 'min_length': minimum string length (warns). 'max_length': maximum string length (warns). 'pattern': regex match (warns)."),
5253
- value: z20.string().max(500).optional().describe("For min_length/max_length: the length bound as a string integer. For pattern: the regex string. Unused for 'required'.")
5489
+ rule: z22.enum(["required", "min_length", "max_length", "pattern"]).describe("'required': field must be non-empty (blocks accept). 'min_length': minimum string length (warns). 'max_length': maximum string length (warns). 'pattern': regex match (warns)."),
5490
+ value: z22.string().max(500).optional().describe("For min_length/max_length: the length bound as a string integer. For pattern: the regex string. Unused for 'required'.")
5254
5491
  });
5255
- var fieldSchema = z20.object({
5256
- key: z20.string().max(200).describe("Field key, e.g. 'description', 'severity', 'status'"),
5257
- label: z20.string().max(200).describe("Display label, e.g. 'Description', 'Severity'"),
5258
- type: z20.string().max(50).describe("Field type: 'string', 'select', 'array', 'number', 'boolean'"),
5259
- required: z20.boolean().optional().describe("Whether this field is required"),
5260
- options: z20.array(z20.string().max(200)).max(200).optional().describe("Options for 'select' type fields"),
5261
- searchable: z20.boolean().optional().describe("Whether this field is included in full-text search"),
5262
- displayHint: z20.enum(["hero", "badge", "meta", "section", "hidden", "inline-meta"]).optional().describe("V2 rendering hint: how the field should be displayed in Cortex"),
5263
- zone: z20.enum(["header", "body", "meta"]).optional().describe("V2 layout zone: where the field appears in the entry view"),
5264
- colorMap: z20.record(z20.string().max(50)).optional().describe("V2 value-to-semantic-color mapping, e.g. { critical: 'danger', low: 'success' }"),
5265
- accentSource: z20.boolean().optional().describe("When true, this field's colorMap value drives the card-level accent styling"),
5266
- iconMap: z20.record(z20.string(), z20.string().max(50)).optional().describe("Maps field values to icons (emoji/symbol), prepended to badge text"),
5267
- helpText: z20.string().max(2e3).optional().describe("Help text shown in admin field editors"),
5268
- optionDescriptions: z20.record(z20.string().max(500)).optional().describe("Per-option guidance for select fields"),
5269
- semanticRole: z20.enum(["problem", "appetite", "elements", "architecture", "done_when", "risks", "exclusions"]).optional().describe("Semantic role for schema-driven consumers \u2014 enables field-key-independent validation and rendering"),
5270
- maxLength: z20.number().optional().describe("Maximum character length for field values. Three-tier resolution: explicit > displayHint > type fallback."),
5271
- minLength: z20.number().optional().describe("Minimum character length for field values. Only explicit \u2014 no defaults.")
5492
+ var fieldSchema = z22.object({
5493
+ key: z22.string().max(200).describe("Field key, e.g. 'description', 'severity', 'status'"),
5494
+ label: z22.string().max(200).describe("Display label, e.g. 'Description', 'Severity'"),
5495
+ type: z22.string().max(50).describe("Field type: 'string', 'select', 'array', 'number', 'boolean'"),
5496
+ required: z22.boolean().optional().describe("Whether this field is required"),
5497
+ options: z22.array(z22.string().max(200)).max(200).optional().describe("Options for 'select' type fields"),
5498
+ searchable: z22.boolean().optional().describe("Whether this field is included in full-text search"),
5499
+ displayHint: z22.enum(["hero", "badge", "meta", "section", "hidden", "inline-meta"]).optional().describe("V2 rendering hint: how the field should be displayed in Cortex"),
5500
+ zone: z22.enum(["header", "body", "meta"]).optional().describe("V2 layout zone: where the field appears in the entry view"),
5501
+ colorMap: z22.record(z22.string().max(50)).optional().describe("V2 value-to-semantic-color mapping, e.g. { critical: 'danger', low: 'success' }"),
5502
+ accentSource: z22.boolean().optional().describe("When true, this field's colorMap value drives the card-level accent styling"),
5503
+ iconMap: z22.record(z22.string(), z22.string().max(50)).optional().describe("Maps field values to icons (emoji/symbol), prepended to badge text"),
5504
+ helpText: z22.string().max(2e3).optional().describe("Help text shown in admin field editors"),
5505
+ optionDescriptions: z22.record(z22.string().max(500)).optional().describe("Per-option guidance for select fields"),
5506
+ semanticRole: z22.enum(["problem", "appetite", "elements", "architecture", "done_when", "risks", "exclusions"]).optional().describe("Semantic role for schema-driven consumers \u2014 enables field-key-independent validation and rendering"),
5507
+ maxLength: z22.number().optional().describe("Maximum character length for field values. Three-tier resolution: explicit > displayHint > type fallback."),
5508
+ minLength: z22.number().optional().describe("Minimum character length for field values. Only explicit \u2014 no defaults.")
5272
5509
  });
5273
- var collectionsSchema = z20.object({
5274
- action: z20.enum(COLLECTIONS_ACTIONS).describe(
5510
+ var collectionsSchema = z22.object({
5511
+ action: z22.enum(COLLECTIONS_ACTIONS).describe(
5275
5512
  "'list': browse all collections. 'update': update an existing collection. 'describe': full documentation for one collection \u2014 fields, option guides, usage guidance, examples. 'audit': health report for all collections \u2014 missing classification, displayHint coverage, and field schema gaps. 'export': full system_collection_definitions export with classification metadata (thinkingLayer, classificationPriority, classificationCheck, classificationSignals, governanceRole, governanceFunction, timelineRole, canBeElementOf, descriptionFieldKey). Admin only. 'label-list'/'label-create'/'label-update'/'label-delete'/'label-apply'/'label-remove': manage workspace labels (absorbs the `labels` tool). Creating a new collection is not offered here \u2014 ask a platform admin to provision it, or enable the module ontology pack that owns the drawer."
5276
5513
  ),
5277
- slug: z20.string().max(200).optional().describe("URL-safe identifier for create/update, e.g. 'glossary', 'tech-debt'. For label-*: label slug."),
5278
- name: z20.string().max(ENTRY_NAME_MAX_CHARS).optional().describe("For label-*: the label's name. For update: refused (the catalog's)."),
5279
- description: z20.string().max(2e4).optional().describe("For label-*: what the label means (max 2,000 chars). For update: refused (the catalog's)."),
5280
- purpose: z20.string().max(2e3).optional().describe("Why this collection exists \u2014 strategic reason"),
5281
- icon: z20.string().max(50).optional().describe("For update: always refused \u2014 a collection's icon comes from the platform catalog."),
5282
- navGroup: z20.enum(["daily", "strategic", "governance", "reference", "collections"]).optional().describe("Sidebar placement: 'daily', 'strategic', 'governance', 'reference', 'collections'"),
5283
- fields: z20.array(fieldSchema).max(200).optional().describe("Field definitions for create, or replacement schema for update (replaces all fields)"),
5284
- axis: z20.enum(["work", "none", "pending"]).optional().describe("REQUIRED for create: 'work' (needs stageLabelMap + validWorkflowStatuses), 'none', or 'pending'. For update: always refused \u2014 a collection's axis comes from the platform catalog."),
5285
- stageLabelMap: z20.record(z20.string(), z20.enum(["open", "in_progress", "done", "stopped", "tag"])).optional().describe("Required when axis is 'work': maps each valid workflowStatus to a canonical stage."),
5286
- defaultCanonicalKey: z20.string().max(200).optional().describe("The canonical_key entries in this collection default to (e.g. 'decision', 'insight'). Consumers read from collection doc; code map is fallback."),
5287
- defaultWorkflowStatus: z20.string().max(200).optional().describe("Default workflowStatus for new entries. For update: always refused \u2014 the status words come from the platform catalog."),
5288
- validWorkflowStatuses: z20.array(z20.string().max(200)).max(50).optional().describe("The allowed workflowStatus values for entries in this collection. For update: always refused \u2014 the status words come from the platform catalog."),
5514
+ slug: z22.string().max(200).optional().describe("URL-safe identifier for create/update, e.g. 'glossary', 'tech-debt'. For label-*: label slug."),
5515
+ name: z22.string().max(ENTRY_NAME_MAX_CHARS).optional().describe("For label-*: the label's name. For update: refused (the catalog's)."),
5516
+ description: z22.string().max(2e4).optional().describe("For label-*: what the label means (max 2,000 chars). For update: refused (the catalog's)."),
5517
+ purpose: z22.string().max(2e3).optional().describe("Why this collection exists \u2014 strategic reason"),
5518
+ icon: z22.string().max(50).optional().describe("For update: always refused \u2014 a collection's icon comes from the platform catalog."),
5519
+ navGroup: z22.enum(["daily", "strategic", "governance", "reference", "collections"]).optional().describe("Sidebar placement: 'daily', 'strategic', 'governance', 'reference', 'collections'"),
5520
+ fields: z22.array(fieldSchema).max(200).optional().describe("Field definitions for create, or replacement schema for update (replaces all fields)"),
5521
+ axis: z22.enum(["work", "none", "pending"]).optional().describe("REQUIRED for create: 'work' (needs stageLabelMap + validWorkflowStatuses), 'none', or 'pending'. For update: always refused \u2014 a collection's axis comes from the platform catalog."),
5522
+ stageLabelMap: z22.record(z22.string(), z22.enum(["open", "in_progress", "done", "stopped", "tag"])).optional().describe("Required when axis is 'work': maps each valid workflowStatus to a canonical stage."),
5523
+ defaultCanonicalKey: z22.string().max(200).optional().describe("The canonical_key entries in this collection default to (e.g. 'decision', 'insight'). Consumers read from collection doc; code map is fallback."),
5524
+ defaultWorkflowStatus: z22.string().max(200).optional().describe("Default workflowStatus for new entries. For update: always refused \u2014 the status words come from the platform catalog."),
5525
+ validWorkflowStatuses: z22.array(z22.string().max(200)).max(50).optional().describe("The allowed workflowStatus values for entries in this collection. For update: always refused \u2014 the status words come from the platform catalog."),
5289
5526
  // ENT-65, FEAT-200
5290
- classificationCheck: z20.string().max(500).optional().describe("LLM decision-tree check for this collection (3\u2013500 chars). Guides the classifier in routing entries here."),
5291
- classificationPriority: z20.number().optional().describe("Classifier priority (1\u20139, lower = higher priority). Used with classificationCheck to order the decision tree."),
5292
- qualityCriteria: z20.array(qualityCriterionSchema).max(50).optional().describe("Per-collection accept gate rules. 'required' rule hard-blocks accepts on empty fields; 'min_length'/'pattern' rules warn. Pass an empty array to clear all criteria."),
5293
- statusList: z20.array(statusListEntrySchema).max(50).optional().describe("Ordered status vocabulary: {label, category, archiveDefault?}[]. For update: always refused \u2014 the status words come from the platform catalog."),
5527
+ classificationCheck: z22.string().max(500).optional().describe("LLM decision-tree check for this collection (3\u2013500 chars). Guides the classifier in routing entries here."),
5528
+ classificationPriority: z22.number().optional().describe("Classifier priority (1\u20139, lower = higher priority). Used with classificationCheck to order the decision tree."),
5529
+ qualityCriteria: z22.array(qualityCriterionSchema).max(50).optional().describe("Per-collection accept gate rules. 'required' rule hard-blocks accepts on empty fields; 'min_length'/'pattern' rules warn. Pass an empty array to clear all criteria."),
5530
+ statusList: z22.array(statusListEntrySchema).max(50).optional().describe("Ordered status vocabulary: {label, category, archiveDefault?}[]. For update: always refused \u2014 the status words come from the platform catalog."),
5294
5531
  // For label-*
5295
- color: z20.string().max(50).optional().describe("For label-create/label-update: hex color, e.g. '#ef4444'."),
5296
- parentSlug: z20.string().max(200).optional().describe("For label-create: parent group slug for label hierarchy."),
5297
- isGroup: z20.boolean().optional().describe("For label-create/label-update: true if this is a group container, not a taggable label."),
5298
- order: z20.number().optional().describe("For label-create/label-update: sort order within its group."),
5299
- entryId: z20.string().max(200).optional().describe("For label-apply/label-remove: entry ID.")
5532
+ color: z22.string().max(50).optional().describe("For label-create/label-update: hex color, e.g. '#ef4444'."),
5533
+ parentSlug: z22.string().max(200).optional().describe("For label-create: parent group slug for label hierarchy."),
5534
+ isGroup: z22.boolean().optional().describe("For label-create/label-update: true if this is a group container, not a taggable label."),
5535
+ order: z22.number().optional().describe("For label-create/label-update: sort order within its group."),
5536
+ entryId: z22.string().max(200).optional().describe("For label-apply/label-remove: entry ID.")
5300
5537
  });
5301
- var collectionsListVariant = z20.object({ action: z20.literal("list") });
5302
- var collectionsDescribeVariant = z20.object({ action: z20.literal("describe"), slug: z20.string().max(200) });
5303
- var collectionsUpdateVariant = z20.object({
5304
- action: z20.literal("update"),
5305
- slug: z20.string().max(200),
5306
- name: z20.string().max(ENTRY_NAME_MAX_CHARS).optional(),
5307
- description: z20.string().max(2e4).optional(),
5308
- purpose: z20.string().max(2e3).optional(),
5309
- icon: z20.string().max(50).optional(),
5310
- navGroup: z20.enum(["daily", "strategic", "governance", "reference", "collections"]).optional(),
5311
- fields: z20.array(fieldSchema).optional(),
5312
- axis: z20.enum(["work", "none", "pending"]).optional(),
5313
- stageLabelMap: z20.record(z20.string(), z20.enum(["open", "in_progress", "done", "stopped", "tag"])).optional(),
5538
+ var collectionsListVariant = z22.object({ action: z22.literal("list") });
5539
+ var collectionsDescribeVariant = z22.object({ action: z22.literal("describe"), slug: z22.string().max(200) });
5540
+ var collectionsUpdateVariant = z22.object({
5541
+ action: z22.literal("update"),
5542
+ slug: z22.string().max(200),
5543
+ name: z22.string().max(ENTRY_NAME_MAX_CHARS).optional(),
5544
+ description: z22.string().max(2e4).optional(),
5545
+ purpose: z22.string().max(2e3).optional(),
5546
+ icon: z22.string().max(50).optional(),
5547
+ navGroup: z22.enum(["daily", "strategic", "governance", "reference", "collections"]).optional(),
5548
+ fields: z22.array(fieldSchema).optional(),
5549
+ axis: z22.enum(["work", "none", "pending"]).optional(),
5550
+ stageLabelMap: z22.record(z22.string(), z22.enum(["open", "in_progress", "done", "stopped", "tag"])).optional(),
5314
5551
  // WP-640 SWEEP (P1): axis/stageLabelMap repair legacy custom collections — see collections.ts's updateCollection.
5315
- defaultCanonicalKey: z20.string().max(200).optional(),
5316
- defaultWorkflowStatus: z20.string().max(200).optional(),
5317
- validWorkflowStatuses: z20.array(z20.string().max(200)).optional(),
5318
- classificationCheck: z20.string().max(500).optional(),
5319
- classificationPriority: z20.number().optional(),
5320
- qualityCriteria: z20.array(qualityCriterionSchema).optional(),
5321
- statusList: z20.array(statusListEntrySchema).optional()
5552
+ defaultCanonicalKey: z22.string().max(200).optional(),
5553
+ defaultWorkflowStatus: z22.string().max(200).optional(),
5554
+ validWorkflowStatuses: z22.array(z22.string().max(200)).optional(),
5555
+ classificationCheck: z22.string().max(500).optional(),
5556
+ classificationPriority: z22.number().optional(),
5557
+ qualityCriteria: z22.array(qualityCriterionSchema).optional(),
5558
+ statusList: z22.array(statusListEntrySchema).optional()
5322
5559
  });
5323
- var collectionsAuditVariant = z20.object({ action: z20.literal("audit") });
5324
- var collectionsExportVariant = z20.object({ action: z20.literal("export") });
5325
- var collectionsLabelListVariant = z20.object({ action: z20.literal("label-list") });
5326
- var collectionsLabelCreateVariant = z20.object({
5327
- action: z20.literal("label-create"),
5328
- slug: z20.string().max(200),
5329
- name: z20.string().max(ENTRY_NAME_MAX_CHARS),
5330
- color: z20.string().max(50).optional(),
5331
- description: z20.string().max(LABEL_DESCRIPTION_MAX_CHARS).optional(),
5332
- parentSlug: z20.string().max(200).optional(),
5333
- isGroup: z20.boolean().optional(),
5334
- order: z20.number().optional()
5560
+ var collectionsAuditVariant = z22.object({ action: z22.literal("audit") });
5561
+ var collectionsExportVariant = z22.object({ action: z22.literal("export") });
5562
+ var collectionsLabelListVariant = z22.object({ action: z22.literal("label-list") });
5563
+ var collectionsLabelCreateVariant = z22.object({
5564
+ action: z22.literal("label-create"),
5565
+ slug: z22.string().max(200),
5566
+ name: z22.string().max(ENTRY_NAME_MAX_CHARS),
5567
+ color: z22.string().max(50).optional(),
5568
+ description: z22.string().max(LABEL_DESCRIPTION_MAX_CHARS).optional(),
5569
+ parentSlug: z22.string().max(200).optional(),
5570
+ isGroup: z22.boolean().optional(),
5571
+ order: z22.number().optional()
5335
5572
  });
5336
- var collectionsLabelUpdateVariant = z20.object({
5337
- action: z20.literal("label-update"),
5338
- slug: z20.string().max(200),
5339
- name: z20.string().max(ENTRY_NAME_MAX_CHARS).optional(),
5340
- color: z20.string().max(50).optional(),
5341
- description: z20.string().max(LABEL_DESCRIPTION_MAX_CHARS).optional(),
5342
- isGroup: z20.boolean().optional(),
5343
- order: z20.number().optional()
5573
+ var collectionsLabelUpdateVariant = z22.object({
5574
+ action: z22.literal("label-update"),
5575
+ slug: z22.string().max(200),
5576
+ name: z22.string().max(ENTRY_NAME_MAX_CHARS).optional(),
5577
+ color: z22.string().max(50).optional(),
5578
+ description: z22.string().max(LABEL_DESCRIPTION_MAX_CHARS).optional(),
5579
+ isGroup: z22.boolean().optional(),
5580
+ order: z22.number().optional()
5344
5581
  });
5345
- var collectionsLabelDeleteVariant = z20.object({ action: z20.literal("label-delete"), slug: z20.string().max(200) });
5346
- var collectionsLabelApplyVariant = z20.object({ action: z20.literal("label-apply"), slug: z20.string().max(200), entryId: z20.string().max(200) });
5347
- var collectionsLabelRemoveVariant = z20.object({ action: z20.literal("label-remove"), slug: z20.string().max(200), entryId: z20.string().max(200) });
5348
- var collectionsActionUnion = z20.discriminatedUnion("action", [
5582
+ var collectionsLabelDeleteVariant = z22.object({ action: z22.literal("label-delete"), slug: z22.string().max(200) });
5583
+ var collectionsLabelApplyVariant = z22.object({ action: z22.literal("label-apply"), slug: z22.string().max(200), entryId: z22.string().max(200) });
5584
+ var collectionsLabelRemoveVariant = z22.object({ action: z22.literal("label-remove"), slug: z22.string().max(200), entryId: z22.string().max(200) });
5585
+ var collectionsActionUnion = z22.discriminatedUnion("action", [
5349
5586
  collectionsListVariant,
5350
5587
  collectionsDescribeVariant,
5351
5588
  collectionsUpdateVariant,
@@ -5652,7 +5889,7 @@ async function handleExport() {
5652
5889
  }
5653
5890
 
5654
5891
  // src/tools/orient.ts
5655
- import { z as z21 } from "zod/v3";
5892
+ import { z as z23 } from "zod/v3";
5656
5893
 
5657
5894
  // src/tools/planned-work.ts
5658
5895
  var MORE_DRAFTS_LINE = "_More drafts exist than shown. `context action=list status=draft` lists more, but that list is capped as well; page with limit/cursor once context list supports it._";
@@ -6552,25 +6789,25 @@ async function markOrientedWithSnapshotFallback(agentSessionId, coherenceSnapsho
6552
6789
  }
6553
6790
  }
6554
6791
  var ORIENT_ACTIONS = ["task"];
6555
- var orientSchema = z21.object({
6556
- action: z21.enum(ORIENT_ACTIONS).optional().default("task").describe(
6792
+ var orientSchema = z23.object({
6793
+ action: z23.enum(ORIENT_ACTIONS).optional().default("task").describe(
6557
6794
  "'task': task-grounded context loader, the only action."
6558
6795
  ),
6559
- mode: z21.enum(["full", "brief"]).optional().default("full").describe("For 'task': full = full context (default). brief = compact summary for mid-session re-orientation. Prefer using the `tier` param for depth control."),
6560
- tier: z21.enum(["summary", "standard", "full"]).optional().describe(
6796
+ mode: z23.enum(["full", "brief"]).optional().default("full").describe("For 'task': full = full context (default). brief = compact summary for mid-session re-orientation. Prefer using the `tier` param for depth control."),
6797
+ tier: z23.enum(["summary", "standard", "full"]).optional().describe(
6561
6798
  "For 'task': payload depth. Defaults to standard (~256 KB), with or without a task. Pass summary, standard, or full to override."
6562
6799
  ),
6563
- task: z21.string().max(2e3).optional().describe(
6800
+ task: z23.string().max(2e3).optional().describe(
6564
6801
  "For 'task': natural-language task description for task-scoped context. When provided, orient returns scored, relevant entries for the task."
6565
6802
  ),
6566
6803
  // D1d (WP-754/TASK-204): matches the server's StartupResolutionSignals shape; domain resolution (original purpose) is deleted, sole survivor is `context.served` telemetry.
6567
- startupSignals: z21.object({
6568
- changedPaths: z21.array(z21.string()).max(25).optional().describe("Paths changed in the current working tree, if known (e.g. from a prior git status/diff tool call)."),
6569
- reviewedArtifactRefs: z21.array(z21.string()).max(25).optional().describe("Chain entry IDs the caller has already reviewed this session, if tracked."),
6570
- branchName: z21.string().max(120).optional().describe("Current git branch name, if known."),
6571
- worktreeName: z21.string().max(120).optional().describe("Current worktree/directory name, if known.")
6804
+ startupSignals: z23.object({
6805
+ changedPaths: z23.array(z23.string()).max(25).optional().describe("Paths changed in the current working tree, if known (e.g. from a prior git status/diff tool call)."),
6806
+ reviewedArtifactRefs: z23.array(z23.string()).max(25).optional().describe("Chain entry IDs the caller has already reviewed this session, if tracked."),
6807
+ branchName: z23.string().max(120).optional().describe("Current git branch name, if known."),
6808
+ worktreeName: z23.string().max(120).optional().describe("Current worktree/directory name, if known.")
6572
6809
  }).optional().describe("For 'task': best-effort startup signals \u2014 changedPaths/reviewedArtifactRefs/branchName/worktreeName. All optional; omit fields you don't know."),
6573
- invocationPath: z21.enum(["session-start", "session-close", "manual-orient", "handshake", "unknown"]).optional().describe("For 'task': how this orient was invoked. Defaults to 'manual-orient' (the direct `orient task=...` call shape) when omitted.")
6810
+ invocationPath: z23.enum(["session-start", "session-close", "manual-orient", "handshake", "unknown"]).optional().describe("For 'task': how this orient was invoked. Defaults to 'manual-orient' (the direct `orient task=...` call shape) when omitted.")
6574
6811
  });
6575
6812
  function taskGroundingWarningLines(task, hasTaskGrounding = false) {
6576
6813
  if (hasTaskGrounding) return [];
@@ -7222,7 +7459,7 @@ async function _handleOrient({ mode: mode2 = "full", tier, task, startupSignals,
7222
7459
  }
7223
7460
 
7224
7461
  // src/tools/workflows.ts
7225
- import { z as z22 } from "zod/v3";
7462
+ import { z as z24 } from "zod/v3";
7226
7463
 
7227
7464
  // src/lib/resolveCollection.ts
7228
7465
  async function resolveCollection(params) {
@@ -8081,73 +8318,73 @@ function workflowRunOutputToText(output) {
8081
8318
 
8082
8319
  // src/tools/workflows.ts
8083
8320
  var WORKFLOWS_ACTIONS = ["list", "start", "checkpoint", "get-run"];
8084
- var jsonValueSchema = z22.lazy(
8085
- () => z22.union([
8086
- z22.string(),
8087
- z22.number(),
8088
- z22.boolean(),
8089
- z22.null(),
8090
- z22.array(jsonValueSchema),
8091
- z22.record(jsonValueSchema)
8321
+ var jsonValueSchema = z24.lazy(
8322
+ () => z24.union([
8323
+ z24.string(),
8324
+ z24.number(),
8325
+ z24.boolean(),
8326
+ z24.null(),
8327
+ z24.array(jsonValueSchema),
8328
+ z24.record(jsonValueSchema)
8092
8329
  ])
8093
8330
  );
8094
- var workflowRunOutputInputSchema = z22.union([
8095
- z22.object({
8096
- format: z22.literal("freetext"),
8097
- value: z22.string()
8331
+ var workflowRunOutputInputSchema = z24.union([
8332
+ z24.object({
8333
+ format: z24.literal("freetext"),
8334
+ value: z24.string()
8098
8335
  }),
8099
- z22.object({
8100
- format: z22.literal("list"),
8101
- value: z22.array(z22.string())
8336
+ z24.object({
8337
+ format: z24.literal("list"),
8338
+ value: z24.array(z24.string())
8102
8339
  }),
8103
- z22.object({
8104
- format: z22.literal("choice"),
8105
- value: z22.union([z22.string(), z22.array(z22.string())])
8340
+ z24.object({
8341
+ format: z24.literal("choice"),
8342
+ value: z24.union([z24.string(), z24.array(z24.string())])
8106
8343
  }),
8107
- z22.object({
8108
- format: z22.literal("structured"),
8344
+ z24.object({
8345
+ format: z24.literal("structured"),
8109
8346
  value: jsonValueSchema
8110
8347
  })
8111
8348
  ]);
8112
- var workflowsSchema = z22.object({
8113
- action: z22.enum(WORKFLOWS_ACTIONS).describe(
8349
+ var workflowsSchema = z24.object({
8350
+ action: z24.enum(WORKFLOWS_ACTIONS).describe(
8114
8351
  "'list': browse available workflows. 'start': start or resume a workflow \u2014 returns first (or current) round and next-step checkpoint call. 'checkpoint': record round output or final summary. 'get-run': inspect a persisted workflow run."
8115
8352
  ),
8116
- workflowId: z22.string().max(200).optional().describe("Workflow ID for start, checkpoint, or get-run, e.g. 'retro', 'implementation-review'"),
8117
- runId: z22.string().max(200).optional().describe("Workflow run ID: for get-run, which run to load; for checkpoint, target this run (avoids session drift \u2014 pass runId from get-run)."),
8118
- roundId: z22.string().max(200).optional().describe("Round ID for checkpoint, e.g. 'what-went-well'"),
8119
- output: z22.union([z22.string(), workflowRunOutputInputSchema]).optional().describe("The round's output \u2014 either legacy synthesized text or a typed workflow run payload."),
8120
- isFinal: z22.boolean().optional().describe("If true, finalize an existing durable workflow run from its terminal round and create the summary chain entry."),
8121
- restart: z22.boolean().optional().describe("If true, start a new durable run from the workflow's first round in the current session."),
8122
- summaryName: z22.string().max(ENTRY_NAME_MAX_CHARS).optional().describe("Optional name for final chain entry. If omitted, the workflow summary template is used."),
8123
- summaryDescription: z22.string().max(ENTRY_BODY_MAX_CHARS).optional().describe("Optional override for the final chain entry description. Defaults to the final round output text."),
8124
- summaryEntryId: z22.string().max(200).optional().describe("Link an existing entry as the run's summary instead of creating one. Used by facilitated workflows (e.g. shape) where the primary record is created by the specialized tool."),
8353
+ workflowId: z24.string().max(200).optional().describe("Workflow ID for start, checkpoint, or get-run, e.g. 'retro', 'implementation-review'"),
8354
+ runId: z24.string().max(200).optional().describe("Workflow run ID: for get-run, which run to load; for checkpoint, target this run (avoids session drift \u2014 pass runId from get-run)."),
8355
+ roundId: z24.string().max(200).optional().describe("Round ID for checkpoint, e.g. 'what-went-well'"),
8356
+ output: z24.union([z24.string(), workflowRunOutputInputSchema]).optional().describe("The round's output \u2014 either legacy synthesized text or a typed workflow run payload."),
8357
+ isFinal: z24.boolean().optional().describe("If true, finalize an existing durable workflow run from its terminal round and create the summary chain entry."),
8358
+ restart: z24.boolean().optional().describe("If true, start a new durable run from the workflow's first round in the current session."),
8359
+ summaryName: z24.string().max(ENTRY_NAME_MAX_CHARS).optional().describe("Optional name for final chain entry. If omitted, the workflow summary template is used."),
8360
+ summaryDescription: z24.string().max(ENTRY_BODY_MAX_CHARS).optional().describe("Optional override for the final chain entry description. Defaults to the final round output text."),
8361
+ summaryEntryId: z24.string().max(200).optional().describe("Link an existing entry as the run's summary instead of creating one. Used by facilitated workflows (e.g. shape) where the primary record is created by the specialized tool."),
8125
8362
  // WP-513: team+role to create the finalize summary AS OWNER of (rung 2 only).
8126
- ownerTeamEntryId: z22.string().max(200).optional().describe("For 'checkpoint' (isFinal): owning team (rung-2)."),
8127
- ownerRoleEntryId: z22.string().max(200).optional().describe("For 'checkpoint' (isFinal): owning role (rung-2).")
8363
+ ownerTeamEntryId: z24.string().max(200).optional().describe("For 'checkpoint' (isFinal): owning team (rung-2)."),
8364
+ ownerRoleEntryId: z24.string().max(200).optional().describe("For 'checkpoint' (isFinal): owning role (rung-2).")
8128
8365
  });
8129
- var workflowsListVariant = z22.object({ action: z22.literal("list") });
8130
- var workflowsGetRunVariant = z22.object({
8131
- action: z22.literal("get-run"),
8132
- runId: z22.string().max(200).optional(),
8133
- workflowId: z22.string().max(200).optional()
8366
+ var workflowsListVariant = z24.object({ action: z24.literal("list") });
8367
+ var workflowsGetRunVariant = z24.object({
8368
+ action: z24.literal("get-run"),
8369
+ runId: z24.string().max(200).optional(),
8370
+ workflowId: z24.string().max(200).optional()
8134
8371
  });
8135
- var workflowsStartVariant = z22.object({ action: z22.literal("start"), workflowId: z22.string().max(200) });
8136
- var workflowsCheckpointVariant = z22.object({
8137
- action: z22.literal("checkpoint"),
8138
- workflowId: z22.string().max(200),
8139
- roundId: z22.string().max(200),
8140
- output: z22.union([z22.string(), workflowRunOutputInputSchema]),
8141
- isFinal: z22.boolean().optional(),
8142
- restart: z22.boolean().optional(),
8143
- summaryName: z22.string().max(ENTRY_NAME_MAX_CHARS).optional(),
8144
- summaryDescription: z22.string().max(ENTRY_BODY_MAX_CHARS).optional(),
8145
- summaryEntryId: z22.string().max(200).optional(),
8146
- runId: z22.string().max(200).optional(),
8147
- ownerTeamEntryId: z22.string().max(200).optional(),
8148
- ownerRoleEntryId: z22.string().max(200).optional()
8372
+ var workflowsStartVariant = z24.object({ action: z24.literal("start"), workflowId: z24.string().max(200) });
8373
+ var workflowsCheckpointVariant = z24.object({
8374
+ action: z24.literal("checkpoint"),
8375
+ workflowId: z24.string().max(200),
8376
+ roundId: z24.string().max(200),
8377
+ output: z24.union([z24.string(), workflowRunOutputInputSchema]),
8378
+ isFinal: z24.boolean().optional(),
8379
+ restart: z24.boolean().optional(),
8380
+ summaryName: z24.string().max(ENTRY_NAME_MAX_CHARS).optional(),
8381
+ summaryDescription: z24.string().max(ENTRY_BODY_MAX_CHARS).optional(),
8382
+ summaryEntryId: z24.string().max(200).optional(),
8383
+ runId: z24.string().max(200).optional(),
8384
+ ownerTeamEntryId: z24.string().max(200).optional(),
8385
+ ownerRoleEntryId: z24.string().max(200).optional()
8149
8386
  });
8150
- var workflowsActionUnion = z22.discriminatedUnion("action", [
8387
+ var workflowsActionUnion = z24.discriminatedUnion("action", [
8151
8388
  workflowsListVariant,
8152
8389
  workflowsGetRunVariant,
8153
8390
  workflowsStartVariant,
@@ -8876,7 +9113,7 @@ function parseListOutput(output) {
8876
9113
  }
8877
9114
 
8878
9115
  // src/tools/quality.ts
8879
- import { z as z23 } from "zod/v3";
9116
+ import { z as z25 } from "zod/v3";
8880
9117
 
8881
9118
  // src/lib/spineVerdictRead.ts
8882
9119
  function isRecord(value) {
@@ -9041,36 +9278,36 @@ async function handleSpineCheck(story, refresh = false) {
9041
9278
 
9042
9279
  // src/tools/quality.ts
9043
9280
  var QUALITY_ACTIONS = ["check", "re-evaluate", "verify-chain", "spine-check"];
9044
- var qualitySchema = z23.object({
9045
- action: z23.enum(QUALITY_ACTIONS).describe(
9281
+ var qualitySchema = z25.object({
9282
+ action: z25.enum(QUALITY_ACTIONS).describe(
9046
9283
  "'check': read the entry's server quality verdict (tier + criteria). 're-evaluate': trigger fresh evaluation. 'verify-chain': verify entries against the codebase (codeMapping drift, cross-references) \u2014 absorbs `verify`. 'spine-check': structural completeness of the purpose+vision+strategy spine \u2014 story:true also runs the on-demand one-story judgment."
9047
9284
  ),
9048
- entryId: z23.string().max(200).optional().describe("For 'check'/'re-evaluate': entry ID, e.g. 'TEN-graph-db', '<PREFIX>-<n>'."),
9049
- context: z23.enum(["capture", "commit", "review"]).default("review").optional().describe("For re-evaluate: evaluation context"),
9050
- collection: z23.string().max(200).optional().describe("For 'verify-chain': collection slug to verify (default: glossary)."),
9051
- mode: z23.enum(["report", "fix"]).optional().describe("For 'verify-chain': 'report' = read-only trust report (default). 'fix' = also update drifted codeMapping statuses."),
9052
- story: z23.boolean().optional().describe("For 'spine-check': also run the on-demand one-story LLM judgment (never scheduled; result labelled [unvalidated])."),
9053
- refresh: z23.boolean().optional().describe(
9285
+ entryId: z25.string().max(200).optional().describe("For 'check'/'re-evaluate': entry ID, e.g. 'TEN-graph-db', '<PREFIX>-<n>'."),
9286
+ context: z25.enum(["capture", "commit", "review"]).default("review").optional().describe("For re-evaluate: evaluation context"),
9287
+ collection: z25.string().max(200).optional().describe("For 'verify-chain': collection slug to verify (default: glossary)."),
9288
+ mode: z25.enum(["report", "fix"]).optional().describe("For 'verify-chain': 'report' = read-only trust report (default). 'fix' = also update drifted codeMapping statuses."),
9289
+ story: z25.boolean().optional().describe("For 'spine-check': also run the on-demand one-story LLM judgment (never scheduled; result labelled [unvalidated])."),
9290
+ refresh: z25.boolean().optional().describe(
9054
9291
  "For 'spine-check': force a new run even if a fresh verdict already exists. Default false \u2014 a plain call NEVER re-triggers work just to read the last result; it auto-schedules only when no run is already in flight AND the stored verdict is absent/stale/missing the requested story pass."
9055
9292
  )
9056
9293
  });
9057
- var qualityCheckVariant = z23.object({ action: z23.literal("check"), entryId: z23.string().max(200) });
9058
- var qualityReEvaluateVariant = z23.object({
9059
- action: z23.literal("re-evaluate"),
9060
- entryId: z23.string().max(200),
9061
- context: z23.enum(["capture", "commit", "review"]).optional().default("review")
9294
+ var qualityCheckVariant = z25.object({ action: z25.literal("check"), entryId: z25.string().max(200) });
9295
+ var qualityReEvaluateVariant = z25.object({
9296
+ action: z25.literal("re-evaluate"),
9297
+ entryId: z25.string().max(200),
9298
+ context: z25.enum(["capture", "commit", "review"]).optional().default("review")
9062
9299
  });
9063
- var qualityVerifyChainVariant = z23.object({
9064
- action: z23.literal("verify-chain"),
9065
- collection: z23.string().max(200).optional().default("glossary"),
9066
- mode: z23.enum(["report", "fix"]).optional().default("report")
9300
+ var qualityVerifyChainVariant = z25.object({
9301
+ action: z25.literal("verify-chain"),
9302
+ collection: z25.string().max(200).optional().default("glossary"),
9303
+ mode: z25.enum(["report", "fix"]).optional().default("report")
9067
9304
  });
9068
- var qualitySpineCheckVariant = z23.object({
9069
- action: z23.literal("spine-check"),
9070
- story: z23.boolean().optional().default(false),
9071
- refresh: z23.boolean().optional().default(false)
9305
+ var qualitySpineCheckVariant = z25.object({
9306
+ action: z25.literal("spine-check"),
9307
+ story: z25.boolean().optional().default(false),
9308
+ refresh: z25.boolean().optional().default(false)
9072
9309
  });
9073
- var qualityActionUnion = z23.discriminatedUnion("action", [
9310
+ var qualityActionUnion = z25.discriminatedUnion("action", [
9074
9311
  qualityCheckVariant,
9075
9312
  qualityReEvaluateVariant,
9076
9313
  qualityVerifyChainVariant,
@@ -9082,43 +9319,43 @@ var QUALITY_ACTION_SPECS = {
9082
9319
  "verify-chain": { params: ["collection", "mode"], description: "All params optional; collection defaults to 'glossary'." },
9083
9320
  "spine-check": { params: ["story", "refresh"], description: "All params optional; story and refresh default to false \u2014 a plain call never re-triggers work, it just reads back the latest verdict (auto-refreshing only when stale/absent)." }
9084
9321
  };
9085
- var qualityCheckOutputSchema = z23.object({
9086
- entryId: z23.string(),
9322
+ var qualityCheckOutputSchema = z25.object({
9323
+ entryId: z25.string(),
9087
9324
  /** WP-480 S1: false = the ID does not resolve to an entry (typo/deleted) — distinct from "no verdict yet". */
9088
- entryFound: z23.boolean().optional(),
9089
- hasVerdict: z23.boolean(),
9325
+ entryFound: z25.boolean().optional(),
9326
+ hasVerdict: z25.boolean(),
9090
9327
  /** WP-480 S1: the verdict was judged against content the entry no longer has — re-evaluate for a current one. */
9091
- stale: z23.boolean().optional(),
9092
- tier: z23.string().optional(),
9093
- passed: z23.boolean().optional(),
9094
- criteria: z23.array(z23.object({
9095
- id: z23.string(),
9096
- passed: z23.boolean(),
9097
- hint: z23.string().optional()
9328
+ stale: z25.boolean().optional(),
9329
+ tier: z25.string().optional(),
9330
+ passed: z25.boolean().optional(),
9331
+ criteria: z25.array(z25.object({
9332
+ id: z25.string(),
9333
+ passed: z25.boolean(),
9334
+ hint: z25.string().optional()
9098
9335
  }))
9099
9336
  });
9100
- var qualityReevaluateOutputSchema = z23.object({
9101
- entryId: z23.string(),
9102
- context: z23.string(),
9103
- score: z23.number(),
9104
- maxScore: z23.number(),
9105
- improved: z23.boolean()
9337
+ var qualityReevaluateOutputSchema = z25.object({
9338
+ entryId: z25.string(),
9339
+ context: z25.string(),
9340
+ score: z25.number(),
9341
+ maxScore: z25.number(),
9342
+ improved: z25.boolean()
9106
9343
  });
9107
- var qualitySpineCheckOutputSchema = z23.object({
9108
- hasVerdict: z23.boolean(),
9109
- noSpineEntries: z23.boolean().optional(),
9110
- structuralPassed: z23.boolean().optional(),
9111
- storyPassed: z23.boolean().nullable().optional(),
9112
- stale: z23.boolean().optional(),
9344
+ var qualitySpineCheckOutputSchema = z25.object({
9345
+ hasVerdict: z25.boolean(),
9346
+ noSpineEntries: z25.boolean().optional(),
9347
+ structuralPassed: z25.boolean().optional(),
9348
+ storyPassed: z25.boolean().nullable().optional(),
9349
+ stale: z25.boolean().optional(),
9113
9350
  // TASK-156: a capped membership scan must stay distinguishable from a complete one, and a
9114
9351
  // malformed verdict needs its own discriminator — both were silently stripped before this.
9115
- staleMemberIds: z23.array(z23.string()).optional(),
9116
- membershipTruncated: z23.boolean().optional(),
9352
+ staleMemberIds: z25.array(z25.string()).optional(),
9353
+ membershipTruncated: z25.boolean().optional(),
9117
9354
  // PRRT_kwDORS4ils6i_PVS: a KNOWN relations-fingerprint mismatch, distinct from "unknown" —
9118
9355
  // without this, a detected topology change and an unverifiable legacy fingerprint were
9119
9356
  // indistinguishable in structured output.
9120
- relationsChanged: z23.boolean().optional(),
9121
- malformed: z23.boolean().optional()
9357
+ relationsChanged: z25.boolean().optional(),
9358
+ malformed: z25.boolean().optional()
9122
9359
  });
9123
9360
  function registerQualityTools(server) {
9124
9361
  const qualityHandlers = {
@@ -9245,10 +9482,10 @@ async function handleReEvaluate(entryId, context) {
9245
9482
  }
9246
9483
 
9247
9484
  // src/tools/session.ts
9248
- import { z as z26 } from "zod/v3";
9485
+ import { z as z28 } from "zod/v3";
9249
9486
 
9250
9487
  // src/tools/wrapup.ts
9251
- import { z as z24 } from "zod/v3";
9488
+ import { z as z26 } from "zod/v3";
9252
9489
 
9253
9490
  // src/lib/compose-wrapup-view.ts
9254
9491
  function toComposed(e) {
@@ -9530,8 +9767,8 @@ async function runWrapupCommitAll(data) {
9530
9767
  overflowUnscanned
9531
9768
  };
9532
9769
  }
9533
- var wrapupSchema = z24.object({
9534
- action: z24.enum(["review", "commit-all"]).optional().describe(
9770
+ var wrapupSchema = z26.object({
9771
+ action: z26.enum(["review", "commit-all"]).optional().describe(
9535
9772
  "Action to perform. 'review' (default) shows the wrapup summary. 'commit-all' accepts all uncommitted drafts; it writes no relations."
9536
9773
  )
9537
9774
  });
@@ -9624,26 +9861,26 @@ ${text}` : text;
9624
9861
  }
9625
9862
 
9626
9863
  // src/tools/facilitate.ts
9627
- import { z as z25 } from "zod/v3";
9864
+ import { z as z27 } from "zod/v3";
9628
9865
  var FACILITATE_ACTIONS = ["resume", "commit-constellation"];
9629
- var coherencyAcknowledgementSchema = z25.object({
9630
- response: z25.string().max(200).describe("Acknowledgement response per offender: 'linked' | 'accepted-fix' | 'diverged'."),
9631
- entryId: z25.string().max(200).optional().describe("Entry the acknowledgement links to (e.g. the strategic spine entry)."),
9632
- reason: z25.string().max(2e3).optional().describe("Free-text justification, required for a 'diverged' response."),
9633
- subjectEntryId: z25.string().max(200).optional().describe("The refused entry this acknowledgement answers.")
9866
+ var coherencyAcknowledgementSchema = z27.object({
9867
+ response: z27.string().max(200).describe("Acknowledgement response per offender: 'linked' | 'accepted-fix' | 'diverged'."),
9868
+ entryId: z27.string().max(200).optional().describe("Entry the acknowledgement links to (e.g. the strategic spine entry)."),
9869
+ reason: z27.string().max(2e3).optional().describe("Free-text justification, required for a 'diverged' response."),
9870
+ subjectEntryId: z27.string().max(200).optional().describe("The refused entry this acknowledgement answers.")
9634
9871
  });
9635
- var facilitateSchema = z25.object({
9636
- action: z25.enum(FACILITATE_ACTIONS).describe(
9872
+ var facilitateSchema = z27.object({
9873
+ action: z27.enum(FACILITATE_ACTIONS).describe(
9637
9874
  "'resume': load session state from an existing bet entry. 'commit-constellation': atomically accept a bet and all its linked draft entries in one call. Requires betEntryId."
9638
9875
  ),
9639
- betEntryId: z25.string().max(200).optional().describe("Bet entry ID. Required for both actions."),
9640
- operationId: z25.string().max(200).optional().describe("Optional idempotency key for commit-constellation retries."),
9876
+ betEntryId: z27.string().max(200).optional().describe("Bet entry ID. Required for both actions."),
9877
+ operationId: z27.string().max(200).optional().describe("Optional idempotency key for commit-constellation retries."),
9641
9878
  // WP-465 slice ⑤: coherency retry controls for a COHERENCY_REFUSED constellation hold.
9642
9879
  // Forwarded verbatim to agentKnowledge.facilitateEnvelope (validation lives at the gate).
9643
- coherencyAcknowledgements: z25.array(coherencyAcknowledgementSchema).max(20).optional().describe(
9880
+ coherencyAcknowledgements: z27.array(coherencyAcknowledgementSchema).max(20).optional().describe(
9644
9881
  "Per-offender acknowledgements to retry a constellation held under standard/strict coherency mode. Each: {subjectEntryId, response: 'linked' | 'accepted-fix' | 'diverged', entryId?, reason?}."
9645
9882
  ),
9646
- steeringOverrideReason: z25.string().max(2e3).optional().describe(
9883
+ steeringOverrideReason: z27.string().max(2e3).optional().describe(
9647
9884
  "Typed override reason (>= 12 chars) to push a constellation past a coherency hold instead of acknowledging."
9648
9885
  )
9649
9886
  });
@@ -9988,49 +10225,49 @@ var SESSION_ACTIONS = [
9988
10225
  "resume",
9989
10226
  "commit-constellation"
9990
10227
  ];
9991
- var coherencyAcknowledgementFlatSchema2 = z26.object({
9992
- response: z26.string().max(200).describe("Acknowledgement response per offender: 'linked' | 'accepted-fix' | 'diverged'."),
9993
- entryId: z26.string().max(200).optional().describe("Entry the acknowledgement links to."),
9994
- reason: z26.string().max(2e3).optional().describe("Free-text justification, required for a 'diverged' response."),
9995
- subjectEntryId: z26.string().max(200).optional().describe("The refused entry this acknowledgement answers.")
10228
+ var coherencyAcknowledgementFlatSchema2 = z28.object({
10229
+ response: z28.string().max(200).describe("Acknowledgement response per offender: 'linked' | 'accepted-fix' | 'diverged'."),
10230
+ entryId: z28.string().max(200).optional().describe("Entry the acknowledgement links to."),
10231
+ reason: z28.string().max(2e3).optional().describe("Free-text justification, required for a 'diverged' response."),
10232
+ subjectEntryId: z28.string().max(200).optional().describe("The refused entry this acknowledgement answers.")
9996
10233
  });
9997
10234
  var TEAM_PARAM_DESCRIPTION = "For 'start': the team the role is held in, by its 'TEAM-<n>' id \u2014 only needed with role, when you hold that role in more than one team; a refusal that names the way in prints it.";
9998
- var sessionSchema = z26.object({
9999
- action: z26.enum(SESSION_ACTIONS).describe(
10235
+ var sessionSchema = z28.object({
10236
+ action: z28.enum(SESSION_ACTIONS).describe(
10000
10237
  "'start': begin a tracked session. 'close': end the session and record activity. 'status': check current session state. 'wrapup-review': review uncommitted drafts before closing (absorbs session-wrapup action=review). 'wrapup-commit': accept all uncommitted drafts (absorbs session-wrapup action=commit-all). 'resume': load session state from an existing bet entry (absorbs facilitate action=resume). 'commit-constellation': atomically accept a bet and its linked drafts (absorbs facilitate action=commit-constellation)."
10001
10238
  ),
10002
- betEntryId: z26.string().max(200).optional().describe("For 'resume'/'commit-constellation': bet entry ID. Required for both."),
10003
- operationId: z26.string().max(200).optional().describe("For 'commit-constellation': optional idempotency key for retries."),
10004
- coherencyAcknowledgements: z26.array(coherencyAcknowledgementFlatSchema2).max(20).optional().describe(
10239
+ betEntryId: z28.string().max(200).optional().describe("For 'resume'/'commit-constellation': bet entry ID. Required for both."),
10240
+ operationId: z28.string().max(200).optional().describe("For 'commit-constellation': optional idempotency key for retries."),
10241
+ coherencyAcknowledgements: z28.array(coherencyAcknowledgementFlatSchema2).max(20).optional().describe(
10005
10242
  "For 'commit-constellation': per-offender acknowledgements to retry a constellation held under standard/strict coherency mode."
10006
10243
  ),
10007
- steeringOverrideReason: z26.string().max(2e3).optional().describe(
10244
+ steeringOverrideReason: z28.string().max(2e3).optional().describe(
10008
10245
  "For 'commit-constellation': typed override reason (>= 12 chars) to push past a coherency hold instead of acknowledging."
10009
10246
  ),
10010
10247
  // DEC-1715 (WP-753 O1a)
10011
- role: z26.string().max(200).optional().describe(
10248
+ role: z28.string().max(200).optional().describe(
10012
10249
  "For 'start': role to act as this session, by the '<PREFIX>-<n>' id a start reply prints beside the role name \u2014 only needed if you hold more than one; the error lists your roles if omitted."
10013
10250
  ),
10014
- team: z26.string().max(200).optional().describe(TEAM_PARAM_DESCRIPTION)
10251
+ team: z28.string().max(200).optional().describe(TEAM_PARAM_DESCRIPTION)
10015
10252
  // TASK-801
10016
10253
  });
10017
- var sessionStartVariant = z26.object({
10018
- action: z26.literal("start"),
10254
+ var sessionStartVariant = z28.object({
10255
+ action: z28.literal("start"),
10019
10256
  // DEC-1715 (WP-753 O1a): the role to act as this session — only needed when the person holds
10020
10257
  // more than one role; the server refuses and lists the choices when it's required and omitted.
10021
- role: z26.string().max(200).optional().describe(
10258
+ role: z28.string().max(200).optional().describe(
10022
10259
  "Role to act as this session, by the '<PREFIX>-<n>' id a start reply prints beside the role name \u2014 only needed if you hold more than one; the error lists your roles if omitted."
10023
10260
  ),
10024
- team: z26.string().max(200).optional().describe(TEAM_PARAM_DESCRIPTION)
10261
+ team: z28.string().max(200).optional().describe(TEAM_PARAM_DESCRIPTION)
10025
10262
  // TASK-801
10026
10263
  });
10027
- var sessionCloseVariant = z26.object({ action: z26.literal("close") });
10028
- var sessionStatusVariant = z26.object({ action: z26.literal("status") });
10029
- var sessionWrapupReviewVariant = z26.object({ action: z26.literal("wrapup-review") });
10030
- var sessionWrapupCommitVariant = z26.object({ action: z26.literal("wrapup-commit") });
10031
- var sessionResumeVariant = facilitateSchema.omit({ action: true }).extend({ action: z26.literal("resume") });
10032
- var sessionCommitConstellationVariant = facilitateSchema.omit({ action: true }).extend({ action: z26.literal("commit-constellation") });
10033
- var sessionActionUnion = z26.discriminatedUnion("action", [
10264
+ var sessionCloseVariant = z28.object({ action: z28.literal("close") });
10265
+ var sessionStatusVariant = z28.object({ action: z28.literal("status") });
10266
+ var sessionWrapupReviewVariant = z28.object({ action: z28.literal("wrapup-review") });
10267
+ var sessionWrapupCommitVariant = z28.object({ action: z28.literal("wrapup-commit") });
10268
+ var sessionResumeVariant = facilitateSchema.omit({ action: true }).extend({ action: z28.literal("resume") });
10269
+ var sessionCommitConstellationVariant = facilitateSchema.omit({ action: true }).extend({ action: z28.literal("commit-constellation") });
10270
+ var sessionActionUnion = z28.discriminatedUnion("action", [
10034
10271
  sessionStartVariant,
10035
10272
  sessionCloseVariant,
10036
10273
  sessionStatusVariant,
@@ -10270,10 +10507,10 @@ async function handleStatus() {
10270
10507
  }
10271
10508
 
10272
10509
  // src/tools/workspace.ts
10273
- import { z as z29 } from "zod/v3";
10510
+ import { z as z31 } from "zod/v3";
10274
10511
 
10275
10512
  // src/tools/health.ts
10276
- import { z as z27 } from "zod/v3";
10513
+ import { z as z29 } from "zod/v3";
10277
10514
 
10278
10515
  // src/lib/auditView.ts
10279
10516
  async function buildTenantAuditView(limit) {
@@ -10619,60 +10856,60 @@ ${logLines.join("\n")}${seamSummaryText}` }],
10619
10856
  };
10620
10857
  }
10621
10858
  var HEALTH_ACTIONS = ["check", "whoami", "status", "audit", "self-test"];
10622
- var healthSchema = z27.object({
10623
- action: z27.enum(HEALTH_ACTIONS).describe(
10859
+ var healthSchema = z29.object({
10860
+ action: z29.enum(HEALTH_ACTIONS).describe(
10624
10861
  "'check': connectivity and workspace stats. 'whoami': session identity. 'status': workspace readiness. 'audit': session audit log. 'self-test': validate all tool schemas."
10625
10862
  ),
10626
- limit: z27.number().min(1).max(50).default(20).optional().describe("For audit: how many recent calls to show (max 50)")
10863
+ limit: z29.number().min(1).max(50).default(20).optional().describe("For audit: how many recent calls to show (max 50)")
10627
10864
  });
10628
- var healthCheckOutputSchema = z27.object({
10629
- healthy: z27.boolean(),
10630
- collections: z27.number(),
10631
- entries: z27.number(),
10632
- latencyMs: z27.number(),
10633
- workspace: z27.string()
10865
+ var healthCheckOutputSchema = z29.object({
10866
+ healthy: z29.boolean(),
10867
+ collections: z29.number(),
10868
+ entries: z29.number(),
10869
+ latencyMs: z29.number(),
10870
+ workspace: z29.string()
10634
10871
  });
10635
- var organisationHealthSchema = z27.object({
10636
- reviewed: z27.number(),
10637
- agreements: z27.number(),
10638
- disagreements: z27.number(),
10639
- abstentions: z27.number(),
10640
- agreementRate: z27.number(),
10641
- flags: z27.array(z27.object({ collection: z27.string(), count: z27.number(), suggestedCollection: z27.string() }))
10872
+ var organisationHealthSchema = z29.object({
10873
+ reviewed: z29.number(),
10874
+ agreements: z29.number(),
10875
+ disagreements: z29.number(),
10876
+ abstentions: z29.number(),
10877
+ agreementRate: z29.number(),
10878
+ flags: z29.array(z29.object({ collection: z29.string(), count: z29.number(), suggestedCollection: z29.string() }))
10642
10879
  });
10643
- var healthStatusOutputSchema = z27.object({
10644
- sampleTruncated: z27.boolean().nullable().optional(),
10645
- activeEntries: z27.number(),
10646
- totalRelations: z27.number(),
10647
- orphanedEntries: z27.number(),
10648
- checks: z27.array(z27.object({ label: z27.string(), passed: z27.boolean(), current: z27.number(), required: z27.number() })),
10649
- gaps: z27.array(z27.object({ id: z27.string(), label: z27.string(), guidance: z27.string() })),
10880
+ var healthStatusOutputSchema = z29.object({
10881
+ sampleTruncated: z29.boolean().nullable().optional(),
10882
+ activeEntries: z29.number(),
10883
+ totalRelations: z29.number(),
10884
+ orphanedEntries: z29.number(),
10885
+ checks: z29.array(z29.object({ label: z29.string(), passed: z29.boolean(), current: z29.number(), required: z29.number() })),
10886
+ gaps: z29.array(z29.object({ id: z29.string(), label: z29.string(), guidance: z29.string() })),
10650
10887
  organisationHealth: organisationHealthSchema.optional()
10651
10888
  });
10652
- var healthAuditOutputSchema = z27.object({
10653
- totalCalls: z27.number(),
10654
- calls: z27.array(z27.object({
10655
- tool: z27.string(),
10656
- action: z27.string().optional(),
10657
- timestamp: z27.string(),
10658
- durationMs: z27.number().optional()
10889
+ var healthAuditOutputSchema = z29.object({
10890
+ totalCalls: z29.number(),
10891
+ calls: z29.array(z29.object({
10892
+ tool: z29.string(),
10893
+ action: z29.string().optional(),
10894
+ timestamp: z29.string(),
10895
+ durationMs: z29.number().optional()
10659
10896
  }))
10660
10897
  });
10661
- var healthWhoamiOutputSchema = z27.object({
10662
- workspaceId: z27.string(),
10663
- workspaceName: z27.string(),
10664
- scope: z27.string(),
10665
- sessionId: z27.union([z27.string(), z27.null()]),
10666
- oriented: z27.boolean()
10898
+ var healthWhoamiOutputSchema = z29.object({
10899
+ workspaceId: z29.string(),
10900
+ workspaceName: z29.string(),
10901
+ scope: z29.string(),
10902
+ sessionId: z29.union([z29.string(), z29.null()]),
10903
+ oriented: z29.boolean()
10667
10904
  });
10668
- var selfTestOutputSchema = z27.object({
10669
- passed: z27.number(),
10670
- failed: z27.number(),
10671
- total: z27.number(),
10672
- results: z27.array(z27.object({
10673
- tool: z27.string(),
10674
- valid: z27.boolean(),
10675
- error: z27.string().optional()
10905
+ var selfTestOutputSchema = z29.object({
10906
+ passed: z29.number(),
10907
+ failed: z29.number(),
10908
+ total: z29.number(),
10909
+ results: z29.array(z29.object({
10910
+ tool: z29.string(),
10911
+ valid: z29.boolean(),
10912
+ error: z29.string().optional()
10676
10913
  }))
10677
10914
  });
10678
10915
  function handleSelfTest(server) {
@@ -10723,9 +10960,9 @@ function handleSelfTest(server) {
10723
10960
  }
10724
10961
 
10725
10962
  // src/tools/usage.ts
10726
- import { z as z28 } from "zod/v3";
10727
- var usageSummarySchema = z28.object({
10728
- periodDays: z28.number().min(1).max(90).optional().describe("Number of days to look back (default 30, max 90)")
10963
+ import { z as z30 } from "zod/v3";
10964
+ var usageSummarySchema = z30.object({
10965
+ periodDays: z30.number().min(1).max(90).optional().describe("Number of days to look back (default 30, max 90)")
10729
10966
  });
10730
10967
  async function handleUsageSummary(periodDays) {
10731
10968
  const ws = await getWorkspaceContext();
@@ -10795,20 +11032,20 @@ var WORKSPACE_ACTIONS = [
10795
11032
  "self-test",
10796
11033
  "usage"
10797
11034
  ];
10798
- var workspaceSchema = z29.object({
10799
- action: z29.enum(WORKSPACE_ACTIONS).describe(
11035
+ var workspaceSchema = z31.object({
11036
+ action: z31.enum(WORKSPACE_ACTIONS).describe(
10800
11037
  "'check': connectivity and workspace stats (absorbs health action=check). 'whoami': session identity (absorbs health action=whoami). 'status': workspace readiness (absorbs health action=status). 'audit': session audit log (absorbs health action=audit). 'self-test': validate all tool schemas (absorbs health action=self-test). 'usage': LLM usage and cost summary (absorbs get-usage-summary)."
10801
11038
  ),
10802
- limit: z29.number().min(1).max(50).optional().describe("For 'audit': how many recent calls to show (max 50, default 20)."),
10803
- periodDays: z29.number().min(1).max(90).optional().describe("For 'usage': number of days to look back (default 30, max 90).")
11039
+ limit: z31.number().min(1).max(50).optional().describe("For 'audit': how many recent calls to show (max 50, default 20)."),
11040
+ periodDays: z31.number().min(1).max(90).optional().describe("For 'usage': number of days to look back (default 30, max 90).")
10804
11041
  });
10805
- var workspaceCheckVariant = z29.object({ action: z29.literal("check") });
10806
- var workspaceWhoamiVariant = z29.object({ action: z29.literal("whoami") });
10807
- var workspaceStatusVariant = z29.object({ action: z29.literal("status") });
10808
- var workspaceAuditVariant = z29.object({ action: z29.literal("audit"), limit: z29.number().min(1).max(50).optional().default(20) });
10809
- var workspaceSelfTestVariant = z29.object({ action: z29.literal("self-test") });
10810
- var workspaceUsageVariant = z29.object({ action: z29.literal("usage"), periodDays: z29.number().min(1).max(90).optional() });
10811
- var workspaceActionUnion = z29.discriminatedUnion("action", [
11042
+ var workspaceCheckVariant = z31.object({ action: z31.literal("check") });
11043
+ var workspaceWhoamiVariant = z31.object({ action: z31.literal("whoami") });
11044
+ var workspaceStatusVariant = z31.object({ action: z31.literal("status") });
11045
+ var workspaceAuditVariant = z31.object({ action: z31.literal("audit"), limit: z31.number().min(1).max(50).optional().default(20) });
11046
+ var workspaceSelfTestVariant = z31.object({ action: z31.literal("self-test") });
11047
+ var workspaceUsageVariant = z31.object({ action: z31.literal("usage"), periodDays: z31.number().min(1).max(90).optional() });
11048
+ var workspaceActionUnion = z31.discriminatedUnion("action", [
10812
11049
  workspaceCheckVariant,
10813
11050
  workspaceWhoamiVariant,
10814
11051
  workspaceStatusVariant,
@@ -10854,7 +11091,7 @@ function registerWorkspaceTools(server) {
10854
11091
  }
10855
11092
 
10856
11093
  // src/tools/feedback.ts
10857
- import { z as z30 } from "zod/v3";
11094
+ import { z as z32 } from "zod/v3";
10858
11095
 
10859
11096
  // src/lib/productFeedbackConstants.ts
10860
11097
  var PRODUCT_FEEDBACK_CATEGORIES = ["bug", "friction", "idea", "praise", "other"];
@@ -10867,41 +11104,41 @@ var MAX_FEEDBACK_ANSWER_LENGTH = 240;
10867
11104
 
10868
11105
  // src/tools/feedback.ts
10869
11106
  var actions = ["submit", "list", "queue", "note", "group", "status"];
10870
- var category = z30.enum(PRODUCT_FEEDBACK_CATEGORIES);
10871
- var status = z30.enum(PRODUCT_FEEDBACK_STATUSES);
10872
- var vendorStatus = z30.enum(VENDOR_SETTABLE_STATUSES);
10873
- var tensionId = z30.string().regex(FEEDBACK_TENSION_ID_PATTERN);
10874
- var answer = z30.string().trim().min(1).max(MAX_FEEDBACK_ANSWER_LENGTH);
11107
+ var category = z32.enum(PRODUCT_FEEDBACK_CATEGORIES);
11108
+ var status = z32.enum(PRODUCT_FEEDBACK_STATUSES);
11109
+ var vendorStatus = z32.enum(VENDOR_SETTABLE_STATUSES);
11110
+ var tensionId = z32.string().regex(FEEDBACK_TENSION_ID_PATTERN);
11111
+ var answer = z32.string().trim().min(1).max(MAX_FEEDBACK_ANSWER_LENGTH);
10875
11112
  var MESSAGE_DISPLAY_LIMIT = 1e3;
10876
11113
  var FULL_MESSAGE_MAX_LIMIT = 5;
10877
- var feedbackSchema = z30.object({
10878
- action: z30.enum(actions).describe("submit requires message; list returns your own workspace's feedback (all statuses); queue is the vendor triage queue (system admins only) and accepts filters; note requires feedbackId+note; group requires feedbackIds+groupId (null clears); status requires feedbackId+status and may carry tensionId and answer."),
10879
- message: z30.string().max(1e4).optional().describe("Required for submit: the product feedback text (max 10,000 characters)."),
11114
+ var feedbackSchema = z32.object({
11115
+ action: z32.enum(actions).describe("submit requires message; list returns your own workspace's feedback (all statuses); queue is the vendor triage queue (system admins only) and accepts filters; note requires feedbackId+note; group requires feedbackIds+groupId (null clears); status requires feedbackId+status and may carry tensionId and answer."),
11116
+ message: z32.string().max(1e4).optional().describe("Required for submit: the product feedback text (max 10,000 characters)."),
10880
11117
  category: category.optional().describe("Submit/queue category; submit defaults to other."),
10881
- command: z30.string().max(1e3).optional().describe("Optional submit command context; argument values are scrubbed server-side."),
10882
- route: z30.string().max(500).optional().describe("Optional submit route context."),
10883
- client: z30.string().max(100).optional().describe("Optional submit client label."),
10884
- feedbackId: z30.string().max(200).optional().describe("Required for note/status: target feedback ID."),
10885
- feedbackIds: z30.array(z30.string().max(200)).max(100).optional().describe("Required for group: 1\u2013100 target feedback IDs."),
10886
- note: z30.string().max(4e3).optional().describe("Required for note: replacement triage note (max 4,000 characters)."),
11118
+ command: z32.string().max(1e3).optional().describe("Optional submit command context; argument values are scrubbed server-side."),
11119
+ route: z32.string().max(500).optional().describe("Optional submit route context."),
11120
+ client: z32.string().max(100).optional().describe("Optional submit client label."),
11121
+ feedbackId: z32.string().max(200).optional().describe("Required for note/status: target feedback ID."),
11122
+ feedbackIds: z32.array(z32.string().max(200)).max(100).optional().describe("Required for group: 1\u2013100 target feedback IDs."),
11123
+ note: z32.string().max(4e3).optional().describe("Required for note: replacement triage note (max 4,000 characters)."),
10887
11124
  status: status.optional().describe("list/queue filter (any status, including 'screening'); the status action's required target value excludes 'screening' (system-only)."),
10888
11125
  tensionId: tensionId.optional().describe("status only: the tension the report joined, its display id TEN- and a number (vendor-only, never shown to the reporter)."),
10889
11126
  answer: answer.optional().describe(`status only: the one sentence the reporter reads under their report, dated (max ${MAX_FEEDBACK_ANSWER_LENGTH} characters).`),
10890
- groupId: z30.string().min(1).max(200).nullable().optional().describe("Group filter, or required group destination; null explicitly clears grouping."),
10891
- workspaceId: z30.string().max(200).optional().describe("Queue-only filter (vendor cross-workspace triage); omitted means all workspaces. Has no effect on list \u2014 your own workspace is always injected server-side."),
10892
- since: z30.number().optional().describe("Queue-only lower createdAt window bound (inclusive) in epoch milliseconds."),
10893
- before: z30.number().optional().describe("Queue-only upper createdAt window bound (exclusive) in epoch milliseconds. A time-window FILTER, not the pager \u2014 use cursor to page."),
10894
- limit: z30.number().int().min(1).max(100).optional().describe("list/queue row limit, default 50, maximum 100."),
10895
- cursor: z30.string().max(2e3).optional().describe("Opaque pagination cursor from a previous list/queue response (continueCursor); omit for the first page."),
10896
- full: z30.boolean().optional().describe(`list/queue only: return untruncated messages; allowed only when limit <= ${FULL_MESSAGE_MAX_LIMIT}.`)
11127
+ groupId: z32.string().min(1).max(200).nullable().optional().describe("Group filter, or required group destination; null explicitly clears grouping."),
11128
+ workspaceId: z32.string().max(200).optional().describe("Queue-only filter (vendor cross-workspace triage); omitted means all workspaces. Has no effect on list \u2014 your own workspace is always injected server-side."),
11129
+ since: z32.number().optional().describe("Queue-only lower createdAt window bound (inclusive) in epoch milliseconds."),
11130
+ before: z32.number().optional().describe("Queue-only upper createdAt window bound (exclusive) in epoch milliseconds. A time-window FILTER, not the pager \u2014 use cursor to page."),
11131
+ limit: z32.number().int().min(1).max(100).optional().describe("list/queue row limit, default 50, maximum 100."),
11132
+ cursor: z32.string().max(2e3).optional().describe("Opaque pagination cursor from a previous list/queue response (continueCursor); omit for the first page."),
11133
+ full: z32.boolean().optional().describe(`list/queue only: return untruncated messages; allowed only when limit <= ${FULL_MESSAGE_MAX_LIMIT}.`)
10897
11134
  });
10898
- var union = z30.discriminatedUnion("action", [
10899
- z30.object({ action: z30.literal("submit"), message: z30.string().min(1).max(1e4), category: category.optional(), command: z30.string().max(1e3).optional(), route: z30.string().max(500).optional(), client: z30.string().max(100).optional() }),
10900
- z30.object({ action: z30.literal("list"), status: status.optional(), limit: z30.number().int().min(1).max(100).optional(), cursor: z30.string().max(2e3).optional(), full: z30.boolean().optional() }),
10901
- z30.object({ action: z30.literal("queue"), status: status.optional(), category: category.optional(), groupId: z30.string().min(1).max(200).optional(), workspaceId: z30.string().max(200).optional(), since: z30.number().optional(), before: z30.number().optional(), limit: z30.number().int().min(1).max(100).optional(), cursor: z30.string().max(2e3).optional(), full: z30.boolean().optional() }),
10902
- z30.object({ action: z30.literal("note"), feedbackId: z30.string().min(1).max(200), note: z30.string().max(4e3) }),
10903
- z30.object({ action: z30.literal("group"), feedbackIds: z30.array(z30.string().min(1).max(200)).min(1).max(100), groupId: z30.string().min(1).max(200).nullable() }),
10904
- z30.object({ action: z30.literal("status"), feedbackId: z30.string().min(1).max(200), status: vendorStatus, tensionId: tensionId.optional(), answer: answer.optional() })
11135
+ var union = z32.discriminatedUnion("action", [
11136
+ z32.object({ action: z32.literal("submit"), message: z32.string().min(1).max(1e4), category: category.optional(), command: z32.string().max(1e3).optional(), route: z32.string().max(500).optional(), client: z32.string().max(100).optional() }),
11137
+ z32.object({ action: z32.literal("list"), status: status.optional(), limit: z32.number().int().min(1).max(100).optional(), cursor: z32.string().max(2e3).optional(), full: z32.boolean().optional() }),
11138
+ z32.object({ action: z32.literal("queue"), status: status.optional(), category: category.optional(), groupId: z32.string().min(1).max(200).optional(), workspaceId: z32.string().max(200).optional(), since: z32.number().optional(), before: z32.number().optional(), limit: z32.number().int().min(1).max(100).optional(), cursor: z32.string().max(2e3).optional(), full: z32.boolean().optional() }),
11139
+ z32.object({ action: z32.literal("note"), feedbackId: z32.string().min(1).max(200), note: z32.string().max(4e3) }),
11140
+ z32.object({ action: z32.literal("group"), feedbackIds: z32.array(z32.string().min(1).max(200)).min(1).max(100), groupId: z32.string().min(1).max(200).nullable() }),
11141
+ z32.object({ action: z32.literal("status"), feedbackId: z32.string().min(1).max(200), status: vendorStatus, tensionId: tensionId.optional(), answer: answer.optional() })
10905
11142
  ]);
10906
11143
  var specs = {
10907
11144
  submit: { params: ["message", "category", "command", "route", "client"], description: "message is required; category defaults to other." },
@@ -11030,19 +11267,19 @@ function registerFeedbackTool(server) {
11030
11267
  }
11031
11268
 
11032
11269
  // src/tools/conflicts.ts
11033
- import { z as z31 } from "zod/v3";
11270
+ import { z as z33 } from "zod/v3";
11034
11271
  var ACTIONS = ["list", "resolve"];
11035
- var mode = z31.enum(["actionable", "all"]);
11036
- var verdict = z31.enum(["real", "noise"]);
11037
- var conflictsSchema = z31.object({
11038
- action: z31.enum(ACTIONS),
11272
+ var mode = z33.enum(["actionable", "all"]);
11273
+ var verdict = z33.enum(["real", "noise"]);
11274
+ var conflictsSchema = z33.object({
11275
+ action: z33.enum(ACTIONS),
11039
11276
  mode: mode.optional().describe("list: all adds judged and snoozed rows."),
11040
- issueId: z31.string().max(200).optional().describe("resolve: an id from list."),
11277
+ issueId: z33.string().max(200).optional().describe("resolve: an id from list."),
11041
11278
  verdict: verdict.optional().describe("resolve: real keeps the row open; noise closes it.")
11042
11279
  });
11043
- var conflictsActionUnion = z31.discriminatedUnion("action", [
11044
- z31.object({ action: z31.literal("list"), mode: mode.optional() }),
11045
- z31.object({ action: z31.literal("resolve"), issueId: z31.string().min(1).max(200), verdict })
11280
+ var conflictsActionUnion = z33.discriminatedUnion("action", [
11281
+ z33.object({ action: z33.literal("list"), mode: mode.optional() }),
11282
+ z33.object({ action: z33.literal("resolve"), issueId: z33.string().min(1).max(200), verdict })
11046
11283
  ]);
11047
11284
  var CONFLICTS_ACTION_SPECS = {
11048
11285
  list: { params: ["mode"], description: "mode defaults to actionable." },
@@ -11085,26 +11322,26 @@ function registerConflictsTool(server) {
11085
11322
  }
11086
11323
 
11087
11324
  // src/tools/question.ts
11088
- import { z as z32 } from "zod/v3";
11325
+ import { z as z34 } from "zod/v3";
11089
11326
  var QUESTION_ACTIONS = ["create", "assign", "answer", "force-close", "list"];
11090
- var questionSchema = z32.object({
11091
- action: z32.enum(QUESTION_ACTIONS).describe(
11327
+ var questionSchema = z34.object({
11328
+ action: z34.enum(QUESTION_ACTIONS).describe(
11092
11329
  "'create': create a new routed question from text \u2014 the dedicated birth action; every question is born escalated to the team lead (generic entry creation and move-and-retype also pair a routing facet). 'assign': reassign ownership to a role (toRole required \u2014 no bare self-confirm, no toPerson). 'answer': close the loop \u2014 writes the answer on the question and moves it to answered; --entry links an existing decision instead. 'force-close': Team-Lead-only escape hatch \u2014 reason required. 'list': the ack-gate read model \u2014 what you owe (or, with mine:true, every question you asked, including closed ones)."
11093
11330
  ),
11094
- entryId: z32.string().max(200).optional().describe("Target question's id (human TEN-nnn form or a raw entry id). Required for every action except 'create'/'list'."),
11095
- text: z32.string().max(5e3).optional().describe("'create': the question text. 'answer': the answer text (mutually exclusive with answerEntryId, max 2000 chars)."),
11096
- blocks: z32.array(z32.string().max(200)).max(50).optional().describe("'create': entry ids this question is blocking (repeatable)."),
11097
- toRole: z32.string().max(200).optional().describe("'assign': reassign to this role (human ROL-nnn form or a raw entry id) \u2014 required."),
11098
- reason: z32.string().max(2e3).optional().describe("'force-close': required reason."),
11099
- answerEntryId: z32.string().max(200).optional().describe("'answer': point at an already-written entry instead of supplying text (mutually exclusive with text)."),
11100
- mine: z32.boolean().optional().describe("'list': true for arm C (every question YOU asked, every status). Omit/false for the ack-gate view (what you owe).")
11331
+ entryId: z34.string().max(200).optional().describe("Target question's id (human TEN-nnn form or a raw entry id). Required for every action except 'create'/'list'."),
11332
+ text: z34.string().max(5e3).optional().describe("'create': the question text. 'answer': the answer text (mutually exclusive with answerEntryId, max 2000 chars)."),
11333
+ blocks: z34.array(z34.string().max(200)).max(50).optional().describe("'create': entry ids this question is blocking (repeatable)."),
11334
+ toRole: z34.string().max(200).optional().describe("'assign': reassign to this role (human ROL-nnn form or a raw entry id) \u2014 required."),
11335
+ reason: z34.string().max(2e3).optional().describe("'force-close': required reason."),
11336
+ answerEntryId: z34.string().max(200).optional().describe("'answer': point at an already-written entry instead of supplying text (mutually exclusive with text)."),
11337
+ mine: z34.boolean().optional().describe("'list': true for arm C (every question YOU asked, every status). Omit/false for the ack-gate view (what you owe).")
11101
11338
  });
11102
- var createVariant = z32.object({ action: z32.literal("create"), text: z32.string().min(1).max(5e3), blocks: z32.array(z32.string().max(200)).max(50).optional() });
11103
- var assignVariant = z32.object({ action: z32.literal("assign"), entryId: z32.string().max(200), toRole: z32.string().max(200).min(1) });
11104
- var answerVariant = z32.object({ action: z32.literal("answer"), entryId: z32.string().max(200), text: z32.string().max(2e3).optional(), answerEntryId: z32.string().max(200).optional() });
11105
- var forceCloseVariant = z32.object({ action: z32.literal("force-close"), entryId: z32.string().max(200), reason: z32.string().min(1).max(2e3) });
11106
- var listVariant = z32.object({ action: z32.literal("list"), mine: z32.boolean().optional() });
11107
- var questionActionUnion = z32.discriminatedUnion("action", [
11339
+ var createVariant = z34.object({ action: z34.literal("create"), text: z34.string().min(1).max(5e3), blocks: z34.array(z34.string().max(200)).max(50).optional() });
11340
+ var assignVariant = z34.object({ action: z34.literal("assign"), entryId: z34.string().max(200), toRole: z34.string().max(200).min(1) });
11341
+ var answerVariant = z34.object({ action: z34.literal("answer"), entryId: z34.string().max(200), text: z34.string().max(2e3).optional(), answerEntryId: z34.string().max(200).optional() });
11342
+ var forceCloseVariant = z34.object({ action: z34.literal("force-close"), entryId: z34.string().max(200), reason: z34.string().min(1).max(2e3) });
11343
+ var listVariant = z34.object({ action: z34.literal("list"), mine: z34.boolean().optional() });
11344
+ var questionActionUnion = z34.discriminatedUnion("action", [
11108
11345
  createVariant,
11109
11346
  assignVariant,
11110
11347
  answerVariant,
@@ -11258,225 +11495,6 @@ function registerQuestionTools(server) {
11258
11495
  );
11259
11496
  }
11260
11497
 
11261
- // src/tools/role.ts
11262
- import { z as z33 } from "zod/v3";
11263
- var ROLE_ACTIONS = ["accountable", "seat", "unseat", "release_move", "accept_move", "refuse_move"];
11264
- var roleSchema = z33.object({
11265
- action: z33.enum(ROLE_ACTIONS).describe(
11266
- "'accountable': what this role owns (explicit), reports on (reporting), and \u2014 not yet computed \u2014 inherits via Team-Lead vacancy coverage. 'seat': put a person in this role. 'unseat': take this role from a person. 'release_move' / 'accept_move' / 'refuse_move': move a role or team to a team with another Team Lead."
11267
- ),
11268
- roleId: z33.string().max(200).optional().describe("The role, by the human chain id `chain.getEntry` or `pb get` would print for it, or a raw entry id."),
11269
- canonicalKey: z33.string().max(200).optional().describe("'accountable': narrow to one kind (e.g. objective, key_result, tension)."),
11270
- limit: z33.number().int().positive().max(200).optional().describe("'accountable': max explicit-list rows per page (default and maximum 200)."),
11271
- cursor: z33.number().optional().describe("'accountable': continue the explicit list from an earlier truncated page's nextCursor."),
11272
- person: z33.string().max(200).optional().describe(
11273
- "'seat' / 'unseat': an existing person, by person id or exact email. For 'seat', give this or newPersonName, not both."
11274
- ),
11275
- newPersonName: z33.string().max(120).optional().describe(
11276
- "'seat': the name of a person who has no login yet; a placeholder person is created once and reused for the same exact name. Refused when the name belongs to someone with an account."
11277
- ),
11278
- teamId: z33.string().max(200).optional().describe(
11279
- "'seat' / 'unseat': the team, by chain id or entry id. Only needed for Team Lead, which lives in every team; any other role is seated in its home team. 'release_move': the team it moves to."
11280
- ),
11281
- onMyAuthority: z33.boolean().optional().describe(
11282
- "'seat', Team Lead only: as the team's Team Lead, seat a second Team Lead who holds the seat on your authority until you unseat them."
11283
- ),
11284
- childId: z33.string().max(200).optional().describe("'release_move' / 'accept_move' / 'refuse_move': the role or team that moves, by chain id or entry id."),
11285
- why: z33.string().max(1e3).optional().describe("'refuse_move': why the move is refused; it becomes a tension for the old team's Team Lead.")
11286
- });
11287
- var accountableVariant = z33.object({
11288
- action: z33.literal("accountable"),
11289
- roleId: z33.string().max(200),
11290
- canonicalKey: z33.string().max(200).optional(),
11291
- limit: z33.number().int().positive().max(200).optional(),
11292
- cursor: z33.number().optional()
11293
- });
11294
- var seatVariant = z33.object({
11295
- action: z33.literal("seat"),
11296
- roleId: z33.string().max(200),
11297
- person: z33.string().max(200).optional(),
11298
- newPersonName: z33.string().max(120).optional(),
11299
- teamId: z33.string().max(200).optional(),
11300
- onMyAuthority: z33.boolean().optional()
11301
- });
11302
- var unseatVariant = z33.object({
11303
- action: z33.literal("unseat"),
11304
- roleId: z33.string().max(200),
11305
- person: z33.string().max(200),
11306
- teamId: z33.string().max(200).optional()
11307
- });
11308
- var releaseMoveVariant = z33.object({ action: z33.literal("release_move"), childId: z33.string().max(200), teamId: z33.string().max(200) });
11309
- var acceptMoveVariant = z33.object({ action: z33.literal("accept_move"), childId: z33.string().max(200) });
11310
- var refuseMoveVariant = z33.object({ action: z33.literal("refuse_move"), childId: z33.string().max(200), why: z33.string().max(1e3) });
11311
- var roleActionUnion = z33.discriminatedUnion("action", [
11312
- accountableVariant,
11313
- seatVariant,
11314
- unseatVariant,
11315
- releaseMoveVariant,
11316
- acceptMoveVariant,
11317
- refuseMoveVariant
11318
- ]);
11319
- var ROLE_ACTION_SPECS = {
11320
- accountable: { params: ["roleId", "canonicalKey", "limit", "cursor"], description: "roleId is required; canonicalKey/limit/cursor are optional." },
11321
- seat: {
11322
- params: ["roleId", "person", "newPersonName", "teamId", "onMyAuthority"],
11323
- description: "roleId is required, plus exactly one of person or newPersonName; teamId and onMyAuthority only for Team Lead."
11324
- },
11325
- unseat: { params: ["roleId", "person", "teamId"], description: "roleId and person are required; teamId only for Team Lead." },
11326
- release_move: { params: ["childId", "teamId"], description: "childId and teamId (the team it moves to) are required." },
11327
- accept_move: { params: ["childId"], description: "childId is required." },
11328
- refuse_move: { params: ["childId", "why"], description: "childId and why are required." }
11329
- };
11330
- function formatHolders(result2) {
11331
- if (result2.holders.length === 0) return [` holders: none${result2.holdersTruncated ? " found, but the membership scan is incomplete" : ""}`];
11332
- return [
11333
- " holders:",
11334
- ...result2.holders.map((h) => {
11335
- const word = personStatusTag(h.status);
11336
- const tag = word ? ` (${word})` : "";
11337
- const team = h.teamName ? ` in ${sanitizeSingleLineField(h.teamName)}` : "";
11338
- return ` ${sanitizeSingleLineField(h.displayName)}${tag}${team}`;
11339
- }),
11340
- ...result2.holdersTruncated ? [" (membership scan cut at 500 rows, more holders may exist)"] : []
11341
- ];
11342
- }
11343
- function formatRow(row) {
11344
- const id = row.entryId ?? row.id;
11345
- const statusDisplay = row.workflowStatus ? `${row.status} / ${sanitizeSingleLineField(row.workflowStatus)}` : row.status;
11346
- const collectionSlug = row.collectionSlug ? sanitizeSingleLineField(row.collectionSlug) : "?";
11347
- return ` ${id} ${sanitizeSingleLineField(row.name)} [${collectionSlug}/${statusDisplay}]`;
11348
- }
11349
- function formatSection(label, rows, truncated, cursorHint) {
11350
- if (rows.length === 0 && !truncated) return [` ${label}: not recorded yet`];
11351
- const lines = rows.length === 0 ? [` ${label}: not recorded yet, but the scan is incomplete`] : [` ${label}:`, ...rows.map(formatRow)];
11352
- if (truncated) lines.push(` (scan incomplete, more may exist \u2014 ${cursorHint ?? "raise limit (capped at 200, no cursor for this list)"})`);
11353
- return lines;
11354
- }
11355
- function formatRoleAccountable(result2) {
11356
- const roleLabel2 = `${sanitizeSingleLineField(result2.role.name)} (${result2.role.entryId ?? result2.role.id})`;
11357
- const explicitCursorHint = result2.nextCursor !== null ? `raise limit or continue with cursor ${result2.nextCursor}` : "raise limit";
11358
- return [
11359
- `${roleLabel2} \u2014 accountability:`,
11360
- ...formatHolders(result2),
11361
- ...formatSection("explicit (owns)", result2.explicit, result2.explicitTruncated, explicitCursorHint),
11362
- ...formatSection("reporting (reports on)", result2.reporting, result2.reportingTruncated),
11363
- ` inherited: not-computed \u2014 ${result2.inheritedNotComputedReason}`
11364
- ].join("\n");
11365
- }
11366
- async function handleRoleAccountable(roleId, canonicalKey, limit, cursor) {
11367
- try {
11368
- const raw = await kernelQuery("chain.listByRole", { roleId, canonicalKey, limit, cursor });
11369
- const result2 = raw;
11370
- const message = formatRoleAccountable(result2);
11371
- return successResult(message, message, result2);
11372
- } catch (err) {
11373
- return roleErrorResult(err, "accountable", { roleId, canonicalKey, limit, cursor });
11374
- }
11375
- }
11376
- async function runSeatAction(action, fn, params) {
11377
- try {
11378
- requireWriteAccess();
11379
- const sessionId = getAgentSessionId();
11380
- const raw = await kernelMutation(fn, {
11381
- roleRef: params.roleId,
11382
- targetPersonRef: params.person,
11383
- newPersonName: params.newPersonName,
11384
- teamRef: params.teamId,
11385
- sessionId,
11386
- ...params.onMyAuthority ? { onMyAuthority: true } : {}
11387
- });
11388
- const result2 = raw.refusal ? { ...raw, refusal: { ...raw.refusal, requiredAction: authorityRecovery(raw.refusal) } } : raw;
11389
- const message = formatSeatSentence(action, result2, sanitizeSingleLineField);
11390
- if (result2.outcome === "refused") {
11391
- return failureResult(
11392
- message,
11393
- "PERMISSION_DENIED",
11394
- message,
11395
- result2.refusal?.requiredAction ?? "Ask the team's Team Lead to seat them.",
11396
- void 0,
11397
- result2
11398
- );
11399
- }
11400
- return successResult(message, message, result2);
11401
- } catch (err) {
11402
- return roleErrorResult(err, action, params);
11403
- }
11404
- }
11405
- function handleRoleSeat(roleId, person, newPersonName, teamId, onMyAuthority) {
11406
- return runSeatAction("seat", "organisation.seatInRole", { roleId, person, newPersonName, teamId, onMyAuthority });
11407
- }
11408
- function handleRoleUnseat(roleId, person, teamId) {
11409
- return runSeatAction("unseat", "organisation.unseatFromRole", { roleId, person, teamId });
11410
- }
11411
- function callMove(action, params, sessionId) {
11412
- if (action === "release_move") {
11413
- return kernelMutation("organisation.hierarchy.release", { childRef: params.childId, toParentRef: params.teamId, sessionId });
11414
- }
11415
- if (action === "accept_move") return kernelMutation("organisation.hierarchy.accept", { childRef: params.childId, sessionId });
11416
- return kernelMutation("organisation.hierarchy.refuse", { childRef: params.childId, why: params.why, sessionId });
11417
- }
11418
- async function handleMove(action, params) {
11419
- try {
11420
- requireWriteAccess();
11421
- const raw = await callMove(action, params, getAgentSessionId());
11422
- if (raw.outcome === "refused") {
11423
- const recovery = authorityRecovery(raw.refusal) ?? "Ask the Team Lead the refusal names.";
11424
- const message2 = `Refused: ${sanitizeSingleLineField(raw.refusal.reason)} ${recovery}`;
11425
- const code = ORGANISATION_VALIDATION_REFUSALS.has(raw.refusal.code) ? "VALIDATION_ERROR" : "PERMISSION_DENIED";
11426
- return failureResult(message2, code, message2, recovery, void 0, raw);
11427
- }
11428
- const message = raw.replaced ? `${raw.line} It replaces the release toward ${sanitizeSingleLineField(raw.replaced)}.` : raw.line;
11429
- return successResult(message, message, raw);
11430
- } catch (err) {
11431
- return roleErrorResult(err, action, params);
11432
- }
11433
- }
11434
- function roleErrorResult(err, action, params = {}) {
11435
- const classified = classifyError(err);
11436
- const definedParams = Object.fromEntries(Object.entries(params).filter(([, v]) => v !== void 0));
11437
- const fallbackActions = retryIsFutile(classified.code) ? void 0 : [{ tool: "role", description: `Retry ${action}`, parameters: { action, ...definedParams } }];
11438
- return failureResult(
11439
- classified.message,
11440
- classified.code,
11441
- classified.message,
11442
- classified.recovery ?? (action === "accountable" ? "Use action='accountable' with roleId set to a real role's chain id or entry id." : `Fix the input the message names and call action='${action}' again.`),
11443
- classified.availableActions ?? fallbackActions,
11444
- classified.diagnostics
11445
- );
11446
- }
11447
- function registerRoleTools(server) {
11448
- const roleHandlers = {
11449
- accountable: (data) => handleRoleAccountable(data.roleId, data.canonicalKey, data.limit, data.cursor),
11450
- seat: (data) => handleRoleSeat(data.roleId, data.person, data.newPersonName, data.teamId, data.onMyAuthority),
11451
- unseat: (data) => handleRoleUnseat(data.roleId, data.person, data.teamId),
11452
- release_move: (data) => handleMove("release_move", data),
11453
- accept_move: (data) => handleMove("accept_move", data),
11454
- refuse_move: (data) => handleMove("refuse_move", data)
11455
- };
11456
- server.registerTool(
11457
- "role",
11458
- {
11459
- title: "Role",
11460
- description: "Workspace roles \u2014 who is accountable for what, who holds it, and where it sits. Six actions:\n\n- **accountable**: what this role owns (`explicit` \u2014 the canonical ownership row for objective/key_result/tension, data.ownerRoleId for domain), reports on (`reporting`, data.reporterRoleId \u2014 key results only, a separate fact from ownership), and \u2014 always `not-computed` today, never faked as empty \u2014 inherits via Team-Lead vacancy coverage. Also lists `holders`.\n- **seat**: put a person in this role, by person id or email, or by `newPersonName` for someone with no login yet (a placeholder person, created once per exact name). The role's home team is used; name `teamId` only for Team Lead. A held role refuses a second holder and names the holder; the person keeps their other roles in the team. Needs the team's Team Lead. With `onMyAuthority`, the team's Team Lead seats a second Team Lead on their own authority; unseating them revokes it.\n- **unseat**: take this role from a person; their other roles in the team stay. The last active Team Lead of a team is refused. Invite a person, or take them out of a team, with the `people` tool.\n- **release_move** / **accept_move** / **refuse_move**: move a role or team (`childId`) to a team whose Team Lead is another seat. Acting as Team Lead of its current team, release it toward `teamId`; acting as Team Lead of that team, accept it, or refuse it with `why` (it stays, and a tension goes to its current team's Team Lead).",
11461
- inputSchema: roleSchema,
11462
- // The hints describe the whole tool, so they say the most it can do: unseat removes a role and can
11463
- // delete a membership row (destructive), and a repeated seat still writes history and audit rows
11464
- // (not idempotent), even though accountable is a pure read (PR #1508 review). Closed world: nothing here
11465
- // reaches outside the workspace since invite moved to `people` (TASK-677, DEC-1981).
11466
- annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: true, openWorldHint: false }
11467
- },
11468
- thinWrapper(async (args) => {
11469
- const parsed = parseOrFail(roleSchema, args);
11470
- if (!parsed.ok) return parsed.result;
11471
- const { action } = parsed.data;
11472
- return runWithToolContext(
11473
- { tool: "role", action },
11474
- () => dispatchDiscriminated("role", roleActionUnion, parsed.data, ROLE_ACTION_SPECS, roleHandlers)
11475
- );
11476
- })
11477
- );
11478
- }
11479
-
11480
11498
  // src/resources/index.ts
11481
11499
  import { existsSync as existsSync3 } from "fs";
11482
11500
  import { dirname, join, resolve as resolve4 } from "path";
@@ -11805,7 +11823,7 @@ ${entry.labels.map((l) => `- ${l.name ?? l.slug}`).join("\n")}`);
11805
11823
  }
11806
11824
 
11807
11825
  // src/prompts/index.ts
11808
- import { z as z34 } from "zod/v3";
11826
+ import { z as z35 } from "zod/v3";
11809
11827
  async function buildReviewAgainstRulesPrompt(domain) {
11810
11828
  const entries = await kernelQuery("chain.listEntries", { collectionSlug: "rules" });
11811
11829
  const rules = entries.slice(0, 50);
@@ -11887,27 +11905,27 @@ function registerPrompts(server) {
11887
11905
  server.prompt(
11888
11906
  "review-against-rules",
11889
11907
  "Review code or a design decision against the rules, focused on a given area. Fetches the rules and asks you to do a structured compliance review.",
11890
- { domain: z34.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
11908
+ { domain: z35.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
11891
11909
  async ({ domain }) => buildReviewAgainstRulesPrompt(domain)
11892
11910
  );
11893
11911
  server.prompt(
11894
11912
  "name-check",
11895
11913
  "Check variable names, field names, or API names against the glossary for terminology alignment. Flags drift from canonical terms.",
11896
- { names: z34.string().describe("Comma-separated list of names to check (e.g. 'user_id, workspace_id, entry_status')") },
11914
+ { names: z35.string().describe("Comma-separated list of names to check (e.g. 'user_id, workspace_id, entry_status')") },
11897
11915
  async ({ names }) => buildNameCheckPrompt(names)
11898
11916
  );
11899
11917
  server.prompt(
11900
11918
  "draft-decision-record",
11901
11919
  "Draft a structured decision record from a description of what was decided. Includes context from recent decisions and relevant rules.",
11902
- { context: z34.string().describe("Description of the decision (e.g. 'We decided to keep one body field per entry because...')") },
11920
+ { context: z35.string().describe("Description of the decision (e.g. 'We decided to keep one body field per entry because...')") },
11903
11921
  async ({ context }) => buildDraftDecisionRecordPrompt(context)
11904
11922
  );
11905
11923
  server.prompt(
11906
11924
  "draft-rule-from-context",
11907
11925
  "Draft a new business rule from an observation or discovery made while coding. Fetches existing rules for the domain to ensure consistency.",
11908
11926
  {
11909
- observation: z34.string().describe("What you observed or discovered (e.g. 'A workspace can have more than one owner role')"),
11910
- domain: z34.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
11927
+ observation: z35.string().describe("What you observed or discovered (e.g. 'A workspace can have more than one owner role')"),
11928
+ domain: z35.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
11911
11929
  },
11912
11930
  async ({ observation, domain }) => buildDraftRuleFromContextPrompt(observation, domain)
11913
11931
  );
@@ -12003,7 +12021,7 @@ var INSTRUCTIONS = [
12003
12021
  "Finish with `session action=wrapup-review`, then `session action=close`.",
12004
12022
  "If the work falls outside the active work packages, name the gap and ask the user before designing it."
12005
12023
  ].join("\n");
12006
- var sessionIdArg = z35.string().max(200).optional().describe(
12024
+ var sessionIdArg = z36.string().max(200).optional().describe(
12007
12025
  "The sessionId `session action=start` returned. Pass it on every write: the connection does not remember your session between calls."
12008
12026
  );
12009
12027
  function acceptSessionIdOnEveryTool(server) {
@@ -12072,4 +12090,4 @@ export {
12072
12090
  SERVER_VERSION,
12073
12091
  createProductBrainServer
12074
12092
  };
12075
- //# sourceMappingURL=chunk-3AMIZKIT.js.map
12093
+ //# sourceMappingURL=chunk-FYYPTIZM.js.map