@adrata/adrata-mcp 1.0.47 → 1.0.50
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/access/tiers.js +39 -0
- package/api-bridge.js +25 -0
- package/package.json +2 -2
- package/server.js +35 -4
- package/server.json +2 -2
- package/skills/capture-to-idea/SKILL.md +170 -0
- package/tool-annotations.js +85 -0
- package/tools/threads/shared.js +390 -0
- package/tools/threads/work-decision-tools.js +368 -0
- package/tools/threads/work-discussion-tools.js +125 -0
- package/tools/threads/work-goal-tools.js +145 -0
- package/tools/threads/work-idea-tools.js +172 -0
- package/tools/threads/work-indicator-tools.js +129 -0
- package/tools/threads/work-proposition-tools.js +417 -0
- package/tools/threads/work-thread-tools.js +165 -0
package/access/tiers.js
CHANGED
|
@@ -254,6 +254,45 @@ export const TOOL_TIERS = {
|
|
|
254
254
|
update_work_launch: TIERS.ENTERPRISE,
|
|
255
255
|
add_work_item_to_launch: TIERS.ENTERPRISE,
|
|
256
256
|
remove_work_item_from_launch: TIERS.ENTERPRISE,
|
|
257
|
+
// Threads: goals, indicators, decisions, ideas, discussion (tools/threads/).
|
|
258
|
+
list_goals: TIERS.ENTERPRISE,
|
|
259
|
+
get_goal_thread: TIERS.ENTERPRISE,
|
|
260
|
+
create_goal: TIERS.ENTERPRISE,
|
|
261
|
+
link_goal: TIERS.ENTERPRISE,
|
|
262
|
+
list_indicators: TIERS.ENTERPRISE,
|
|
263
|
+
create_indicator: TIERS.ENTERPRISE,
|
|
264
|
+
record_indicator_reading: TIERS.ENTERPRISE,
|
|
265
|
+
list_decisions: TIERS.ENTERPRISE,
|
|
266
|
+
get_decision: TIERS.ENTERPRISE,
|
|
267
|
+
record_decision: TIERS.ENTERPRISE,
|
|
268
|
+
set_decision_status: TIERS.ENTERPRISE,
|
|
269
|
+
record_decision_outcome: TIERS.ENTERPRISE,
|
|
270
|
+
take_decision_position: TIERS.ENTERPRISE,
|
|
271
|
+
link_decision: TIERS.ENTERPRISE,
|
|
272
|
+
get_decision_graph: TIERS.ENTERPRISE,
|
|
273
|
+
relate_decisions: TIERS.ENTERPRISE,
|
|
274
|
+
unrelate_decisions: TIERS.ENTERPRISE,
|
|
275
|
+
list_ideas: TIERS.ENTERPRISE,
|
|
276
|
+
get_idea: TIERS.ENTERPRISE,
|
|
277
|
+
capture_idea: TIERS.ENTERPRISE,
|
|
278
|
+
set_idea_state: TIERS.ENTERPRISE,
|
|
279
|
+
link_idea: TIERS.ENTERPRISE,
|
|
280
|
+
list_discussion: TIERS.ENTERPRISE,
|
|
281
|
+
list_thread_changes: TIERS.ENTERPRISE,
|
|
282
|
+
post_discussion: TIERS.ENTERPRISE,
|
|
283
|
+
resolve_discussion: TIERS.ENTERPRISE,
|
|
284
|
+
// Propositions, the graph read and document filing (tools/threads/work-proposition-tools.js).
|
|
285
|
+
list_propositions: TIERS.ENTERPRISE,
|
|
286
|
+
get_proposition: TIERS.ENTERPRISE,
|
|
287
|
+
get_proposition_calibration: TIERS.ENTERPRISE,
|
|
288
|
+
state_proposition: TIERS.ENTERPRISE,
|
|
289
|
+
record_proposition_reading: TIERS.ENTERPRISE,
|
|
290
|
+
set_proposition_status: TIERS.ENTERPRISE,
|
|
291
|
+
link_proposition: TIERS.ENTERPRISE,
|
|
292
|
+
unlink_proposition: TIERS.ENTERPRISE,
|
|
293
|
+
get_thread_graph: TIERS.ENTERPRISE,
|
|
294
|
+
file_document: TIERS.ENTERPRISE,
|
|
295
|
+
unfile_document: TIERS.ENTERPRISE,
|
|
257
296
|
set_work_board_column_wip_limit: TIERS.ENTERPRISE,
|
|
258
297
|
attach_work_item_qa_evidence: TIERS.ENTERPRISE,
|
|
259
298
|
verify_work_item_qa_evidence_playback: TIERS.ENTERPRISE,
|
package/api-bridge.js
CHANGED
|
@@ -224,6 +224,20 @@ const ALLOWED_PREFIXES = [
|
|
|
224
224
|
'/api/v1/work-items',
|
|
225
225
|
// A launch is a lens over the boards; its writes are board writes.
|
|
226
226
|
'/api/v1/work-launches',
|
|
227
|
+
// The threads family: goals, indicators, decisions, ideas, the discussion
|
|
228
|
+
// under them and their field-change log. None is a board citizen; their
|
|
229
|
+
// writes are board-family writes (read:tasks / write:tasks), and every one
|
|
230
|
+
// is listed separately because matchesPathPrefix is segment-boundary matched.
|
|
231
|
+
'/api/v1/work-decisions',
|
|
232
|
+
'/api/v1/work-discussions',
|
|
233
|
+
'/api/v1/work-goals',
|
|
234
|
+
'/api/v1/work-ideas',
|
|
235
|
+
'/api/v1/work-indicators',
|
|
236
|
+
'/api/v1/work-propositions',
|
|
237
|
+
'/api/v1/work-thread-changes',
|
|
238
|
+
// The one graph read, any root (`/work-threads/graph`); the family name
|
|
239
|
+
// is `work-threads`, listed separately for the same segment-boundary reason.
|
|
240
|
+
'/api/v1/work-threads',
|
|
227
241
|
// The containers above the cards (initiative / epic / feature) and the
|
|
228
242
|
// "add this to the roadmap" verb. Creating an epic proposal is a governed
|
|
229
243
|
// write like any other; the strategic commitment itself still moves through
|
|
@@ -357,6 +371,17 @@ const PATH_WRITE_SCOPES = [
|
|
|
357
371
|
['/api/v1/work-items', 'write:tasks'],
|
|
358
372
|
['/api/v1/work-board-rollups', 'write:tasks'],
|
|
359
373
|
['/api/v1/work-launches', 'write:tasks'],
|
|
374
|
+
// The threads family maps onto the same scope family as the boards it
|
|
375
|
+
// hangs beside; without these rows a thread write previews as
|
|
376
|
+
// `requiredScope: "unknown (no mapping for this path)"`.
|
|
377
|
+
['/api/v1/work-goals', 'write:tasks'],
|
|
378
|
+
['/api/v1/work-indicators', 'write:tasks'],
|
|
379
|
+
['/api/v1/work-decisions', 'write:tasks'],
|
|
380
|
+
['/api/v1/work-ideas', 'write:tasks'],
|
|
381
|
+
['/api/v1/work-discussions', 'write:tasks'],
|
|
382
|
+
['/api/v1/work-thread-changes', 'write:tasks'],
|
|
383
|
+
['/api/v1/work-propositions', 'write:tasks'],
|
|
384
|
+
['/api/v1/work-threads', 'write:tasks'],
|
|
360
385
|
];
|
|
361
386
|
|
|
362
387
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adrata/adrata-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.50",
|
|
4
4
|
"description": "Adrata MCP Server \u2014 connect Claude Code, Codex, Gemini, Cursor, and other AI tools to your CRM. About 275 tools registered at startup for companies, people, deals, actions, buyer groups, warm intros, webhooks and intelligence, plus 65 more behind eight named toolsets you load with enable_toolset.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server.js",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node server.js",
|
|
12
|
-
"test": "node --test analytics.test.js server.test.js api-bridge.test.js http/edge-block.test.js http/rate-limit.test.js audit-flush.test.js buyer-group-writes.test.js note-writes.test.js mcp-spec.test.js packaging.test.js product-profile.test.js security.test.js security.cap-contract.test.js tool-annotations.test.js toolsets.test.js toolsets/communications.test.js toolsets/prospecting.test.js access/auth.test.js access/oauth-callback.test.js access/oauth-session.test.js access/oauth-capabilities.test.js access/oauth-scope-grantability.test.js scripts/local-dev-server.test.js tools/competitive-coverage.test.js tools/describe-count.test.js tools/email-tools.test.js tools/scheduling.test.js tools/work-board-tools.test.js tools/launches/work-launch-tools.test.js tools/work-hub/audit.test.js tools/work-hub/field-changes.test.js tools/work-hub/criteria-quality.test.js tools/roadmap-tools.test.js tools/provisioning/onboarding-tools.test.js tools/source-control/connection-tools.test.js governance/money.test.js http/rate-limit-policy.test.js"
|
|
12
|
+
"test": "node --test analytics.test.js server.test.js api-bridge.test.js http/edge-block.test.js http/rate-limit.test.js audit-flush.test.js buyer-group-writes.test.js note-writes.test.js mcp-spec.test.js packaging.test.js product-profile.test.js security.test.js security.cap-contract.test.js tool-annotations.test.js toolsets.test.js toolsets/communications.test.js toolsets/prospecting.test.js access/auth.test.js access/oauth-callback.test.js access/oauth-session.test.js access/oauth-capabilities.test.js access/oauth-scope-grantability.test.js scripts/local-dev-server.test.js tools/competitive-coverage.test.js tools/describe-count.test.js tools/email-tools.test.js tools/scheduling.test.js tools/work-board-tools.test.js tools/launches/work-launch-tools.test.js tools/threads/work-thread-tools.test.js tools/work-hub/audit.test.js tools/work-hub/field-changes.test.js tools/work-hub/criteria-quality.test.js tools/roadmap-tools.test.js tools/provisioning/onboarding-tools.test.js tools/source-control/connection-tools.test.js governance/money.test.js http/rate-limit-policy.test.js"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|
|
15
15
|
"mcp",
|
package/server.js
CHANGED
|
@@ -54,6 +54,7 @@ import { registerEnterpriseTools } from './tools/enterprise-tools.js';
|
|
|
54
54
|
import { registerEmailTools } from './tools/email-tools.js';
|
|
55
55
|
import { registerWorkBoardTools } from './tools/work-board-tools.js';
|
|
56
56
|
import { registerWorkLaunchTools } from './tools/launches/work-launch-tools.js';
|
|
57
|
+
import { registerWorkThreadTools } from './tools/threads/work-thread-tools.js';
|
|
57
58
|
import { registerSourceControlTools } from './tools/source-control/connection-tools.js';
|
|
58
59
|
import { registerRoadmapTools } from './tools/roadmap-tools.js';
|
|
59
60
|
import { registerProvisioningTools } from './tools/provisioning/onboarding-tools.js';
|
|
@@ -1828,14 +1829,30 @@ server.tool('get_company_firmographics', 'Get employee count and revenue metadat
|
|
|
1828
1829
|
{ companyId: z.string() },
|
|
1829
1830
|
async (a) => ok(await api('GET', `/api/v1/companies/${a.companyId}/firmographics`)));
|
|
1830
1831
|
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1832
|
+
// `enrichPhone` and `forceRefresh` reach the route ONLY through the request
|
|
1833
|
+
// body, and this tool used to send none at all — no `body` key, so the POST went
|
|
1834
|
+
// out empty and the route's `enrich_phone` fell to its `unwrap_or(false)`
|
|
1835
|
+
// default on every call. The flag was therefore unreachable from MCP: an agent
|
|
1836
|
+
// could pass it, the schema would accept it, and no vendor was ever asked for a
|
|
1837
|
+
// phone. That is the "capability that nothing can reach" shape, and it is the
|
|
1838
|
+
// reason phone was never requested on any of the async jobs in production.
|
|
1839
|
+
server.tool('enrich_person', `Trigger enrichment for a person — email, phone, professional info, social profiles. SPENDS VENDOR CREDITS. Phone is a separate paid field (a BetterContact mobile is 10 credits against 1 for an email), so pass enrichPhone: true whenever the ask is for a mobile, cell, direct line or "contact details" — without it no vendor is asked for a number and the run still reports success.${governedWriteNote('write:people')}`,
|
|
1840
|
+
{
|
|
1841
|
+
personId: z.string(),
|
|
1842
|
+
enrichPhone: z.boolean().optional().describe('Also look up a mobile/direct phone. Paid field, explicit opt-in, defaults false.'),
|
|
1843
|
+
forceRefresh: z.boolean().optional().describe('Run even when fields are already populated; only for an explicitly stale or wrong record.'),
|
|
1844
|
+
...governedWriteArgs,
|
|
1845
|
+
},
|
|
1846
|
+
async ({ personId, enrichPhone, forceRefresh, ...g }) => runGovernedWrite(
|
|
1834
1847
|
g,
|
|
1835
1848
|
{
|
|
1836
1849
|
method: 'POST',
|
|
1837
1850
|
path: `/api/v1/people/${personId}/enrich`,
|
|
1838
|
-
|
|
1851
|
+
body: body({ enrichPhone, forceRefresh }),
|
|
1852
|
+
preview: {
|
|
1853
|
+
entity: 'person', entityId: personId, operation: 'enrich',
|
|
1854
|
+
spendsVendorCredits: true, enrichPhone: enrichPhone === true,
|
|
1855
|
+
},
|
|
1839
1856
|
},
|
|
1840
1857
|
));
|
|
1841
1858
|
|
|
@@ -2421,6 +2438,20 @@ registerWorkLaunchTools(server, {
|
|
|
2421
2438
|
getGrantedScope: () => loadTokens()?.scope,
|
|
2422
2439
|
});
|
|
2423
2440
|
|
|
2441
|
+
// ===== THREAD TOOLS =====
|
|
2442
|
+
// Goals, propositions, indicators, decisions, ideas and the discussion under
|
|
2443
|
+
// them — the threads family — plus the one graph read and Galaxy document
|
|
2444
|
+
// filing. Nothing is a board citizen, progress derived on read. Same governed
|
|
2445
|
+
// contract; see tools/threads/work-thread-tools.js.
|
|
2446
|
+
registerWorkThreadTools(server, {
|
|
2447
|
+
z,
|
|
2448
|
+
api,
|
|
2449
|
+
ok,
|
|
2450
|
+
validateApiBridgeRequest,
|
|
2451
|
+
buildMutationHeaders,
|
|
2452
|
+
getGrantedScope: () => loadTokens()?.scope,
|
|
2453
|
+
});
|
|
2454
|
+
|
|
2424
2455
|
registerSourceControlTools(server, {
|
|
2425
2456
|
z,
|
|
2426
2457
|
api,
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "com.adrata/adrata-mcp",
|
|
4
4
|
"description": "Adrata revenue-intelligence MCP server: companies, people, opportunities, actions, buyer groups, enrichment, email, and workspace operations for AI agents.",
|
|
5
5
|
"status": "active",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.50",
|
|
7
7
|
"websiteUrl": "https://adrata.com/developers",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/adrata/adrata",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"registryType": "npm",
|
|
16
16
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
17
17
|
"identifier": "@adrata/adrata-mcp",
|
|
18
|
-
"version": "1.0.
|
|
18
|
+
"version": "1.0.50",
|
|
19
19
|
"transport": {
|
|
20
20
|
"type": "stdio"
|
|
21
21
|
},
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: capture-to-idea
|
|
3
|
+
description: Turn something you just read or heard — a customer call, a competitor's launch, a keynote, an audit finding, a note from the owner — into the right Starfield thread record with its source attached. Use when someone says "capture this", "we should do X", "add this to the ideas", or when an audit turns up a gap worth remembering rather than a bug worth fixing.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Capture to idea
|
|
7
|
+
|
|
8
|
+
The gap between "somebody said something smart" and "we did it" is the
|
|
9
|
+
record. A Slack line evaporates; a card is the wrong size for it; an epic is
|
|
10
|
+
too big a commitment for a hunch. The threads family exists for exactly this
|
|
11
|
+
middle: an **idea** with the vector it arrived on and the source it came from,
|
|
12
|
+
so six weeks later the next person can go back to the transcript instead of
|
|
13
|
+
to your memory of it.
|
|
14
|
+
|
|
15
|
+
This skill is about spending three minutes now so the capture is findable,
|
|
16
|
+
attributable and honest.
|
|
17
|
+
|
|
18
|
+
## First: which noun is it?
|
|
19
|
+
|
|
20
|
+
Four nouns share the thread tools, and putting a thing in the wrong one is
|
|
21
|
+
the most common mistake. Decide before you write:
|
|
22
|
+
|
|
23
|
+
| You are holding… | It is a… | Tool |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| A thing we could build or do, whose value is still an argument | **idea** | `capture_idea` |
|
|
26
|
+
| An outcome we are committing to, or a bet on how to reach one | **goal** / **strategy** | `create_goal` |
|
|
27
|
+
| A ruling — a choice among alternatives that others must now live with | **decision** | `record_decision` |
|
|
28
|
+
| A number that says whether a goal is on track, or a new reading of one | **indicator** / reading | `create_indicator`, `record_indicator_reading` |
|
|
29
|
+
| A claim about the world we are acting on, that evidence could show false | **proposition** | `state_proposition` |
|
|
30
|
+
| A bug, an outage, a thing that is broken right now | a **card** | the `incident-to-card` skill, not this one |
|
|
31
|
+
|
|
32
|
+
Two tells. If the sentence has "we should…" in it, it is an idea until someone
|
|
33
|
+
with the authority to commit says so. If the sentence has "we will no longer…"
|
|
34
|
+
or "from now on…", it is a decision, and it needs the alternatives it beat and
|
|
35
|
+
who decided. A number with no goal behind it is not an indicator yet; capture
|
|
36
|
+
the idea that there should be a goal.
|
|
37
|
+
|
|
38
|
+
## Read the source before you write anything
|
|
39
|
+
|
|
40
|
+
Read the whole thing — the transcript, the doc, the thread, the audit — not the
|
|
41
|
+
summary of it. Then extract, without inventing:
|
|
42
|
+
|
|
43
|
+
1. **The outcome** — what would be true if this idea were realized, as a
|
|
44
|
+
statement. Not the feature. "Sellers see which buyer-group member is the
|
|
45
|
+
real decision-maker before the first call", not "add a decision-maker badge".
|
|
46
|
+
2. **Who it serves** — the audience. `seller | manager | leader` are the people
|
|
47
|
+
Adrata serves; `builder` is us building Adrata, Starfield and Portals;
|
|
48
|
+
`company` is the whole business. Pick the one the source actually names.
|
|
49
|
+
3. **Where it came from** — the vector, chosen honestly (below).
|
|
50
|
+
4. **The source itself** — the specific, re-findable thing: the URL, the
|
|
51
|
+
`company/...` path, the customer's name and the call date, the transcript
|
|
52
|
+
title, the audit's name. "Ross said" is not a source; the ideas notepad path
|
|
53
|
+
and the heading is.
|
|
54
|
+
5. **What it links to** — an existing goal it serves, an epic or card that
|
|
55
|
+
already realizes part of it, a decision that constrains it, another idea it
|
|
56
|
+
is a variant of.
|
|
57
|
+
|
|
58
|
+
If you cannot state the outcome from the source, write what the source said
|
|
59
|
+
and mark the outcome as unstated. A confident outcome the source never
|
|
60
|
+
expressed is your idea wearing their name.
|
|
61
|
+
|
|
62
|
+
## Pick the vector honestly
|
|
63
|
+
|
|
64
|
+
`vector` is where the idea arrived from, and it is required because the
|
|
65
|
+
distribution of vectors is itself a signal — a roadmap fed only by `owner` and
|
|
66
|
+
`internal_audit` is a roadmap nobody outside asked for.
|
|
67
|
+
|
|
68
|
+
- `owner` — the owner said it (the ideas notepad, a direct message, a ruling in
|
|
69
|
+
passing). Only when he actually did.
|
|
70
|
+
- `customer` — a paying or prospective customer said it, named. Not "customers
|
|
71
|
+
probably want".
|
|
72
|
+
- `buyer_demand` — a pattern across buyers: several deals, a lost-deal reason,
|
|
73
|
+
an RFP requirement. Name the deals.
|
|
74
|
+
- `competitor` — a competitor shipped or announced it. Name the release.
|
|
75
|
+
- `keynote` — it came from a talk, a paper, a conference. Name the talk.
|
|
76
|
+
- `internal_audit` — an audit, a review or a measurement of our own product
|
|
77
|
+
turned it up. Name the audit.
|
|
78
|
+
- `technology` — a new capability made it possible (a model, an API, a
|
|
79
|
+
platform change). Name it.
|
|
80
|
+
- `regulation` — a law, a compliance regime, a platform policy requires it.
|
|
81
|
+
|
|
82
|
+
The flattering vector is usually `customer`. If one customer mentioned it once
|
|
83
|
+
in passing, it is `customer` with that customer named and a body that says
|
|
84
|
+
"mentioned once". Do not upgrade it to `buyer_demand`.
|
|
85
|
+
|
|
86
|
+
## Check before you create
|
|
87
|
+
|
|
88
|
+
1. `list_ideas` filtered by the audience, and skim titles. A near-duplicate
|
|
89
|
+
idea is a list nobody reads; if one exists, `post_discussion` on it with the
|
|
90
|
+
new source and `link_idea` if the new source connects it to something.
|
|
91
|
+
2. `list_goals` for the goal it serves. Most ideas serve one; say which.
|
|
92
|
+
3. `list_work_scopes` when the idea sounds like an epic that may already exist.
|
|
93
|
+
If an epic already realizes it, the idea is still worth capturing (the
|
|
94
|
+
source and the vector are new information), linked to that scope.
|
|
95
|
+
|
|
96
|
+
## Writing it
|
|
97
|
+
|
|
98
|
+
**Title**: the outcome, at most 160 characters. Someone scanning `/ideas`
|
|
99
|
+
must be able to tell what would be different without opening it.
|
|
100
|
+
|
|
101
|
+
**Body**: what it is, who it is for, why now, what it would take — under plain
|
|
102
|
+
headings. Quote the source where you can, verbatim, in a block. Say what you
|
|
103
|
+
do not know. Do not write acceptance criteria; an idea is not a card.
|
|
104
|
+
|
|
105
|
+
## Filing it
|
|
106
|
+
|
|
107
|
+
1. `capture_idea` with `title`, `body`, `vector`, `source`, `audience`. The
|
|
108
|
+
idea lands in state `captured`. Do not set a state; exploring is a later
|
|
109
|
+
act with its own reason.
|
|
110
|
+
2. `link_idea` with `scopeId`, `workItemId` or `relatedIdeaId` for each
|
|
111
|
+
connection you found, one call each, with a reason in a sentence.
|
|
112
|
+
3. `link_goal` with the `goalId` and `ideaId` if it serves a goal.
|
|
113
|
+
4. If a decision constrains it, `link_decision` from that decision to the idea.
|
|
114
|
+
|
|
115
|
+
If you decided it was a **goal**: `create_goal` (level `goal`, or `strategy`
|
|
116
|
+
with `parentGoalId`; a strategy is a bet, so give its hypothesis, falsifier and
|
|
117
|
+
confidence). If it was a **decision**: `record_decision` with the alternatives
|
|
118
|
+
and a `door`; it lands `proposed`, and people take positions on it. If it was
|
|
119
|
+
an **indicator**: `create_indicator` with the definition that stops two people
|
|
120
|
+
reading two numbers, then `record_indicator_reading` for the number you have.
|
|
121
|
+
|
|
122
|
+
## Propositions: when the source is a claim, not a thing
|
|
123
|
+
|
|
124
|
+
Some sources are not a thing to build. They are a claim about the world that
|
|
125
|
+
our plans rest on: "enterprise buyers will pay for provable control", "sellers
|
|
126
|
+
will not type into a CRM", "the three rocks are the fastest route to $1M ARR".
|
|
127
|
+
Filing that as an idea loses it; the claim is what a strategy's hypothesis and
|
|
128
|
+
a decision's reversal condition were both privately pointing at. File a
|
|
129
|
+
**proposition** instead:
|
|
130
|
+
|
|
131
|
+
1. `list_propositions` first. The same claim stated twice is two confidence
|
|
132
|
+
histories nobody can reconcile; if it exists, `record_proposition_reading`
|
|
133
|
+
with the new evidence and its source.
|
|
134
|
+
2. `state_proposition` with the `statement` as one sentence, the `rationale`,
|
|
135
|
+
a `confidence` (your honest probability, 0-1; it becomes the first reading
|
|
136
|
+
and is scored later), the `falsifier` — the observation that would show it
|
|
137
|
+
false, required, because a claim with no falsifier is a slogan — and the
|
|
138
|
+
`audience`.
|
|
139
|
+
3. `link_proposition` to each goal, decision, idea or indicator that rests on
|
|
140
|
+
it, with a reason. That is the `assumes` edge; when the claim is falsified,
|
|
141
|
+
everything linked lights up.
|
|
142
|
+
4. If the source is a document — the audit, the transcript, the deck — file it
|
|
143
|
+
with `file_document` (family `proposition`, role `evidence` or `source`).
|
|
144
|
+
|
|
145
|
+
Never edit a proposition's confidence: every change is a reading with its
|
|
146
|
+
evidence. A source that argues *against* a claim we hold is a reading with a
|
|
147
|
+
lower confidence, not a new proposition.
|
|
148
|
+
|
|
149
|
+
## Promotion is a separate act
|
|
150
|
+
|
|
151
|
+
Do not create an epic to capture an idea. When an idea has earned an epic,
|
|
152
|
+
that is `add_to_roadmap` (it creates the proposed scope and hands you the git
|
|
153
|
+
half to write), then `link_idea` with the new `scopeId`, then `set_idea_state`
|
|
154
|
+
to `committed` with the reason. Three verbs, because they are three decisions
|
|
155
|
+
someone can disagree with separately.
|
|
156
|
+
|
|
157
|
+
## Writes
|
|
158
|
+
|
|
159
|
+
Every thread write is governed: it previews by default. Show the human the
|
|
160
|
+
record you are about to file — title, body, vector, source, audience, and the
|
|
161
|
+
links — and file it only after they say yes, with `dryRun: false`,
|
|
162
|
+
`approved: true`, a `reason`, and an `idempotencyKey`. Reuse the same key on
|
|
163
|
+
retry so a flaky connection does not capture the same idea twice.
|
|
164
|
+
|
|
165
|
+
## After filing
|
|
166
|
+
|
|
167
|
+
Report the idea id, its vector and source, and what it was linked to. If
|
|
168
|
+
reading the source surfaced a second thing — a bug, a decision nobody
|
|
169
|
+
recorded, a number nobody tracks — say so as a separate line, clearly marked
|
|
170
|
+
as your observation rather than as something the source said.
|
package/tool-annotations.js
CHANGED
|
@@ -129,6 +129,48 @@ const DOMAIN_OVERRIDES = {
|
|
|
129
129
|
update_work_launch: 'board',
|
|
130
130
|
add_work_item_to_launch: 'board',
|
|
131
131
|
remove_work_item_from_launch: 'board',
|
|
132
|
+
// Threads: goals, indicators, decisions, ideas, discussion (tools/threads/).
|
|
133
|
+
// Board pack because they hang beside the boards and share its scope family;
|
|
134
|
+
// without an override `list_goals` would fall to the general catch-all.
|
|
135
|
+
list_goals: 'board',
|
|
136
|
+
get_goal_thread: 'board',
|
|
137
|
+
create_goal: 'board',
|
|
138
|
+
link_goal: 'board',
|
|
139
|
+
list_indicators: 'board',
|
|
140
|
+
create_indicator: 'board',
|
|
141
|
+
record_indicator_reading: 'board',
|
|
142
|
+
list_decisions: 'board',
|
|
143
|
+
get_decision: 'board',
|
|
144
|
+
record_decision: 'board',
|
|
145
|
+
set_decision_status: 'board',
|
|
146
|
+
record_decision_outcome: 'board',
|
|
147
|
+
take_decision_position: 'board',
|
|
148
|
+
link_decision: 'board',
|
|
149
|
+
get_decision_graph: 'board',
|
|
150
|
+
relate_decisions: 'board',
|
|
151
|
+
unrelate_decisions: 'board',
|
|
152
|
+
list_ideas: 'board',
|
|
153
|
+
get_idea: 'board',
|
|
154
|
+
capture_idea: 'board',
|
|
155
|
+
set_idea_state: 'board',
|
|
156
|
+
link_idea: 'board',
|
|
157
|
+
list_discussion: 'board',
|
|
158
|
+
list_thread_changes: 'board',
|
|
159
|
+
post_discussion: 'board',
|
|
160
|
+
resolve_discussion: 'board',
|
|
161
|
+
// Propositions (the fifth noun), the one graph read and Galaxy document
|
|
162
|
+
// filing live in the same family (tools/threads/work-proposition-tools.js).
|
|
163
|
+
list_propositions: 'board',
|
|
164
|
+
get_proposition: 'board',
|
|
165
|
+
get_proposition_calibration: 'board',
|
|
166
|
+
state_proposition: 'board',
|
|
167
|
+
record_proposition_reading: 'board',
|
|
168
|
+
set_proposition_status: 'board',
|
|
169
|
+
link_proposition: 'board',
|
|
170
|
+
unlink_proposition: 'board',
|
|
171
|
+
get_thread_graph: 'board',
|
|
172
|
+
file_document: 'board',
|
|
173
|
+
unfile_document: 'board',
|
|
132
174
|
list_forms: 'forms',
|
|
133
175
|
get_form: 'forms',
|
|
134
176
|
create_form: 'forms',
|
|
@@ -234,6 +276,14 @@ const DESTRUCTIVE = new Set([
|
|
|
234
276
|
// destructiveHint — a host would not have confirmation-gated a delete.
|
|
235
277
|
'manage_company', 'manage_person', 'manage_opportunity', 'manage_activity',
|
|
236
278
|
'manage_buyer_group',
|
|
279
|
+
// Decision graph: unrelate_decisions DELETEs an edge and the reason recorded
|
|
280
|
+
// on it. The name has no `delete_` prefix (it is the graph's own verb), so
|
|
281
|
+
// it is listed here to be confirmation-gated; it stays idempotent below.
|
|
282
|
+
'unrelate_decisions',
|
|
283
|
+
// Same shape for the proposition's assumes edge and a Galaxy document
|
|
284
|
+
// filing: each DELETE drops the row and the reason recorded on it, and
|
|
285
|
+
// neither name carries a delete_ prefix. Both stay idempotent below.
|
|
286
|
+
'unlink_proposition', 'unfile_document',
|
|
237
287
|
]);
|
|
238
288
|
|
|
239
289
|
/** True when a tool is destructive (irreversible delete, spend, or send). */
|
|
@@ -265,6 +315,31 @@ const IDEMPOTENT_WRITES = new Set([
|
|
|
265
315
|
// A repeat block is the same edge (the pair is unique), and a repeat
|
|
266
316
|
// unblock deletes a row that is already gone. Neither compounds.
|
|
267
317
|
'block_work_item', 'unblock_work_item',
|
|
318
|
+
// Threads. link_goal / link_decision / link_idea are ON CONFLICT upserts on
|
|
319
|
+
// a join table keyed by the pair: replaying the same link rewrites the same
|
|
320
|
+
// row's reason and adds no edge. take_decision_position is an upsert per
|
|
321
|
+
// (decision, user): the second call replaces the first position, so a retry
|
|
322
|
+
// lands on the same state. resolve_discussion sets or clears resolved_at on
|
|
323
|
+
// one root post; a repeat sets what is already set. Deliberately NOT here:
|
|
324
|
+
// record_indicator_reading, post_discussion, create_goal, create_indicator,
|
|
325
|
+
// record_decision and capture_idea each APPEND a row per call, and
|
|
326
|
+
// set_idea_state / set_decision_status each append a history row — see
|
|
327
|
+
// NON_IDEMPOTENT_WRITES.
|
|
328
|
+
'link_goal', 'link_decision', 'link_idea', 'take_decision_position', 'resolve_discussion',
|
|
329
|
+
// Decision graph edges are keyed by (decision, related decision, relation):
|
|
330
|
+
// a repeat relate rewrites the same edge's reason, a repeat unrelate deletes
|
|
331
|
+
// an edge already gone. Neither compounds. unrelate is its own verb rather
|
|
332
|
+
// than a flag on relate so the removal is visible in the tool NAME — and it
|
|
333
|
+
// is in DESTRUCTIVE above (it drops the edge and its reason), so it is
|
|
334
|
+
// destructive AND idempotent, like every delete/revoke.
|
|
335
|
+
'relate_decisions', 'unrelate_decisions',
|
|
336
|
+
// Propositions: link_proposition is an ON CONFLICT upsert keyed by
|
|
337
|
+
// (proposition, target type, target id), and file_document by (target,
|
|
338
|
+
// document): a repeat rewrites the same row's reason (and role). Their
|
|
339
|
+
// removals delete a row already gone on a repeat. Deliberately NOT here:
|
|
340
|
+
// state_proposition and record_proposition_reading each APPEND a row, and
|
|
341
|
+
// set_proposition_status appends a history row — see NON_IDEMPOTENT_WRITES.
|
|
342
|
+
'link_proposition', 'unlink_proposition', 'file_document', 'unfile_document',
|
|
268
343
|
'add_work_item_acceptance_criterion',
|
|
269
344
|
// (delete_work_item needs no entry here either: DESTRUCTIVE_PATTERN matches
|
|
270
345
|
// the `delete_` prefix, so a host confirmation-gates it on its NAME. That is
|
|
@@ -308,6 +383,16 @@ const NON_IDEMPOTENT_WRITES = new Set([
|
|
|
308
383
|
'enable_toolset', 'track_conversion', 'log_interaction', 'test_webhook',
|
|
309
384
|
'replay_webhook_delivery', 'upgrade_account',
|
|
310
385
|
'release_work_item_qa_pass',
|
|
386
|
+
// Threads: every one of these appends a row per call — a reading, a post, a
|
|
387
|
+
// goal, an indicator, a decision, an idea, a state-history row, or a written-
|
|
388
|
+
// once outcome — so a retry under a FRESH idempotency key compounds.
|
|
389
|
+
'create_goal', 'create_indicator', 'record_indicator_reading',
|
|
390
|
+
'record_decision', 'set_decision_status', 'record_decision_outcome',
|
|
391
|
+
'capture_idea', 'set_idea_state', 'post_discussion',
|
|
392
|
+
// Propositions: a stated claim, a reading (the confidence history IS the
|
|
393
|
+
// calibration record, so a duplicate reading is a real distortion), and a
|
|
394
|
+
// status transition with its history row.
|
|
395
|
+
'state_proposition', 'record_proposition_reading', 'set_proposition_status',
|
|
311
396
|
]);
|
|
312
397
|
|
|
313
398
|
// Tools that reach beyond the workspace (external enrichment, web, providers).
|