@rosthq/cli 0.7.69 → 0.7.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -40845,13 +40845,24 @@ var goalListAtRiskOutputSchema = external_exports.object({
40845
40845
  var goalSignalStateSchema = external_exports.enum(["ok", "risk", "crit", "pending"]);
40846
40846
  var goalStatusSourceSchema = external_exports.enum(["human", "agent", "rollup", "signal"]);
40847
40847
  var goalMeasurableRoleSchema = external_exports.enum(["drives_status", "informs"]);
40848
+ var goalDriverAnchorSchema = external_exports.object({
40849
+ // The closed period being anchored on (an ISO date / period label — free-form so a
40850
+ // caller can pass '2026-Q1' or '2026-06-30' as the books define the period).
40851
+ period_start: external_exports.string().min(1),
40852
+ // The value the goal's Signal should show for that closed period per the books.
40853
+ expected_value: external_exports.number()
40854
+ }).strict();
40848
40855
  var goalBindMeasurableInputSchema = external_exports.object({
40849
40856
  goal_id: uuidSchema8,
40850
40857
  measurable_id: uuidSchema8,
40851
40858
  // DER-1643: new operator bindings default to the conservative 'informs' role.
40852
40859
  // 'drives_status' is the deliberate leaf-only status driver (childless goal only,
40853
40860
  // one per goal) and stays human-confirmed.
40854
- role: goalMeasurableRoleSchema.default("informs")
40861
+ role: goalMeasurableRoleSchema.default("informs"),
40862
+ // DER-1126 (ADR-0009 R5): required when role is 'drives_status' — a binding may not
40863
+ // drive status without a second anchor. Optional here (only drives_status needs it);
40864
+ // the command rejects a driver bind that omits it, exactly like signal.bind_confirm.
40865
+ second_anchor: goalDriverAnchorSchema.optional()
40855
40866
  }).strict();
40856
40867
  var goalBindMeasurableOutputSchema = external_exports.object({
40857
40868
  goal_id: uuidSchema8,
@@ -40869,7 +40880,11 @@ var goalBindMeasurableOutputSchema = external_exports.object({
40869
40880
  // DER-1633: false for a fresh binding; true when this exact (goal, Signal) pair was
40870
40881
  // already bound — the command is an idempotent no-op (no new event, no duplicate
40871
40882
  // confirmation). Lets a caller distinguish "just bound" from "already bound".
40872
- already_bound: external_exports.boolean()
40883
+ already_bound: external_exports.boolean(),
40884
+ // DER-1126: the second anchor recorded when this binding was promoted to (or created
40885
+ // as) the status driver. Null for an 'informs' binding (no anchor) and for a legacy
40886
+ // driver bound before the anchor gate existed.
40887
+ driver_anchor: goalDriverAnchorSchema.nullable()
40873
40888
  }).strict();
40874
40889
  var goalUnbindMeasurableInputSchema = external_exports.object({
40875
40890
  goal_id: uuidSchema8,
@@ -40910,7 +40925,11 @@ var goalMeasurableBindingSchema = external_exports.object({
40910
40925
  computed_status: goalStatusSchema.nullable(),
40911
40926
  // The latest confirmed reading's value (null while awaiting one) — enough to render
40912
40927
  // "measured by <Signal>: <value> <unit>" instead of a bare "Not measured".
40913
- last_reading_value: external_exports.number().nullable()
40928
+ last_reading_value: external_exports.number().nullable(),
40929
+ // DER-1126: the second anchor recorded when this driver was confirmed (null for a
40930
+ // legacy driver bound before the anchor gate). Lets the driver view show "Anchored on
40931
+ // <period>: expected <value>".
40932
+ driver_anchor: goalDriverAnchorSchema.nullable()
40914
40933
  }).strict();
40915
40934
  var goalListMeasurablesInputSchema = external_exports.object({
40916
40935
  goal_id: uuidSchema8
@@ -43109,6 +43128,37 @@ var systemHealthOutputSchema = external_exports.object({
43109
43128
  findings: external_exports.array(systemHealthFindingSchema)
43110
43129
  }).strict();
43111
43130
 
43131
+ // ../../packages/protocol/src/baserow-filter-operators.ts
43132
+ var baserowFilterOperatorSchema = external_exports.enum([
43133
+ "equal",
43134
+ "not_equal",
43135
+ "higher_than",
43136
+ "higher_than_or_equal",
43137
+ "lower_than",
43138
+ "lower_than_or_equal",
43139
+ "contains",
43140
+ "contains_not",
43141
+ "empty",
43142
+ "not_empty"
43143
+ ]);
43144
+ var BASEROW_FILTER_OPERATORS = {
43145
+ equal: { requiresValue: true, label: "equals" },
43146
+ not_equal: { requiresValue: true, label: "does not equal" },
43147
+ higher_than: { requiresValue: true, label: "is greater than" },
43148
+ higher_than_or_equal: { requiresValue: true, label: "is greater than or equal to" },
43149
+ lower_than: { requiresValue: true, label: "is less than" },
43150
+ lower_than_or_equal: { requiresValue: true, label: "is less than or equal to" },
43151
+ contains: { requiresValue: true, label: "contains" },
43152
+ contains_not: { requiresValue: true, label: "does not contain" },
43153
+ empty: { requiresValue: false, label: "is empty" },
43154
+ not_empty: { requiresValue: false, label: "is not empty" }
43155
+ };
43156
+ var baserowFilterOperatorOptions = Object.keys(BASEROW_FILTER_OPERATORS).map((value) => ({
43157
+ value,
43158
+ label: BASEROW_FILTER_OPERATORS[value].label,
43159
+ requiresValue: BASEROW_FILTER_OPERATORS[value].requiresValue
43160
+ }));
43161
+
43112
43162
  // ../../packages/protocol/src/signal-source.ts
43113
43163
  var uuid9 = external_exports.string().uuid();
43114
43164
  var signalCadenceSchema = external_exports.enum(["daily", "weekly", "monthly", "quarterly", "annual"]);
@@ -43239,6 +43289,18 @@ var signalBindConfirmOutputSchema = external_exports.object({
43239
43289
  binding_status: external_exports.literal("active")
43240
43290
  }).strict();
43241
43291
 
43292
+ // ../../packages/protocol/src/baserow-filtered-count.ts
43293
+ var FIELD_KEY_PATTERN = /^field_\d+$/;
43294
+ var baserowFilteredCountInputSchema = external_exports.object({
43295
+ endpointHost: external_exports.string().min(1).max(255),
43296
+ // Accept string or number from callers (CLI/MCP/assistant), coerce to a bounded
43297
+ // positive integer before it is ever used as a URL path segment.
43298
+ tableId: external_exports.coerce.number().int().positive().max(2147483647),
43299
+ fieldKey: external_exports.string().regex(FIELD_KEY_PATTERN, "fieldKey must look like field_123"),
43300
+ operator: baserowFilterOperatorSchema,
43301
+ value: external_exports.string().max(200).optional()
43302
+ }).strict();
43303
+
43242
43304
  // ../../packages/protocol/src/signal-report.ts
43243
43305
  var uuidSchema14 = external_exports.string().uuid();
43244
43306
  var dateOnlySchema2 = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, "expected YYYY-MM-DD");
@@ -44747,7 +44809,24 @@ var statusEventPayloadSchema = external_exports.object({
44747
44809
  // so every existing status writer (status.record, corrections, imports) is
44748
44810
  // unaffected.
44749
44811
  note: external_exports.string().min(1).max(2e3).optional(),
44750
- confidence: external_exports.enum(["low", "medium", "high"]).optional()
44812
+ confidence: external_exports.enum(["low", "medium", "high"]).optional(),
44813
+ // DER-1123: when this reading supersedes a materially-different same-period
44814
+ // reading (e.g. an integration pull replacing an agent-reported value), the
44815
+ // prior reading's provenance is captured here — append-only, so the audit
44816
+ // story survives even though the canonical row keeps a single value
44817
+ // (invariant #8, supersession not silent rewrite). Non-secret fields ONLY
44818
+ // (source kind, the numeric value, actor kind, a timestamp); never a vault
44819
+ // ref, token, or raw integration response.
44820
+ supersededFrom: external_exports.object({
44821
+ // The value's origin (agent report / human entry / integration pull).
44822
+ source: external_exports.enum(["agent", "human", "integration"]),
44823
+ value: external_exports.number().finite(),
44824
+ // The actor that last ESTABLISHED the superseded value — its reporter, or the
44825
+ // human who confirmed/corrected it. Optional (a reading may lack a backing
44826
+ // event). Never an actor id — only the coarse kind, so no PII.
44827
+ actorKind: external_exports.enum(["user", "agent", "system"]).optional(),
44828
+ at: external_exports.string().min(1)
44829
+ }).strict().optional()
44751
44830
  }).strict()).optional(),
44752
44831
  goals: external_exports.array(external_exports.object({
44753
44832
  id: uuidSchema16,
@@ -45534,7 +45613,7 @@ Treat AICOS as a coordinator over the operating system. It can become more usefu
45534
45613
  order: 20,
45535
45614
  title: "Responsibility Graph playbook",
45536
45615
  summary: "How to build a functions-first graph with seats, owners, Stewards, vacancies, and clean authority.",
45537
- version: "2026-07-10.2",
45616
+ version: "2026-07-10.3",
45538
45617
  public: true,
45539
45618
  audiences: ["human", "cli", "mcp", "in_app_agent"],
45540
45619
  stages: ["graph_design", "staffing"],
@@ -45607,6 +45686,8 @@ The graph is also where you land after onboarding \u2014 it is the mission contr
45607
45686
  - **Signal** \u2014 the worst measurable state per seat.
45608
45687
  - **Scoreboard** \u2014 direct work and direct cost on each seat, plus a separate team/subtree rollup for manager seats. The tenant summary counts direct agent work once, so a parent seat's team cost does not double-count the same run again. A seat whose direct cost is a clear outlier above the rest of the fleet is flagged as cost drift (labelled, not colour-only). Human seats and seats with no direct or team runs read calmly rather than showing a bare zero. For a small fleet the Scoreboard also leads with a two-tile summary \u2014 total work and total cost \u2014 framed as the single question that matters: is it earning its keep.
45609
45688
 
45689
+ Switching into Cascade never re-runs the chart layout \u2014 seat positions scale in place to fit taller goal-summary cards, and the connector layer scales to match, so nothing rearranges under you. A company-objectives card anchors above the root seat with the active cycle's objectives; selecting one traces and emphasizes its branch down through the connectors while the rest of the chart recedes. Selecting a seat's goal opens the same goal detail panel \`/cascade\` uses, as a right-hand drawer \u2014 breadcrumb ancestry and child-goal rows let you walk the branch without leaving the graph, and an **Open in Cascade** link round-trips the same cycle, branch, and goal selection to the \`/cascade\` page. Lens, objective, goal, seat, and cycle are all URL state, so a reload or a shared link reopens the same view.
45690
+
45610
45691
  The graph also carries a mission-control panel. It pulls the same operational state visible on the agent and setup-health surfaces: held actions that need steward decisions, failed runs, open Friction, pending Signal readings, stale agents, and setup recommendations. Each item can focus the affected seat on the canvas and links to the route where the operator can act.
45611
45692
 
45612
45693
  ## An agent seat's profile
@@ -46350,7 +46431,7 @@ External connectors are being rolled out provider by provider, conservatively (r
46350
46431
  order: 48,
46351
46432
  title: "CLI and MCP installation guide",
46352
46433
  summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
46353
- version: "2026-07-12.3",
46434
+ version: "2026-07-12.4",
46354
46435
  public: true,
46355
46436
  audiences: ["human", "cli", "mcp", "in_app_agent"],
46356
46437
  stages: ["company_setup", "staffing"],
@@ -47030,7 +47111,7 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
47030
47111
  | \`rost_update_cascade_goal\` | \`goal.update\` | Update a goal's title or definition of done. | Tenant | Call with \`goal_id\` and the changed fields. |
47031
47112
  | \`rost_set_cascade_goal_status\` | \`goal.set_status\` | Set a goal's status (on/off/done). | Seat or tenant-admin | Seats may set only their own goals. |
47032
47113
  | \`rost_set_cascade_goal_progress\` | \`goal.set_progress\` | Report a goal's quantified progress (0-100). An agent's progress is a proposal a human approves before the goal moves. | Seat or tenant-admin | Seats set only their own goals; expect confirmation. |
47033
- | \`rost_bind_a_signal_to_a_cascade_goal\` | \`goal.bind_measurable\` | Bind a measurable to a goal. Defaults to role \`informs\` \u2014 a conservative indicator allowed at any goal level (many per goal) that never changes status. Pass role \`drives_status\` to make it the goal's status driver (childless leaf only, one per goal). Human-gated. | Seat or tenant-admin | Call with \`{"goal_id":"<id>","measurable_id":"<id>"}\` (add \`"role":"drives_status"\` for the driver); expect confirmation. |
47114
+ | \`rost_bind_a_signal_to_a_cascade_goal\` | \`goal.bind_measurable\` | Bind a measurable to a goal. Defaults to role \`informs\` \u2014 a conservative indicator allowed at any goal level (many per goal) that never changes status. Pass role \`drives_status\` to make it the goal's status driver (childless leaf only, one per goal); a driver requires a \`second_anchor\` (a prior closed period + expected value confirming the Signal matched the books) and promotes an existing \`informs\` binding of the same Signal. Human-gated. | Seat or tenant-admin | Call with \`{"goal_id":"<id>","measurable_id":"<id>"}\` (add \`"role":"drives_status","second_anchor":{"period_start":"2026-Q1","expected_value":0}\` for the driver); expect confirmation. |
47034
47115
  | \`rost_unbind_a_signal_from_a_cascade_goal\` | \`goal.unbind_measurable\` | Remove a goal\u2194measurable binding (role defaults to \`drives_status\`; pass \`"role":"informs"\` to remove an indicator). | Seat or tenant-admin | Call with \`{"goal_id":"<id>","measurable_id":"<id>"}\`. |
47035
47116
  | \`rost_toggle_signal_driven_auto_status\` | \`goal.set_auto_status\` | Per-goal steward opt-in (default OFF) that lets the bound measurable's latest confirmed reading compute the goal's status. Enabling applies the current signal immediately. Human-gated. | Seat or tenant-admin | Call with \`{"goal_id":"<id>","enabled":true}\`; expect confirmation. |
47036
47117
  | \`rost_list_at_risk_cascade_goals\` | \`goal.list_at_risk\` | List goals projected off pace (at-risk or projected-miss) with days behind pace. Read-only; status-only goals excluded. | Tenant | Call with \`{}\`. |
@@ -47599,7 +47680,7 @@ Retrieve the narrowest relevant guide before making a setup recommendation. Pref
47599
47680
  order: 60,
47600
47681
  title: "Cascade guide",
47601
47682
  summary: "How to connect company goals to seat-level work without turning {{brand}} into a project-management tool.",
47602
- version: "2026-07-12.1",
47683
+ version: "2026-07-12.2",
47603
47684
  public: true,
47604
47685
  audiences: ["human", "cli", "mcp", "in_app_agent"],
47605
47686
  stages: ["operating_rhythm"],
@@ -47678,7 +47759,7 @@ On human and function seat pages, goal visibility includes both direct goals and
47678
47759
 
47679
47760
  ## Drive a goal's status from a Signal
47680
47761
 
47681
- A Signal can be linked to a goal for visibility, or set to compute its status. Bind a Signal with \`goal.bind_measurable\`. By default the binding is **informative** (role \`informs\`): it appears as a direct indicator on the goal, is allowed at any level (company objective, parent, or leaf), can be one of many on the same goal, and never changes the goal's status. To let a Signal **drive** a leaf goal's status instead, bind it with \`--role drives_status\` (the goal must be a childless seat goal or milestone \u2014 a company objective or a goal with children rolls up from its children, so a driver there is rejected). A goal can have at most one driving Signal, and choosing a driver is a deliberate, human-gated act.
47762
+ A Signal can be linked to a goal for visibility, or set to compute its status. Bind a Signal with \`goal.bind_measurable\`. By default the binding is **informative** (role \`informs\`): it appears as a direct indicator on the goal, is allowed at any level (company objective, parent, or leaf), can be one of many on the same goal, and never changes the goal's status. To let a Signal **drive** a leaf goal's status instead, bind it with \`--role drives_status\` (the goal must be a childless seat goal or milestone \u2014 a company objective or a goal with children rolls up from its children, so a driver there is rejected). A goal can have at most one driving Signal, and choosing a driver is a deliberate, human-gated act. Promoting a Signal to the status driver requires a **second anchor**: a prior closed period plus the value that period's books show (\`second_anchor: { period_start, expected_value }\`). The instantaneous reading is not enough \u2014 the anchor proves the Signal matched reality at least once before it may automatically flip a goal, and it is recorded durably with the confirmation. Confirming a driver over an existing informative binding of the same Signal promotes that binding in place.
47682
47763
 
47683
47764
  A \`drives_status\` binding starts as an indicator only: nothing changes until a steward opts in with \`goal.set_auto_status --enabled true\`. Once opted in, the goal's status is computed from the measurable's latest confirmed reading against its target and direction \u2014 on target reads on-track, below target reads off-track. Only confirmed readings drive status; an agent's draft reading never auto-flips a goal. A human who sets the status by hand pins it, and that pin wins over the computed value until they change it or re-run \`goal.set_auto_status\`. Remove a binding with \`goal.unbind_measurable\`; the goal keeps its last status. Until a binding is opted in, an off-track Signal never raises an at-risk alert on its own.
47684
47765
 
@@ -47706,7 +47787,7 @@ Agents can suggest commitments and report progress. They should not create a new
47706
47787
  order: 61,
47707
47788
  title: "Signal guide",
47708
47789
  summary: "How to define and read measurables so the company runs on evidence instead of status theater.",
47709
- version: "2026-07-08.1",
47790
+ version: "2026-07-12.1",
47710
47791
  public: true,
47711
47792
  audiences: ["human", "cli", "mcp", "in_app_agent"],
47712
47793
  stages: ["operating_rhythm"],
@@ -47794,7 +47875,7 @@ Once a measurable's number lives in a connected system, you can teach {{brand}}
47794
47875
  - \`signal.bind\` persists the validated recipe as an inert \`draft\` binding. It requires the \`preview_token\` from a preview of the same recipe, so nothing is bound that was not first seen working.
47795
47876
  - \`signal.bind_confirm\` is human-only. It activates the binding (\`draft -> active\`); an agent that calls it produces a pending confirmation instead. Only after a human confirms does the scheduled pull begin writing confirmed readings.
47796
47877
 
47797
- Discovery always returns a \`recipe_presets\` array; it is empty when {{brand}} does not recognize a safe source type. Today the proven self-filling path is Baserow REST through \`integration.connect_rest\`: a tenant admin creates or rotates the Baserow connection through the command/vault path, then Signal runs \`integration.discover -> signal.preview -> signal.bind -> signal.bind_confirm\` against that connected source. Baserow row-field recipes and custom filtered-count recipes are supported when the human supplies the real table, field, filter, host, and value path. A first-class filtered-count preset is still a follow-up; do not imply every filtered count appears as a one-click preset. Google Sheets Signal pulls are not live yet: Google uses OAuth JSON credentials and remains withheld from REST presets until the pull executor extracts and refreshes the Google access token instead of treating the stored credential blob as a bearer token. A preset includes a label, cadence, required metadata, a draft \`rest_for_signals\` recipe, and the host allowlist to preview with. The human still fills in the real Baserow table/row/field or exact REST URL and value path before running \`signal.preview\`; presets are starter recipes, not secret stores.
47878
+ Discovery always returns a \`recipe_presets\` array; it is empty when {{brand}} does not recognize a safe source type. Today the proven self-filling path is Baserow REST through \`integration.connect_rest\`: a tenant admin creates or rotates the Baserow connection through the command/vault path, then Signal runs \`integration.discover -> signal.preview -> signal.bind -> signal.bind_confirm\` against that connected source. Baserow discovery now offers two first-class presets: \`baserow.row-field\` (read one scalar field from a row) and \`baserow.filtered-count\` (count rows matching a condition). The filtered-count builder takes a table id, field key, comparison operator, and comparison value and assembles a validated \`rest_for_signals\` recipe whose \`value_path\` is the response \`count\`; the field key and operator are allowlisted (they form the query-parameter name) and the comparison value is percent-encoded, so nothing the operator types can inject into the URL. The human still supplies the real table, field, operator, value, and host \u2014 presets and the builder are starter recipes, not secret stores. Google Sheets Signal pulls are not live yet: Google uses OAuth JSON credentials and remains withheld from REST presets until the pull executor extracts and refreshes the Google access token instead of treating the stored credential blob as a bearer token. A preset includes a label, cadence, required metadata, a draft \`rest_for_signals\` recipe, and the host allowlist to preview with. The human still fills in the real Baserow table/row/field or exact REST URL and value path before running \`signal.preview\`; presets are starter recipes, not secret stores.
47798
47879
 
47799
47880
  Recipes are validated data, never code: a read-only GET, a closed set of non-secret headers, and a restricted scalar path to the value. Every fetch passes one SSRF guard (HTTPS only, a steward-confirmed host allowlist set at bind, and a runtime check that the host does not resolve to a private address). The preset output never contains \`nango://\` handles, vault refs, tokens, or static credential headers. Deterministic pulls write \`integration\`-sourced readings on the measurable's cadence.
47800
47881
 
@@ -50321,7 +50402,7 @@ var COMMAND_MANIFEST = [
50321
50402
  { "id": "friction.list", "namespace": "friction", "action": "list", "title": "List Friction issues", "description": "List Friction issues ranked by status, impact, and severity. Defaults to active (open + diagnosing).", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "status", "flag": "status", "type": "enum", "required": false, "enumValues": ["open", "diagnosing", "resolved", "dropped", "active", "all"] }], "hasComplexInput": false, "help": "List Friction issues ranked by impact and severity to find the issue id before updating or resolving it." },
50322
50403
  { "id": "friction.resolve", "namespace": "friction", "action": "resolve", "title": "Resolve Friction issue", "description": 'Resolve an issue with a root cause. In the default "ids" resolution_mode this also creates a remediation follow-up task; "already_fixed" records only the root cause (no follow-up task). Always records a human decision (decided_by) in the same transaction.', "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "issue_id", "flag": "issue-id", "type": "string", "required": true }, { "name": "resolution_mode", "flag": "resolution-mode", "type": "enum", "required": false, "enumValues": ["ids", "already_fixed"] }, { "name": "root_cause", "flag": "root-cause", "type": "string", "required": true }, { "name": "owner_seat_id", "flag": "owner-seat-id", "type": "string", "required": false }, { "name": "task_title", "flag": "task-title", "type": "string", "required": false }, { "name": "task_description", "flag": "task-description", "type": "string", "required": false }, { "name": "done_by", "flag": "done-by", "type": "string", "required": false }, { "name": "resolving_seat_id", "flag": "resolving-seat-id", "type": "string", "required": false }], "hasComplexInput": false, "help": "Resolve an issue with a root cause and remediation task; resolution is a human decision." },
50323
50404
  { "id": "friction.update_status", "namespace": "friction", "action": "update_status", "title": "Update Friction issue status", "description": "Move a non-terminal issue between open and diagnosing. Terminal issues (resolved/dropped) cannot be reopened here.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "issue_id", "flag": "issue-id", "type": "string", "required": true }, { "name": "status", "flag": "status", "type": "enum", "required": true, "enumValues": ["open", "diagnosing"] }], "hasComplexInput": false, "help": "Move a non-terminal issue between open and diagnosing while it is being worked." },
50324
- { "id": "goal.bind_measurable", "namespace": "goal", "action": "bind_measurable", "title": "Bind a Signal to a Cascade goal", "description": "Bind a measurable to a goal. Role defaults to 'informs' \u2014 a conservative informative indicator allowed at any goal level (many per goal) that never changes goal status. Role 'drives_status' makes it the goal's status driver: allowed only on a childless seat goal or milestone, at most one per goal, and auto-status starts OFF until a steward opts in with goal.set_auto_status. Binding is human-gated.", "requiredScope": "seat", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "goal_id", "flag": "goal-id", "type": "string", "required": true }, { "name": "measurable_id", "flag": "measurable-id", "type": "string", "required": true }, { "name": "role", "flag": "role", "type": "enum", "required": false, "enumValues": ["drives_status", "informs"] }], "hasComplexInput": false, "help": "Bind a Signal to a goal. Defaults to role informs (a conservative indicator at any level that never changes status); pass role drives_status to make it a childless leaf goal's status driver (auto-status starts OFF until a steward opts in)." },
50405
+ { "id": "goal.bind_measurable", "namespace": "goal", "action": "bind_measurable", "title": "Bind a Signal to a Cascade goal", "description": "Bind a measurable to a goal. Role defaults to 'informs' \u2014 a conservative informative indicator allowed at any goal level (many per goal) that never changes goal status. Role 'drives_status' makes it the goal's status driver: allowed only on a childless seat goal or milestone, at most one per goal, requires a second_anchor (a prior closed period + expected value confirming the Signal matched the books), and auto-status starts OFF until a steward opts in with goal.set_auto_status. Binding a driver over an existing informs binding of the same Signal promotes it. Binding is human-gated.", "requiredScope": "seat", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "goal_id", "flag": "goal-id", "type": "string", "required": true }, { "name": "measurable_id", "flag": "measurable-id", "type": "string", "required": true }, { "name": "role", "flag": "role", "type": "enum", "required": false, "enumValues": ["drives_status", "informs"] }], "hasComplexInput": true, "help": "Bind a Signal to a goal. Defaults to role informs (a conservative indicator at any level that never changes status); pass role drives_status with a second_anchor (a prior closed period + expected value confirming the Signal matched the books) to make it a childless leaf goal's status driver \u2014 this promotes an existing informs binding (auto-status starts OFF until a steward opts in)." },
50325
50406
  { "id": "goal.create", "namespace": "goal", "action": "create", "title": "Create Cascade goal", "description": "Create a cycle goal under an existing objective. The parent chain must terminate at a Compass objective in the same cycle (enforced server-side).", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "cycle_id", "flag": "cycle-id", "type": "string", "required": true }, { "name": "seat_id", "flag": "seat-id", "type": "string", "required": true }, { "name": "parent_goal_id", "flag": "parent-goal-id", "type": "string", "required": true }, { "name": "title", "flag": "title", "type": "string", "required": true }, { "name": "definition_of_done", "flag": "definition-of-done", "type": "string", "required": true }, { "name": "status", "flag": "status", "type": "enum", "required": false, "enumValues": ["on", "off"] }], "hasComplexInput": false, "help": "Create cycle goals under an existing objective; the parent chain must terminate at a Compass objective." },
50326
50407
  { "id": "goal.drop", "namespace": "goal", "action": "drop", "title": "Drop Cascade goal", "description": "Drop (retire) a goal by setting status to dropped. Dropping retires the commitment, so it is human-gated. The row is retained for audit (never deleted).", "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "goal_id", "flag": "goal-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Drop a goal to retire a commitment; the goal is retained for audit and dropping is human-gated." },
50327
50408
  { "id": "goal.get", "namespace": "goal", "action": "get", "title": "Get Cascade goal", "description": "Get one goal's core detail and status by id. Seat-scoped callers may only read their own seat's goals.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "goal_id", "flag": "goal-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Get one goal's core detail and status by id; seat tokens may only read their own goals." },