@sonnechasser/ntrp 2.1.12 → 2.2.1
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 +21459 -21744
- package/dist/mcp/server.js +292 -562
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -1088,7 +1088,7 @@ import chalk3 from "chalk";
|
|
|
1088
1088
|
function printDeepdiveHint(metricId, label) {
|
|
1089
1089
|
const name = label ?? metricId;
|
|
1090
1090
|
console.log(
|
|
1091
|
-
" " + chalk3.dim("How this number works: ") + paint("accent", `/
|
|
1091
|
+
" " + chalk3.dim("How this number works: ") + paint("accent", `/tour ${metricId}`) + chalk3.dim(` \u2014 ${name}`)
|
|
1092
1092
|
);
|
|
1093
1093
|
console.log();
|
|
1094
1094
|
}
|
|
@@ -12296,145 +12296,42 @@ var init_packs = __esm({
|
|
|
12296
12296
|
SILENT = "Never name methodology brands in customer-visible text. Apply the moves; keep labels silent.";
|
|
12297
12297
|
COUNSEL_PACKS = [
|
|
12298
12298
|
{
|
|
12299
|
-
id: "
|
|
12300
|
-
catalog_line: "-
|
|
12301
|
-
internal_name: "
|
|
12302
|
-
vital_links: [
|
|
12303
|
-
|
|
12304
|
-
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
"
|
|
12308
|
-
"
|
|
12309
|
-
"
|
|
12310
|
-
"No invented reorgs or named people \u2014 function owners only.",
|
|
12311
|
-
"Do not kill zombies first unless freshness/zombies are the gating constraint."
|
|
12312
|
-
],
|
|
12313
|
-
silent_brand_rule: SILENT,
|
|
12314
|
-
role_card_ids: ["vp_sales", "sales_manager", "ae", "sdr", "se", "sales_ops"],
|
|
12315
|
-
tree_weight: { plg: 0.5, smb_velocity: 1, mid_market: 1, enterprise: 1 },
|
|
12316
|
-
body: `THINK: Conversion physics \u2014 coverage vs win rate vs cycle vs noise. Structure (pod, hunter/farmer, overlay, SE pool) explains capacity, not the first fix. Inspection beats activity. Forecast fiction is a credibility problem before it is a revenue problem.
|
|
12317
|
-
ASK: Coverage, conversion, or cycle? Which stage clusters stuck $ ? Thread count on the largest deals? Which activities lack an opp? Where does manager commit diverge from stage evidence?
|
|
12318
|
-
CUT: stage-age, owner, segment, amount band, activity-to-opp link.
|
|
12319
|
-
DISAGREE: vs marketing \u2014 rejection reasons before "bad leads"; vs revops \u2014 keep fields that protect exit criteria, cut vanity; vs cs \u2014 oversell shows up in handoff packet; vs exec \u2014 sandbag vs stretch, use instruments.
|
|
12320
|
-
TREE: plg=assist/expand not a hunting machine. smb_velocity=SDR\u2192AE pod, speed. mid_market=balanced inspection. enterprise=SE pool, named accounts, multi-thread.
|
|
12321
|
-
CARDS: vp_sales for capacity/quota system; sales_manager for inspection; ae for deal strategy; sdr for create/accept; se only if enterprise/complex; sales_ops for territory/comp distortion.
|
|
12322
|
-
PLAYS: get_play_detail unstick-pipeline / multi-thread-deals / clean-dead-pipeline / retarget-effort / compress-sales-cycle only when that vital is the constraint.`
|
|
12323
|
-
},
|
|
12324
|
-
{
|
|
12325
|
-
id: "counsel_marketing",
|
|
12326
|
-
catalog_line: "- counsel_marketing \u2014 demand quality, ABM, PMM/lifecycle; content/brand/partner as support seats",
|
|
12327
|
-
internal_name: "Marketing counsel",
|
|
12328
|
-
vital_links: ["drop_rate", "signal_to_noise", "nrr"],
|
|
12329
|
-
board_cut: "Marketing's dollar contribution and the leak (creation vs handoff vs conversion narrative).",
|
|
12330
|
-
operator_cut: "SLA, scoring, journey gates, program kill rules, PMM adoption exams \u2014 function owners.",
|
|
12331
|
-
when_to_use: "Pipeline creation, handoff leak, magic number / CAC inputs, messaging stalls, named-account whitespace.",
|
|
12332
|
-
kill_rules: [
|
|
12333
|
-
"Do not buy more top-of-funnel when drop_rate is the constraint.",
|
|
12334
|
-
"Do not optimize MQL count against pipeline dollars.",
|
|
12335
|
-
"No ABM theater on smb_velocity without named-account process.",
|
|
12336
|
-
"Stop at accepted pipeline quality + narrative enablement \u2014 do not own close.",
|
|
12337
|
-
"If content/brand/partner is not the constraint, do not open those cards."
|
|
12299
|
+
id: "counsel_gtm",
|
|
12300
|
+
catalog_line: "- counsel_gtm \u2014 one GTM consultant: weigh tradeoffs, pick one constraint, one sequence, one call",
|
|
12301
|
+
internal_name: "GTM consultant",
|
|
12302
|
+
vital_links: [
|
|
12303
|
+
"freshness",
|
|
12304
|
+
"flow_rate",
|
|
12305
|
+
"drop_rate",
|
|
12306
|
+
"signal_to_noise",
|
|
12307
|
+
"thread_depth",
|
|
12308
|
+
"nrr",
|
|
12309
|
+
"grr"
|
|
12338
12310
|
],
|
|
12339
|
-
silent_brand_rule: SILENT,
|
|
12340
|
-
role_card_ids: [
|
|
12341
|
-
"cmo",
|
|
12342
|
-
"demand_gen",
|
|
12343
|
-
"growth",
|
|
12344
|
-
"abm",
|
|
12345
|
-
"pmm",
|
|
12346
|
-
"lifecycle",
|
|
12347
|
-
"mkt_ops",
|
|
12348
|
-
"content",
|
|
12349
|
-
"brand",
|
|
12350
|
-
"field_partner"
|
|
12351
|
-
],
|
|
12352
|
-
tree_weight: { plg: 1, smb_velocity: 0.85, mid_market: 1, enterprise: 1 },
|
|
12353
|
-
body: `THINK: Creation vs acceptance vs narrative. Sub-seats own different constraints \u2014 demand=volume/quality of accepted pipeline; ABM=named-list orchestration; PMM=win-rate/cycle via message; lifecycle=nurture/reactivation without colliding SDR; mkt ops=taxonomy/sync; content/brand/partner=support, not default.
|
|
12354
|
-
ASK: Is the leak before CRM, in routing, or at AE reject? Which channel's pipeline closes? Are MQLs a shared definition? Would fewer named accounts beat more spend?
|
|
12355
|
-
CUT: source, campaign, MQL\u2192SQL\u2192Opp, named-list vs rest, journey vs SDR sequence overlap.
|
|
12356
|
-
DISAGREE: vs sales \u2014 instrument acceptance reasons, not blame; vs revops \u2014 shared definitions, not last-touch religion; vs cs \u2014 frequency caps on customers; vs exec \u2014 brand spend needs a GTM exam or defer.
|
|
12357
|
-
TREE: plg=growth/lifecycle over classical demand. smb_velocity=inbound+outbound shared; no fake ABM. mid_market=demand-led, ABM opportunistic. enterprise=ABM+PMM first.
|
|
12358
|
-
CARDS: demand_gen if creation/quality; abm if named list; pmm if propose/decide stalls; lifecycle if nurture/collision; mkt_ops if scoring/sync; growth if PLG activation; content/brand/field_partner only when that sub-seat is the constraint.
|
|
12359
|
-
PLAYS: get_play_detail demand-quality-over-volume when accepts are weak; abm-orchestration when named-list process exists; rebalance-pipeline-mix / improve-magic-number for mix/efficiency. Handoff dollars usually belong with revops (fix-handoff-gap / harden-routing-sla) \u2014 marketing consumes the SLA.`
|
|
12360
|
-
},
|
|
12361
|
-
{
|
|
12362
|
-
id: "counsel_revops",
|
|
12363
|
-
catalog_line: "- counsel_revops \u2014 GTM systems, routing/SLA/hygiene; forecast/capacity/territory governance",
|
|
12364
|
-
internal_name: "RevOps counsel",
|
|
12365
|
-
vital_links: ["drop_rate", "freshness", "signal_to_noise"],
|
|
12366
|
-
board_cut: "Whether the revenue machine's instruments can be trusted; cost of leak from process/system breaks.",
|
|
12367
|
-
operator_cut: "Routing SLA, stage validation, sync observability, hygiene, GTM eng backlog \u2014 RevOps / mkt ops / GTM eng.",
|
|
12368
|
-
when_to_use: "Broken instruments, routing, sync, definitions, hygiene, automation debt; forecast ritual design; capacity/territory ops.",
|
|
12369
|
-
kill_rules: [
|
|
12370
|
-
"No governance theater when routing/SLA is broken.",
|
|
12371
|
-
"Do not automate bad process.",
|
|
12372
|
-
"No boil-the-ocean warehouse while drop_rate burns.",
|
|
12373
|
-
"Do not take sides on credit wars \u2014 impose shared definitions.",
|
|
12374
|
-
"Prefer system/process enforcement over headcount."
|
|
12375
|
-
],
|
|
12376
|
-
silent_brand_rule: SILENT,
|
|
12377
|
-
role_card_ids: ["vp_revops", "revops_specialist", "gtm_engineer", "mkt_ops", "sales_ops"],
|
|
12378
|
-
tree_weight: { plg: 0.6, smb_velocity: 0.75, mid_market: 1, enterprise: 1 },
|
|
12379
|
-
body: `THINK: 65% systems \u2014 missing vs late vs wrong vs unused data. Foundation identity before activation automation. 35% governance \u2014 forecast ritual (timely commits, inspection cadence), capacity model math, territory/comp ops that distort behavior. Dashboards without SLA/enforcement are theater.
|
|
12380
|
-
ASK: Behavior or instrument? Which timer/queue/sync break maps to the dollar? Do we have observability before another flow? Is forecast wrong because inputs are dirty or because the ritual is missing? Does territory/comp explain attainment better than skill talk?
|
|
12381
|
-
CUT: source\u2192owner path, unassigned age, stage-exit compliance, sync error class, identity match, commit vs inspection evidence.
|
|
12382
|
-
DISAGREE: vs sales \u2014 keep fields that protect exit criteria; vs marketing \u2014 time-box safe launches; vs cs \u2014 identity + handoff packet before dual-tool debates; vs exec \u2014 platform spend tied to constraint dollar and a 30-day hold exam.
|
|
12383
|
-
TREE: plg=product\u2194CRM identity. smb_velocity=lean routing/SLA. mid_market=full OS. enterprise=matrixed + GTM eng.
|
|
12384
|
-
CARDS: revops_specialist for day-to-day CRM/SLA; gtm_engineer for integrations/identity; mkt_ops for MAP/sync; sales_ops for territory/comp; vp_revops for OS tradeoffs and freeze decisions.
|
|
12385
|
-
PLAYS: get_play_detail harden-routing-sla when timers/observability are the leak; fix-handoff-gap when the path/source audit is missing; forecast-ritual-hygiene only after instrument trust; clean-dead-pipeline / retarget-effort when hygiene/automation feeds noise.`
|
|
12386
|
-
},
|
|
12387
|
-
{
|
|
12388
|
-
id: "counsel_exec",
|
|
12389
|
-
catalog_line: "- counsel_exec \u2014 merge seats into one constraint, one sequence, board-ready call",
|
|
12390
|
-
internal_name: "Executive counsel",
|
|
12391
|
-
vital_links: ["freshness", "flow_rate", "drop_rate", "signal_to_noise", "thread_depth", "nrr", "grr"],
|
|
12392
12311
|
board_cut: "The call, the dollar, the one bet, the exam date.",
|
|
12393
12312
|
operator_cut: "Sequenced workstreams with function owners and contingencies.",
|
|
12394
|
-
when_to_use: "
|
|
12313
|
+
when_to_use: "Any GTM advice \u2014 light NL or full plan. Always before plan ship; cross-functional objectives; CS vs new-logo tension.",
|
|
12395
12314
|
kill_rules: [
|
|
12396
|
-
"
|
|
12315
|
+
"Merge, do not concatenate \u2014 one governing constraint and one sequence.",
|
|
12397
12316
|
"No reorg/headcount as first move.",
|
|
12398
12317
|
"No parallel initiatives that starve the bottleneck.",
|
|
12399
12318
|
"Open with verdict + dollars, not methodology.",
|
|
12400
|
-
"Do not
|
|
12319
|
+
"Do not drop retention when NRR/GRR dollars matter alongside new-logo.",
|
|
12320
|
+
"Do not print named-role opinions or pack ids as speakers."
|
|
12401
12321
|
],
|
|
12402
12322
|
silent_brand_rule: SILENT,
|
|
12403
|
-
role_card_ids: [
|
|
12323
|
+
role_card_ids: [],
|
|
12404
12324
|
tree_weight: { plg: 1, smb_velocity: 1, mid_market: 1, enterprise: 1 },
|
|
12405
|
-
body: `THINK: Merge, do not concatenate. One governing constraint. Drop non-constraint theater. Board altitude = verdict + dollar + exam; operator altitude = sequenced owners
|
|
12406
|
-
|
|
12325
|
+
body: `THINK: Merge, do not concatenate. One governing constraint. Drop non-constraint theater. Board altitude = verdict + dollar + exam; operator altitude = sequenced Monday owners (who does the work \u2014 not who is talking).
|
|
12326
|
+
TENSIONS (weigh inside one recommendation \u2014 never as competing seats):
|
|
12327
|
+
- CS vs new-logo: when NRR/GRR dollars and new-logo/freshness dollars both matter, keep both in one sequence under one governing constraint. Do not drop retention.
|
|
12328
|
+
- Quality vs volume: accepted pipeline quality and conversion beat MQL/activity theater when drop_rate or signal_to_noise is the constraint.
|
|
12329
|
+
- Systems vs theater: instrument trust (routing/SLA/hygiene) before narrative bets or dashboards without enforcement.
|
|
12330
|
+
ASK: What unlocks the others? What dies in 90 days if we are wrong? Which ask is polish?
|
|
12407
12331
|
CUT: layer order of vitals; $ at risk by function; in-flight work that collides.
|
|
12408
|
-
DISAGREE
|
|
12332
|
+
DISAGREE (fold in \u2014 do not stage speakers): reject sales-only or marketing-only tunnels when instruments point elsewhere; hold systems work when instruments are untrusted; hold CS in the plan when GRR/NRR is the money; reject more top-of-funnel when drop_rate is the leak.
|
|
12409
12333
|
TREE: plg=CS/growth weight. smb_velocity=sales machine + routing. mid_market=full GTM. enterprise=thread/ABM/SE + NRR.
|
|
12410
|
-
|
|
12411
|
-
PLAYS: do not pick plays here \u2014 sequence the constraint owner's play and demote the rest to contingency.`
|
|
12412
|
-
},
|
|
12413
|
-
{
|
|
12414
|
-
id: "counsel_cs",
|
|
12415
|
-
catalog_line: "- counsel_cs \u2014 NRR/GRR, renewals, TTV/onboarding, sales\u2192CS handoff",
|
|
12416
|
-
internal_name: "Customer Success counsel",
|
|
12417
|
-
vital_links: ["freshness", "nrr", "grr", "contraction_arr"],
|
|
12418
|
-
board_cut: "NRR/GRR dollars at risk and the retention/expansion bet.",
|
|
12419
|
-
operator_cut: "Risk flags, renewal cadence, handoff packet fields, touch model by segment \u2014 CS / renewals / onboarding.",
|
|
12420
|
-
when_to_use: "NRR/GRR/churn/expansion; post-sale handoff; renewals; after sales wins that create CS landmines.",
|
|
12421
|
-
kill_rules: [
|
|
12422
|
-
"Do not push expansion into unhealthy accounts.",
|
|
12423
|
-
"No more QBRs when onboarding/TTV is the constraint.",
|
|
12424
|
-
"Do not treat renewals as pure billing.",
|
|
12425
|
-
"Do not ignore sales handoff quality as root cause.",
|
|
12426
|
-
"No CS headcount before segmentation/touch-model math."
|
|
12427
|
-
],
|
|
12428
|
-
silent_brand_rule: SILENT,
|
|
12429
|
-
role_card_ids: ["vp_cs", "csm", "renewals", "onboarding"],
|
|
12430
|
-
tree_weight: { plg: 1, smb_velocity: 0.7, mid_market: 1, enterprise: 1 },
|
|
12431
|
-
body: `THINK: GRR/NRR dollars. Churn vs failed expansion vs both. TTV/onboarding leak beats QBR theater. Champion loss vs product vs oversell. Expansion only after health restore when churn risk is high. Sales\u2192CS packet is often the real drop after Closed-Won.
|
|
12432
|
-
ASK: Leading indicator 60\u201390 days before churn? Was the sale clean (ICP/scope/handoff)? Onboarding incomplete or value never landed? Expansion owner CS vs AM vs AE?
|
|
12433
|
-
CUT: tenure, segment, product usage, renewal date aging, handoff-packet completeness, champion map.
|
|
12434
|
-
DISAGREE: vs sales \u2014 oversell and late CS involvement; vs marketing \u2014 customer email fatigue; vs revops \u2014 one SoR for renewal dates/risk; vs exec \u2014 growth plans must carry GRR exams.
|
|
12435
|
-
TREE: plg=CS+lifecycle+growth share the constraint. smb_velocity=often folded into CSM/AE; still name GRR. mid_market=full CS OS. enterprise=strategic accounts, renewals specialist.
|
|
12436
|
-
CARDS: csm for health/save; renewals for commercial deadline; vp_cs for touch model and where expansion sits; onboarding for readiness/TTV (capture required when events are missing).
|
|
12437
|
-
PLAYS: get_play_detail reduce-logo-churn / fix-renewal-process / renewal-early-warning / accelerate-expansion / sales-cs-handoff-packet when that instrument is the constraint.`
|
|
12334
|
+
PLAYS: sequence the constraint owner's play and demote the rest to contingency. Monday owners stay function-shaped ("RevOps starts the path audit") \u2014 never nameplates for who is talking.`
|
|
12438
12335
|
}
|
|
12439
12336
|
];
|
|
12440
12337
|
}
|
|
@@ -14181,139 +14078,38 @@ var init_constraint_signals = __esm({
|
|
|
14181
14078
|
});
|
|
14182
14079
|
|
|
14183
14080
|
// src/data/gtm-counsel/seating.ts
|
|
14184
|
-
function defaultSeatOrder(tree) {
|
|
14185
|
-
const tieBreak = TREE_TIE_BREAKS[tree];
|
|
14186
|
-
return COUNSEL_PACKS.filter(
|
|
14187
|
-
(pack) => pack.id !== "counsel_exec"
|
|
14188
|
-
).slice().sort((a, b) => {
|
|
14189
|
-
const weightDelta = b.tree_weight[tree] - a.tree_weight[tree];
|
|
14190
|
-
if (weightDelta !== 0) return weightDelta;
|
|
14191
|
-
return tieBreak.indexOf(a.id) - tieBreak.indexOf(b.id);
|
|
14192
|
-
}).map((pack) => pack.id);
|
|
14193
|
-
}
|
|
14194
|
-
function baselineSeatWeight(packId, tree) {
|
|
14195
|
-
const pack = COUNSEL_PACKS.find((candidate) => candidate.id === packId);
|
|
14196
|
-
const weight = pack?.tree_weight[tree] ?? 0.5;
|
|
14197
|
-
if (weight >= 0.8) return "full";
|
|
14198
|
-
if (weight >= 0.5) return "secondary";
|
|
14199
|
-
return "risk_note";
|
|
14200
|
-
}
|
|
14201
14081
|
function inferConstraintOwnerPacks(objective, playIds = [], gatingVital) {
|
|
14202
14082
|
return resolveLegacyConstraint(objective, playIds, gatingVital).locked_owners;
|
|
14203
14083
|
}
|
|
14204
14084
|
function inferConstraintOwnerPack(objective, playIds = [], gatingVital) {
|
|
14205
14085
|
return inferConstraintOwnerPacks(objective, playIds, gatingVital)[0] ?? null;
|
|
14206
14086
|
}
|
|
14207
|
-
function inferRoundtableBias(
|
|
14208
|
-
|
|
14209
|
-
const notes = [];
|
|
14210
|
-
const demoteToRiskNote = [];
|
|
14211
|
-
const preferFull = [];
|
|
14212
|
-
const strongMarketing = /marketing (lens|view|perspective)|from marketing|as cmo\b/.test(text);
|
|
14213
|
-
const strongRevops = /revops (lens|view)|rev ops (lens|view)|systems view|from revops/.test(text);
|
|
14214
|
-
const strongCs = /cs (lens|view)|success (lens|view)|retention view|nrr view/.test(text);
|
|
14215
|
-
const strongSales = /sales (lens|view|perspective)|from sales|ae view|quota view/.test(text);
|
|
14216
|
-
const weakMarketing = /\b(cmo|demand gen|demand-gen|mqls?\b|abm\b|pmm\b)\b/.test(text);
|
|
14217
|
-
const weakRevops = /\b(revops|rev ops|routing sla|gtm engineer)\b/.test(text);
|
|
14218
|
-
const weakCs = /\b(customer success|renewals manager|csm\b)\b/.test(text);
|
|
14219
|
-
const weakSales = /\b(vp sales|sales manager|quota attain)\b/.test(text);
|
|
14220
|
-
const notCro = /not cro|ignore cro|operator framing|not ceo.centric|skip exec politics/.test(text);
|
|
14221
|
-
if (strongMarketing || weakMarketing) {
|
|
14222
|
-
preferFull.push("counsel_marketing", "counsel_exec");
|
|
14223
|
-
notes.push(strongMarketing ? "Bias: marketing-weighted roundtable" : "Bias: marketing mentioned \u2014 keep marketing full");
|
|
14224
|
-
}
|
|
14225
|
-
if (strongRevops || weakRevops) {
|
|
14226
|
-
preferFull.push("counsel_revops", "counsel_exec");
|
|
14227
|
-
notes.push(strongRevops ? "Bias: RevOps-weighted roundtable" : "Bias: RevOps mentioned \u2014 keep RevOps full");
|
|
14228
|
-
}
|
|
14229
|
-
if (strongCs || weakCs) {
|
|
14230
|
-
preferFull.push("counsel_cs", "counsel_exec");
|
|
14231
|
-
notes.push(strongCs ? "Bias: CS-weighted roundtable" : "Bias: CS mentioned \u2014 keep CS full");
|
|
14232
|
-
}
|
|
14233
|
-
if (strongSales || weakSales) {
|
|
14234
|
-
preferFull.push("counsel_sales", "counsel_exec");
|
|
14235
|
-
notes.push(strongSales ? "Bias: sales-weighted roundtable" : "Bias: sales mentioned \u2014 keep sales full");
|
|
14236
|
-
}
|
|
14237
|
-
if (notCro) {
|
|
14238
|
-
notes.push("Bias: operator framing for exec seat (not CRO-centric voice)");
|
|
14239
|
-
}
|
|
14240
|
-
const strong = [
|
|
14241
|
-
strongMarketing ? "counsel_marketing" : null,
|
|
14242
|
-
strongRevops ? "counsel_revops" : null,
|
|
14243
|
-
strongCs ? "counsel_cs" : null,
|
|
14244
|
-
strongSales ? "counsel_sales" : null
|
|
14245
|
-
].filter((p) => p != null);
|
|
14246
|
-
if (strong.length === 1) {
|
|
14247
|
-
for (const p of ALL_FUNCTION) {
|
|
14248
|
-
if (p !== strong[0]) demoteToRiskNote.push(p);
|
|
14249
|
-
}
|
|
14250
|
-
}
|
|
14251
|
-
return { demoteToRiskNote, preferFull, notes };
|
|
14087
|
+
function inferRoundtableBias(_objective, _profile) {
|
|
14088
|
+
return { demoteToRiskNote: [], preferFull: [], notes: [] };
|
|
14252
14089
|
}
|
|
14253
14090
|
function buildRoundtableSeating(opts) {
|
|
14254
|
-
const
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
|
|
14258
|
-
|
|
14259
|
-
|
|
14260
|
-
|
|
14261
|
-
|
|
14262
|
-
|
|
14263
|
-
|
|
14264
|
-
|
|
14265
|
-
|
|
14266
|
-
|
|
14091
|
+
const constraint = opts.resolvedConstraint ?? resolveLegacyConstraint(
|
|
14092
|
+
opts.objective,
|
|
14093
|
+
opts.playIds ?? [],
|
|
14094
|
+
opts.gatingVital ?? null
|
|
14095
|
+
);
|
|
14096
|
+
const bias_notes = [...constraint.notes ?? []];
|
|
14097
|
+
if (constraint.locked_owners?.length) {
|
|
14098
|
+
bias_notes.push(
|
|
14099
|
+
`Constraint owners (internal, not speakers): ${constraint.locked_owners.join(", ")}`
|
|
14100
|
+
);
|
|
14101
|
+
}
|
|
14102
|
+
return {
|
|
14103
|
+
tree: opts.tree ?? "mid_market",
|
|
14104
|
+
seats: [],
|
|
14105
|
+
bias_notes,
|
|
14106
|
+
constraint
|
|
14267
14107
|
};
|
|
14268
|
-
|
|
14269
|
-
health: opts.health ?? {
|
|
14270
|
-
gating_vital: gatingVital && ["freshness", "flow_rate", "drop_rate", "signal_to_noise", "thread_depth"].includes(
|
|
14271
|
-
gatingVital
|
|
14272
|
-
) ? gatingVital : null
|
|
14273
|
-
},
|
|
14274
|
-
objective: objectiveContext
|
|
14275
|
-
});
|
|
14276
|
-
const constraintOwners = constraint.locked_owners;
|
|
14277
|
-
const ownerSet = new Set(constraintOwners);
|
|
14278
|
-
const demote = new Set(bias.demoteToRiskNote);
|
|
14279
|
-
const prefer = new Set(bias.preferFull);
|
|
14280
|
-
const seats = [{ pack_id: "counsel_exec", weight: "full" }];
|
|
14281
|
-
for (const packId of defaultSeatOrder(tree)) {
|
|
14282
|
-
let weight = baselineSeatWeight(packId, tree);
|
|
14283
|
-
if (demote.has(packId) && !prefer.has(packId)) weight = "risk_note";
|
|
14284
|
-
if (prefer.has(packId)) weight = "full";
|
|
14285
|
-
if (ownerSet.has(packId)) weight = "full";
|
|
14286
|
-
seats.push({ pack_id: packId, weight });
|
|
14287
|
-
}
|
|
14288
|
-
for (const owner of constraintOwners) {
|
|
14289
|
-
if (!seats.some((s) => s.pack_id === owner)) {
|
|
14290
|
-
seats.push({ pack_id: owner, weight: "full" });
|
|
14291
|
-
}
|
|
14292
|
-
}
|
|
14293
|
-
const bias_notes = [...bias.notes, ...constraint.notes];
|
|
14294
|
-
if (constraintOwners.length) {
|
|
14295
|
-
bias_notes.push(`Constraint-owning pack locked full: ${constraintOwners.join(", ")}`);
|
|
14296
|
-
}
|
|
14297
|
-
return { tree, seats, bias_notes, constraint };
|
|
14298
|
-
}
|
|
14299
|
-
var ALL_FUNCTION, TREE_TIE_BREAKS;
|
|
14108
|
+
}
|
|
14300
14109
|
var init_seating = __esm({
|
|
14301
14110
|
"src/data/gtm-counsel/seating.ts"() {
|
|
14302
14111
|
"use strict";
|
|
14303
|
-
init_packs();
|
|
14304
14112
|
init_constraint_signals();
|
|
14305
|
-
ALL_FUNCTION = [
|
|
14306
|
-
"counsel_sales",
|
|
14307
|
-
"counsel_marketing",
|
|
14308
|
-
"counsel_revops",
|
|
14309
|
-
"counsel_cs"
|
|
14310
|
-
];
|
|
14311
|
-
TREE_TIE_BREAKS = {
|
|
14312
|
-
plg: ["counsel_cs", "counsel_marketing", "counsel_revops", "counsel_sales"],
|
|
14313
|
-
smb_velocity: ["counsel_sales", "counsel_revops", "counsel_marketing", "counsel_cs"],
|
|
14314
|
-
mid_market: ["counsel_sales", "counsel_marketing", "counsel_revops", "counsel_cs"],
|
|
14315
|
-
enterprise: ["counsel_sales", "counsel_marketing", "counsel_revops", "counsel_cs"]
|
|
14316
|
-
};
|
|
14317
14113
|
}
|
|
14318
14114
|
});
|
|
14319
14115
|
|
|
@@ -14351,7 +14147,7 @@ __export(gtm_counsel_exports, {
|
|
|
14351
14147
|
});
|
|
14352
14148
|
function buildCounselCatalogBlock() {
|
|
14353
14149
|
const lines = COUNSEL_PACKS.map((p) => p.catalog_line).join("\n");
|
|
14354
|
-
return `GTM
|
|
14150
|
+
return `GTM consultant (one voice). Call get_counsel_detail with counsel_gtm for the pack body. Do not open role cards. Never invent reorgs; never name methodology brands to the user; never print pack ids or function seats as speakers.
|
|
14355
14151
|
${lines}`;
|
|
14356
14152
|
}
|
|
14357
14153
|
function buildOrgTreeContextLine(profile) {
|
|
@@ -14377,7 +14173,7 @@ function formatCounselDetail(pack, tree) {
|
|
|
14377
14173
|
tree_weight: pack.tree_weight[tree],
|
|
14378
14174
|
org_tree: tree,
|
|
14379
14175
|
body: pack.body,
|
|
14380
|
-
notice: "Apply moves in body/board_cut/operator_cut. Never name internal methodology brands to the user.
|
|
14176
|
+
notice: "Apply moves in body/board_cut/operator_cut. Never name internal methodology brands to the user. Speak as one GTM consultant \u2014 never as competing seats or named-role opinions."
|
|
14381
14177
|
};
|
|
14382
14178
|
}
|
|
14383
14179
|
function formatRoleCardDetail(card, tree) {
|
|
@@ -15026,21 +14822,33 @@ Hours saved, weekly activity trend, session counts, AI token usage, and the full
|
|
|
15026
14822
|
Type reset to confirm. This clears hours and milestones. The install identity stays.`
|
|
15027
14823
|
},
|
|
15028
14824
|
{
|
|
15029
|
-
name: "
|
|
14825
|
+
name: "tour",
|
|
15030
14826
|
raw: `---
|
|
15031
|
-
name:
|
|
14827
|
+
name: tour
|
|
15032
14828
|
description: Show slides for numbers and how to use NTRP
|
|
15033
14829
|
section: Navigation
|
|
15034
14830
|
args: [<metric>|guide|list|tour]
|
|
15035
|
-
handler: ../commands/
|
|
14831
|
+
handler: ../commands/tour.ts
|
|
15036
14832
|
---
|
|
15037
14833
|
|
|
15038
14834
|
CLI slide deck: SaaS refresher, five vital signs (definition, formula, visual, dollar translation), then a how-to section (talk, ask, ship to your AI, loop).
|
|
15039
|
-
Bare \`/
|
|
15040
|
-
Type \`/
|
|
15041
|
-
Type \`/
|
|
14835
|
+
Bare \`/tour\` runs the full onboarding tour. Type \`/tour guide\` to jump to how to use NTRP (includes teaching the inbox once).
|
|
14836
|
+
Type \`/tour handoff\` for the ship-to-AI slide. Type \`/tour <metric>\` to jump to one metric card.
|
|
14837
|
+
Type \`/tour list\` to print the catalog. Works without an AI key. Re-run anytime from the homescreen.
|
|
15042
14838
|
During the tour: Enter advances, \`b\` / \`back\` goes to the previous slide, \`q\` quits. After the tour, the same \`b\` / \`back\` word steps back one confirm gate in the REPL (scope, data, strategy) \u2014 it does not unload data or undo compute.
|
|
15043
14839
|
Live values overlay when an analysis exists.`
|
|
14840
|
+
},
|
|
14841
|
+
{
|
|
14842
|
+
name: "deepdive",
|
|
14843
|
+
raw: `---
|
|
14844
|
+
name: deepdive
|
|
14845
|
+
description: Retired \u2014 the slideshow is /tour
|
|
14846
|
+
section: Hidden
|
|
14847
|
+
hidden: true
|
|
14848
|
+
handler: ../commands/deepdive.ts
|
|
14849
|
+
---
|
|
14850
|
+
|
|
14851
|
+
Former name for the numbers slideshow. Prints a one-line pointer to \`/tour\`. Does not open the deck.`
|
|
15044
14852
|
},
|
|
15045
14853
|
{
|
|
15046
14854
|
name: "thinkwithme",
|
|
@@ -16275,39 +16083,19 @@ var init_tool_schemas = __esm({
|
|
|
16275
16083
|
},
|
|
16276
16084
|
{
|
|
16277
16085
|
name: "get_counsel_detail",
|
|
16278
|
-
description: "Read
|
|
16086
|
+
description: "Read the GTM consultant pack (counsel_gtm): when to use, kill rules, vital links, and instruction body. Use before recommending cross-functional work or during strategist plan check. One voice \u2014 never seat competing functions. Never name methodology brands to the user; no invented reorgs.",
|
|
16279
16087
|
parameters: {
|
|
16280
16088
|
type: "object",
|
|
16281
16089
|
properties: {
|
|
16282
16090
|
counsel_id: {
|
|
16283
16091
|
type: "string",
|
|
16284
16092
|
maxLength: 40,
|
|
16285
|
-
description: "Exact id:
|
|
16093
|
+
description: "Exact id: counsel_gtm."
|
|
16286
16094
|
}
|
|
16287
16095
|
},
|
|
16288
16096
|
required: ["counsel_id"]
|
|
16289
16097
|
}
|
|
16290
16098
|
},
|
|
16291
|
-
{
|
|
16292
|
-
name: "get_role_card",
|
|
16293
|
-
description: "Read a GTM role expertise card (e.g. ae, sdr, vp_revops, csm): owns/does-not-own, metrics, consult questions, failure modes, and tensions. Call after get_counsel_detail when you need how a specific seat thinks. Annotates presence for the active org tree.",
|
|
16294
|
-
parameters: {
|
|
16295
|
-
type: "object",
|
|
16296
|
-
properties: {
|
|
16297
|
-
role_id: {
|
|
16298
|
-
type: "string",
|
|
16299
|
-
maxLength: 40,
|
|
16300
|
-
description: "Exact role id listed on the pack you pulled via get_counsel_detail, e.g. 'ae', 'demand_gen', 'gtm_engineer'."
|
|
16301
|
-
},
|
|
16302
|
-
org_tree: {
|
|
16303
|
-
type: "string",
|
|
16304
|
-
enum: ["plg", "smb_velocity", "mid_market", "enterprise"],
|
|
16305
|
-
description: "Optional org tree override; defaults to profile sales_motion / mid_market."
|
|
16306
|
-
}
|
|
16307
|
-
},
|
|
16308
|
-
required: ["role_id"]
|
|
16309
|
-
}
|
|
16310
|
-
},
|
|
16311
16099
|
{
|
|
16312
16100
|
name: "get_session_brief",
|
|
16313
16101
|
description: "Read the 1-page context brief of a PRIOR session by id or 4-char suffix: status, dataset, scope, computed scores with dollar values, headline metrics, deliverables, and conversation log. Use when the user references earlier work \u2014 'last week we found\u2026', 'compare with the previous analysis', 'what did session 9297 conclude?'. Read-only.",
|
|
@@ -18859,17 +18647,9 @@ function renderRuminationLog(job) {
|
|
|
18859
18647
|
);
|
|
18860
18648
|
if (job.library_path) lines.push(`Strategy: ${job.library_path}`);
|
|
18861
18649
|
if (job.handoff_path) lines.push(`Handoff: ${job.handoff_path}`);
|
|
18862
|
-
if (job.roundtable) {
|
|
18863
|
-
lines.push("");
|
|
18864
|
-
lines.push("## Counsel roundtable");
|
|
18650
|
+
if (job.roundtable?.digest) {
|
|
18865
18651
|
lines.push("");
|
|
18866
18652
|
lines.push(job.roundtable.digest);
|
|
18867
|
-
if (job.roundtable.issues.length) {
|
|
18868
|
-
lines.push("");
|
|
18869
|
-
lines.push(
|
|
18870
|
-
`Validation: ${job.roundtable.issues.map((entry) => entry.code).join(", ")}`
|
|
18871
|
-
);
|
|
18872
|
-
}
|
|
18873
18653
|
}
|
|
18874
18654
|
lines.push("");
|
|
18875
18655
|
lines.push("## Rounds");
|
|
@@ -18998,7 +18778,7 @@ function consumeOrientEmptyEnterCoach(ctx) {
|
|
|
18998
18778
|
if (!hasValidLicense()) {
|
|
18999
18779
|
return "Type /activate to paste a key. Type /checkout if you need to sign up.";
|
|
19000
18780
|
}
|
|
19001
|
-
return "Type a question, type use demo data, or type /
|
|
18781
|
+
return "Type a question, type use demo data, or type /tour. Enter alone does not start a step here.";
|
|
19002
18782
|
}
|
|
19003
18783
|
function formatPhaseLabel(phase) {
|
|
19004
18784
|
switch (phase) {
|
|
@@ -19918,7 +19698,7 @@ function buildExploreTeachingGhosts(ctx, staticExplore) {
|
|
|
19918
19698
|
if (cached2 && cached2.length > 0) {
|
|
19919
19699
|
const ghosts = asksToGhostHints(cached2);
|
|
19920
19700
|
const gating = ctx.snapshot.computeResult?.aggregate.gating_vital_sign;
|
|
19921
|
-
if (gating) ghosts.push(`try /
|
|
19701
|
+
if (gating) ghosts.push(`try /tour ${gating}`);
|
|
19922
19702
|
ghosts.push('try "how should we fix this?"');
|
|
19923
19703
|
return dedupeGhosts(ghosts);
|
|
19924
19704
|
}
|
|
@@ -19929,7 +19709,7 @@ function buildExploreTeachingGhosts(ctx, staticExplore) {
|
|
|
19929
19709
|
});
|
|
19930
19710
|
return dedupeGhosts([
|
|
19931
19711
|
...asksToGhostHints(asks),
|
|
19932
|
-
`try /
|
|
19712
|
+
`try /tour ${health.gating_vital_sign}`,
|
|
19933
19713
|
'try "how should we fix this?"'
|
|
19934
19714
|
]);
|
|
19935
19715
|
}
|
|
@@ -20535,7 +20315,7 @@ function buildDefinitionsAppendix(bundle, opts = {}) {
|
|
|
20535
20315
|
lines.push(`## Metric definitions (for the ${audienceLabel(audience)})`);
|
|
20536
20316
|
lines.push("");
|
|
20537
20317
|
lines.push(
|
|
20538
|
-
audience === "ops" ? "Formula-first brief for operators who execute the plan. Full slides: `/
|
|
20318
|
+
audience === "ops" ? "Formula-first brief for operators who execute the plan. Full slides: `/tour <metric>`." : "Meaning-first brief for the room. Full slides: `/tour <metric>`."
|
|
20539
20319
|
);
|
|
20540
20320
|
lines.push("");
|
|
20541
20321
|
for (const c of candidates) {
|
|
@@ -20622,8 +20402,8 @@ function generateMarkdownReport(data) {
|
|
|
20622
20402
|
lines.push("");
|
|
20623
20403
|
for (const entity of vs.entity_details.slice(0, 5)) {
|
|
20624
20404
|
const name = entity.name ?? entity.id ?? "Unknown";
|
|
20625
|
-
const
|
|
20626
|
-
lines.push(`- **${name}**: ${
|
|
20405
|
+
const issue = entity.issue ?? "";
|
|
20406
|
+
lines.push(`- **${name}**: ${issue}`);
|
|
20627
20407
|
}
|
|
20628
20408
|
if (vs.entity_details.length > 5) {
|
|
20629
20409
|
lines.push(`- *...and ${vs.entity_details.length - 5} more*`);
|
|
@@ -21636,56 +21416,36 @@ Respond with the full plan as strict JSON matching this schema \u2014 no markdow
|
|
|
21636
21416
|
${STRATEGIST_PLAN_SCHEMA_BLOCK}`;
|
|
21637
21417
|
}
|
|
21638
21418
|
function buildRoundtableMessage(input) {
|
|
21639
|
-
const bias = input.biasNotes.length > 0 ? `
|
|
21419
|
+
const bias = input.biasNotes.length > 0 ? `Weighing notes:
|
|
21640
21420
|
- ${input.biasNotes.join("\n- ")}
|
|
21641
21421
|
|
|
21642
21422
|
` : "";
|
|
21643
|
-
const
|
|
21644
|
-
|
|
21645
|
-
` : "";
|
|
21646
|
-
const constraint = input.constraint ? `CONSTRAINT CHANNELS:
|
|
21423
|
+
const constraint = input.constraint ? `CONSTRAINT CHANNELS (weigh inside one recommendation \u2014 do not print as speakers):
|
|
21647
21424
|
- Health gate: ${input.constraint.health_gate ? String(input.constraint.health_gate.id) : "not verified"} \u2192 ${input.constraint.health_gate_owners.join(", ") || "no mapped owner"}
|
|
21648
21425
|
- Objective constraint: ${input.constraint.objective_constraint ? String(input.constraint.objective_constraint.id) : "not verified"} \u2192 ${input.constraint.objective_owners.join(", ") || "no mapped owner"}
|
|
21649
|
-
- If
|
|
21426
|
+
- If both matter (e.g. freshness/new-logo dollars and red NRR/GRR), keep both in one sequence under one governing constraint. Do not drop retention.
|
|
21650
21427
|
|
|
21651
21428
|
` : "";
|
|
21652
|
-
return `STAGE R \u2014
|
|
21429
|
+
return `STAGE R \u2014 PLAN CHECK. Org tree: ${input.tree}. Before stress-testing, run one GTM consultant check on the draft. Speak as NTRP \u2014 one voice. No seats, no CRO/CMO/CS/sales/marketing/RevOps/Exec nameplates.
|
|
21653
21430
|
|
|
21654
|
-
${
|
|
21655
|
-
FUNCTION SEATS (weight full = deep challenge; secondary/risk_note = short risk note only):
|
|
21656
|
-
${input.seatsBlock}
|
|
21657
|
-
|
|
21658
|
-
Call get_counsel_detail only for full seats. risk_note and secondary seats write a short risk without a full pack pull. Cap get_role_card to one id per challenged workstream owner \u2014 pick from that pack's role_card_ids. Call get_play_detail if a play_id is thin. Do not invent reorgs or named people. Never name methodology brands in customer-facing fields.
|
|
21431
|
+
${constraint}${bias}Call get_counsel_detail for counsel_gtm only. Do not call get_role_card. Call get_play_detail if a play_id is thin. Do not invent reorgs or named people. Never name methodology brands in customer-facing fields. Monday owners on actions stay function-shaped ("RevOps starts the path audit") \u2014 that is who does the work, not who is talking.
|
|
21659
21432
|
|
|
21660
21433
|
DRAFT PLAN JSON:
|
|
21661
21434
|
${input.draftPlanJson}
|
|
21662
21435
|
|
|
21663
21436
|
Respond with STRICT JSON only (no prose, no fences):
|
|
21664
21437
|
{
|
|
21665
|
-
"
|
|
21666
|
-
|
|
21667
|
-
|
|
21668
|
-
|
|
21669
|
-
|
|
21670
|
-
"challenge": "\u226440 words \u2014 what the draft gets wrong or misses; empty only if support + risk or exec pre-mortem",
|
|
21671
|
-
"missing_exam": "metric + date + instrument (or empty string)",
|
|
21672
|
-
"cross_functional_risk": "\u226430 words",
|
|
21673
|
-
"role_cards_used": ["ae"]
|
|
21674
|
-
}
|
|
21675
|
-
],
|
|
21676
|
-
"exec": {
|
|
21677
|
-
"governing_constraint": "one constraint with dollar label",
|
|
21678
|
-
"sequence": ["play or workstream order"],
|
|
21679
|
-
"drop": ["non-constraint theater to kill"],
|
|
21680
|
-
"merge_verdict": "\u226450 words",
|
|
21681
|
-
"pre_mortem": "if all seats support: the one most likely death cause; else optional"
|
|
21682
|
-
}
|
|
21438
|
+
"governing_constraint": "one constraint with dollar label",
|
|
21439
|
+
"sequence": ["first move", "second move", "third move"],
|
|
21440
|
+
"drop": "non-constraint theater to kill",
|
|
21441
|
+
"verdict": "\u226450 words \u2014 one recommendation that already weighed the tradeoffs",
|
|
21442
|
+
"pre_mortem": "required \u2014 the one most likely death cause if this plan ships as-is"
|
|
21683
21443
|
}
|
|
21684
21444
|
|
|
21685
|
-
Rules:
|
|
21445
|
+
Rules: one consultant, one sequence, one governing constraint. Fold CS vs new-logo, quality vs volume, and systems vs theater into the recommendation \u2014 never as competing opinions. pre_mortem is always required. Never emit seats[], exec wrappers, pack ids as speakers, or CRO/CMO/CS says headers.`;
|
|
21686
21446
|
}
|
|
21687
21447
|
function buildStressTestMessage(roundtableDigest) {
|
|
21688
|
-
const roundtableBlock = roundtableDigest ? `0.
|
|
21448
|
+
const roundtableBlock = roundtableDigest ? `0. PLAN CHECK DIGEST (mandatory): address every challenge \u2014 accept and revise, or reject with an instrument reason. Add missing_exam items or explicitly defer with why. Reflect Drop in the final plan. If the check was all-agree, Pre-mortem is the death cause check 1 must defend \u2014 do not invent a fake challenge after the fact.
|
|
21689
21449
|
${roundtableDigest}
|
|
21690
21450
|
|
|
21691
21451
|
` : "";
|
|
@@ -22252,181 +22012,169 @@ function str2(value) {
|
|
|
22252
22012
|
return typeof value === "string" ? value.trim() : "";
|
|
22253
22013
|
}
|
|
22254
22014
|
function strArray2(value) {
|
|
22015
|
+
if (typeof value === "string") {
|
|
22016
|
+
const t = value.trim();
|
|
22017
|
+
return t ? [t] : [];
|
|
22018
|
+
}
|
|
22255
22019
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean) : [];
|
|
22256
22020
|
}
|
|
22257
22021
|
function words(value) {
|
|
22258
22022
|
return value.trim() ? value.trim().split(/\s+/).length : 0;
|
|
22259
22023
|
}
|
|
22260
|
-
function
|
|
22024
|
+
function pushIssue(issues, code, message, severity = "blocking") {
|
|
22261
22025
|
issues.push({ code, severity, message });
|
|
22262
22026
|
}
|
|
22263
|
-
function
|
|
22264
|
-
|
|
22265
|
-
|
|
22266
|
-
|
|
22267
|
-
}
|
|
22268
|
-
const obj = raw;
|
|
22269
|
-
const pack = str2(obj.pack);
|
|
22270
|
-
if (pack === "counsel_exec") {
|
|
22271
|
-
issue(issues, "exec_in_seats", "counsel_exec must appear only in the top-level exec object.");
|
|
22272
|
-
return null;
|
|
22273
|
-
}
|
|
22274
|
-
if (!FUNCTION_PACKS.has(pack)) {
|
|
22275
|
-
issue(issues, "unknown_pack", `Seat ${index + 1} has unknown pack '${pack || "(empty)"}'.`);
|
|
22276
|
-
return null;
|
|
22277
|
-
}
|
|
22278
|
-
const stance = str2(obj.stance);
|
|
22279
|
-
if (!STANCES.has(stance)) {
|
|
22280
|
-
issue(issues, "invalid_stance", `${pack} has invalid stance '${stance || "(empty)"}'.`);
|
|
22281
|
-
}
|
|
22282
|
-
if (typeof obj.constraint_agree !== "boolean") {
|
|
22283
|
-
issue(issues, "constraint_agree", `${pack} must set constraint_agree to true or false.`);
|
|
22284
|
-
}
|
|
22285
|
-
const challenge = str2(obj.challenge);
|
|
22286
|
-
const risk = str2(obj.cross_functional_risk);
|
|
22287
|
-
const missingExam = str2(obj.missing_exam);
|
|
22288
|
-
if (words(challenge) > 40) {
|
|
22289
|
-
issue(issues, "challenge_length", `${pack} challenge exceeds 40 words.`);
|
|
22290
|
-
}
|
|
22291
|
-
if (words(risk) > 30) {
|
|
22292
|
-
issue(issues, "risk_length", `${pack} cross_functional_risk exceeds 30 words.`);
|
|
22293
|
-
}
|
|
22294
|
-
const roleCards = strArray2(obj.role_cards_used);
|
|
22295
|
-
const allowed = new Set(getCounselPackById(pack)?.role_card_ids ?? []);
|
|
22296
|
-
const validRoles = [];
|
|
22297
|
-
for (const roleId of roleCards) {
|
|
22298
|
-
if (!getRoleCardById(roleId) || !allowed.has(roleId)) {
|
|
22299
|
-
issue(
|
|
22027
|
+
function scanBannedSpeakers(text, issues, field) {
|
|
22028
|
+
for (const pattern of BANNED_SPEAKER_PATTERNS) {
|
|
22029
|
+
if (pattern.test(text)) {
|
|
22030
|
+
pushIssue(
|
|
22300
22031
|
issues,
|
|
22301
|
-
"
|
|
22302
|
-
`${
|
|
22032
|
+
"banned_speaker",
|
|
22033
|
+
`${field} must not name competing seats or role speakers.`
|
|
22303
22034
|
);
|
|
22304
|
-
|
|
22035
|
+
return;
|
|
22305
22036
|
}
|
|
22306
|
-
validRoles.push(roleId);
|
|
22307
22037
|
}
|
|
22308
|
-
return {
|
|
22309
|
-
pack,
|
|
22310
|
-
stance: STANCES.has(stance) ? stance : "abstain",
|
|
22311
|
-
constraint_agree: obj.constraint_agree === true,
|
|
22312
|
-
challenge,
|
|
22313
|
-
missing_exam: missingExam,
|
|
22314
|
-
cross_functional_risk: risk,
|
|
22315
|
-
role_cards_used: validRoles
|
|
22316
|
-
};
|
|
22317
22038
|
}
|
|
22318
|
-
function
|
|
22039
|
+
function parseConsultantCheck(raw, issues) {
|
|
22319
22040
|
if (!raw || typeof raw !== "object") {
|
|
22320
|
-
|
|
22041
|
+
pushIssue(issues, "consultant_shape", "Consultant check output is not a JSON object.");
|
|
22321
22042
|
return null;
|
|
22322
22043
|
}
|
|
22323
22044
|
const obj = raw;
|
|
22045
|
+
if ("seats" in obj) {
|
|
22046
|
+
pushIssue(issues, "seats_forbidden", "Consultant check must not include a seats array.");
|
|
22047
|
+
}
|
|
22048
|
+
if (obj.exec && typeof obj.exec === "object") {
|
|
22049
|
+
pushIssue(
|
|
22050
|
+
issues,
|
|
22051
|
+
"exec_wrapper_forbidden",
|
|
22052
|
+
"Consultant check must be a flat object \u2014 not seats[] + exec."
|
|
22053
|
+
);
|
|
22054
|
+
}
|
|
22324
22055
|
const governing = str2(obj.governing_constraint);
|
|
22325
|
-
const
|
|
22326
|
-
|
|
22327
|
-
|
|
22328
|
-
|
|
22329
|
-
|
|
22056
|
+
const sequence = strArray2(obj.sequence);
|
|
22057
|
+
const dropRaw = obj.drop;
|
|
22058
|
+
const drop = Array.isArray(dropRaw) ? strArray2(dropRaw).join("; ") : str2(dropRaw);
|
|
22059
|
+
const verdict = str2(obj.verdict) || str2(obj.merge_verdict);
|
|
22060
|
+
const preMortem = str2(obj.pre_mortem) || str2(obj.preMortem);
|
|
22061
|
+
if (!governing) pushIssue(issues, "governing_constraint", "governing_constraint is required.");
|
|
22062
|
+
if (!verdict) pushIssue(issues, "verdict", "verdict is required.");
|
|
22063
|
+
if (words(verdict) > 50) pushIssue(issues, "verdict_length", "verdict exceeds 50 words.");
|
|
22064
|
+
if (!preMortem) {
|
|
22065
|
+
pushIssue(
|
|
22066
|
+
issues,
|
|
22067
|
+
"missing_pre_mortem",
|
|
22068
|
+
"pre_mortem is required (all-agree checks still need a death cause)."
|
|
22069
|
+
);
|
|
22070
|
+
}
|
|
22071
|
+
for (const [field, text] of [
|
|
22072
|
+
["governing_constraint", governing],
|
|
22073
|
+
["verdict", verdict],
|
|
22074
|
+
["drop", drop],
|
|
22075
|
+
["pre_mortem", preMortem],
|
|
22076
|
+
["sequence", sequence.join(" ")]
|
|
22077
|
+
]) {
|
|
22078
|
+
if (text) scanBannedSpeakers(text, issues, field);
|
|
22330
22079
|
}
|
|
22331
22080
|
return {
|
|
22332
22081
|
governing_constraint: governing,
|
|
22333
|
-
sequence
|
|
22334
|
-
drop
|
|
22335
|
-
|
|
22336
|
-
pre_mortem:
|
|
22082
|
+
sequence,
|
|
22083
|
+
drop,
|
|
22084
|
+
verdict,
|
|
22085
|
+
pre_mortem: preMortem
|
|
22337
22086
|
};
|
|
22338
22087
|
}
|
|
22339
|
-
function validateRoundtableResult(raw, options) {
|
|
22088
|
+
function validateRoundtableResult(raw, options = {}) {
|
|
22340
22089
|
const issues = [];
|
|
22341
22090
|
if (!raw || typeof raw !== "object") {
|
|
22342
22091
|
return {
|
|
22343
22092
|
result: null,
|
|
22344
|
-
issues: [
|
|
22093
|
+
issues: [
|
|
22094
|
+
{
|
|
22095
|
+
code: "consultant_shape",
|
|
22096
|
+
severity: "blocking",
|
|
22097
|
+
message: "Consultant check output is not a JSON object."
|
|
22098
|
+
}
|
|
22099
|
+
],
|
|
22345
22100
|
blocking: true
|
|
22346
22101
|
};
|
|
22347
22102
|
}
|
|
22348
|
-
const
|
|
22349
|
-
if (!Array.isArray(obj.seats)) {
|
|
22350
|
-
issue(issues, "missing_seats", "Roundtable requires a seats array.");
|
|
22351
|
-
}
|
|
22352
|
-
const seats = (Array.isArray(obj.seats) ? obj.seats : []).map((seat, index) => parseSeat(seat, issues, index)).filter((seat) => seat != null);
|
|
22353
|
-
const exec = parseExec(obj.exec, issues);
|
|
22354
|
-
const seen = /* @__PURE__ */ new Set();
|
|
22355
|
-
for (const seat of seats) {
|
|
22356
|
-
if (seen.has(seat.pack)) {
|
|
22357
|
-
issue(issues, "duplicate_seat", `Roundtable contains duplicate seat ${seat.pack}.`);
|
|
22358
|
-
}
|
|
22359
|
-
seen.add(seat.pack);
|
|
22360
|
-
}
|
|
22361
|
-
const expected = options.seating.seats.filter((seat) => seat.pack_id !== "counsel_exec").map((seat) => seat.pack_id);
|
|
22362
|
-
for (const pack of expected) {
|
|
22363
|
-
if (!seen.has(pack)) issue(issues, "missing_seat", `Roundtable omitted seated pack ${pack}.`);
|
|
22364
|
-
}
|
|
22365
|
-
for (const pack of seen) {
|
|
22366
|
-
if (!expected.includes(pack)) issue(issues, "unexpected_seat", `Roundtable emitted unseated pack ${pack}.`);
|
|
22367
|
-
}
|
|
22103
|
+
const parsed = parseConsultantCheck(raw, issues);
|
|
22368
22104
|
const pulls = new Set(options.counselPulls ?? []);
|
|
22369
22105
|
if (options.requireCounselPulls !== false) {
|
|
22370
|
-
|
|
22371
|
-
|
|
22372
|
-
|
|
22373
|
-
|
|
22374
|
-
|
|
22375
|
-
|
|
22376
|
-
}
|
|
22377
|
-
if (exec) {
|
|
22378
|
-
const allSupport = seats.length > 0 && seats.every((seat) => seat.stance === "support");
|
|
22379
|
-
if (allSupport && !exec.pre_mortem) {
|
|
22380
|
-
issue(issues, "missing_pre_mortem", "All-support roundtables require exec.pre_mortem.");
|
|
22106
|
+
if (!pulls.has("counsel_gtm")) {
|
|
22107
|
+
pushIssue(
|
|
22108
|
+
issues,
|
|
22109
|
+
"missing_counsel_pull",
|
|
22110
|
+
"Consultant check must call get_counsel_detail for counsel_gtm."
|
|
22111
|
+
);
|
|
22381
22112
|
}
|
|
22382
|
-
for (const
|
|
22383
|
-
if (
|
|
22384
|
-
|
|
22113
|
+
for (const pull of pulls) {
|
|
22114
|
+
if (pull !== "counsel_gtm") {
|
|
22115
|
+
pushIssue(
|
|
22385
22116
|
issues,
|
|
22386
|
-
"
|
|
22387
|
-
|
|
22117
|
+
"forbidden_counsel_pull",
|
|
22118
|
+
`Only counsel_gtm may be pulled (saw '${pull}').`
|
|
22388
22119
|
);
|
|
22389
22120
|
}
|
|
22390
|
-
if (seat.stance === "abstain" && !seat.cross_functional_risk) {
|
|
22391
|
-
issue(issues, "abstain_without_risk", `${seat.pack} abstained without a risk note.`);
|
|
22392
|
-
}
|
|
22393
22121
|
}
|
|
22394
22122
|
}
|
|
22395
|
-
|
|
22396
|
-
const
|
|
22397
|
-
|
|
22398
|
-
|
|
22399
|
-
|
|
22123
|
+
if (options.requireRetentionInSequence && parsed) {
|
|
22124
|
+
const blob = `${parsed.governing_constraint} ${parsed.sequence.join(" ")} ${parsed.verdict} ${parsed.drop}`.toLowerCase();
|
|
22125
|
+
const hasRetention = /nrr|grr|retention|renewal|churn|expansion/.test(blob);
|
|
22126
|
+
const hasNewLogo = /freshness|new[- ]?logo|acquisition|inbound|outbound/.test(blob);
|
|
22127
|
+
if (hasNewLogo && !hasRetention) {
|
|
22128
|
+
pushIssue(
|
|
22129
|
+
issues,
|
|
22130
|
+
"retention_dropped",
|
|
22131
|
+
"Dual-dollar check: new-logo/freshness present but retention/NRR/GRR absent from sequence.",
|
|
22132
|
+
"advisory"
|
|
22133
|
+
);
|
|
22400
22134
|
}
|
|
22401
22135
|
}
|
|
22402
22136
|
const blocking = issues.some((entry) => entry.severity === "blocking");
|
|
22403
22137
|
return {
|
|
22404
|
-
result:
|
|
22138
|
+
result: blocking ? null : parsed,
|
|
22405
22139
|
issues,
|
|
22406
22140
|
blocking
|
|
22407
22141
|
};
|
|
22408
22142
|
}
|
|
22409
|
-
function parseAndValidateRoundtable(text, options) {
|
|
22143
|
+
function parseAndValidateRoundtable(text, options = {}) {
|
|
22410
22144
|
return validateRoundtableResult(parseJsonObjectFromText(text), options);
|
|
22411
22145
|
}
|
|
22412
22146
|
function formatRoundtableDigestFromResult(result) {
|
|
22413
|
-
const lines =
|
|
22414
|
-
|
|
22415
|
-
|
|
22416
|
-
|
|
22417
|
-
if (result.
|
|
22418
|
-
|
|
22419
|
-
|
|
22420
|
-
if (result.
|
|
22147
|
+
const lines = [];
|
|
22148
|
+
if (result.governing_constraint) {
|
|
22149
|
+
lines.push(`Governing constraint: ${result.governing_constraint}`);
|
|
22150
|
+
}
|
|
22151
|
+
if (result.sequence.length) {
|
|
22152
|
+
lines.push(`Sequence: ${result.sequence.join(" then ")}`);
|
|
22153
|
+
}
|
|
22154
|
+
if (result.drop) {
|
|
22155
|
+
lines.push(`Drop: ${result.drop}`);
|
|
22156
|
+
}
|
|
22157
|
+
if (result.verdict) {
|
|
22158
|
+
lines.push(`Verdict: ${result.verdict}`);
|
|
22159
|
+
}
|
|
22160
|
+
if (result.pre_mortem) {
|
|
22161
|
+
lines.push(`Pre-mortem: ${result.pre_mortem}`);
|
|
22162
|
+
}
|
|
22421
22163
|
return lines.join("\n");
|
|
22422
22164
|
}
|
|
22423
|
-
function buildRoundtableRetryMessage(validation,
|
|
22424
|
-
const required = seating.seats.filter((seat) => seat.pack_id !== "counsel_exec").map((seat) => seat.pack_id).join(", ");
|
|
22165
|
+
function buildRoundtableRetryMessage(validation, _seating) {
|
|
22425
22166
|
const failures = validation.issues.filter((entry) => entry.severity === "blocking").map((entry) => `${entry.code}: ${entry.message}`).join("\n- ");
|
|
22426
|
-
return `
|
|
22427
|
-
- ${failures || "
|
|
22428
|
-
|
|
22429
|
-
|
|
22167
|
+
return `Plan check JSON did not validate.
|
|
22168
|
+
- ${failures || "consultant_shape: unusable JSON"}
|
|
22169
|
+
Respond with ONLY corrected flat JSON (no seats, no exec wrapper):
|
|
22170
|
+
{
|
|
22171
|
+
"governing_constraint": "...",
|
|
22172
|
+
"sequence": ["a", "b", "c"],
|
|
22173
|
+
"drop": "...",
|
|
22174
|
+
"verdict": "...",
|
|
22175
|
+
"pre_mortem": "..."
|
|
22176
|
+
}
|
|
22177
|
+
Call get_counsel_detail for counsel_gtm only. pre_mortem is required. Never print CRO/CMO/CS/sales/marketing/RevOps/Exec as speakers.`;
|
|
22430
22178
|
}
|
|
22431
22179
|
function normalizedPlanText(plan) {
|
|
22432
22180
|
return JSON.stringify(plan).toLowerCase();
|
|
@@ -22438,69 +22186,55 @@ function checkRoundtableReflection(input) {
|
|
|
22438
22186
|
const issues = [];
|
|
22439
22187
|
const draft = normalizedPlanText(input.draftPlan);
|
|
22440
22188
|
const final = normalizedPlanText(input.finalPlan);
|
|
22441
|
-
|
|
22442
|
-
const
|
|
22443
|
-
|
|
22444
|
-
|
|
22445
|
-
|
|
22446
|
-
"roundtable_drop_ignored",
|
|
22447
|
-
`Final plan still contains exec drop '${dropped}'.`,
|
|
22448
|
-
"advisory"
|
|
22449
|
-
);
|
|
22450
|
-
}
|
|
22451
|
-
}
|
|
22452
|
-
for (const seat of input.roundtable.seats) {
|
|
22453
|
-
if (seat.missing_exam) {
|
|
22454
|
-
const tokens = significantTokens(seat.missing_exam);
|
|
22455
|
-
if (tokens.length > 0 && !tokens.some((token) => final.includes(token))) {
|
|
22456
|
-
issue(
|
|
22189
|
+
if (input.roundtable.drop) {
|
|
22190
|
+
for (const dropped of input.roundtable.drop.split(/;|\n/).map((part) => part.trim()).filter(Boolean)) {
|
|
22191
|
+
const needle = dropped.toLowerCase();
|
|
22192
|
+
if (needle.length >= 4 && draft.includes(needle) && final.includes(needle)) {
|
|
22193
|
+
pushIssue(
|
|
22457
22194
|
issues,
|
|
22458
|
-
"
|
|
22459
|
-
|
|
22195
|
+
"consultant_drop_ignored",
|
|
22196
|
+
`Final plan still contains drop '${dropped}'.`,
|
|
22460
22197
|
"advisory"
|
|
22461
22198
|
);
|
|
22462
22199
|
}
|
|
22463
22200
|
}
|
|
22464
|
-
if (seat.stance === "challenge" && seat.challenge && draft === final) {
|
|
22465
|
-
issue(
|
|
22466
|
-
issues,
|
|
22467
|
-
"roundtable_unaddressed",
|
|
22468
|
-
`${seat.pack} challenged the draft but the final plan is unchanged.`,
|
|
22469
|
-
"advisory"
|
|
22470
|
-
);
|
|
22471
|
-
}
|
|
22472
22201
|
}
|
|
22473
|
-
|
|
22474
|
-
if (allSupport && input.roundtable.exec.pre_mortem) {
|
|
22202
|
+
if (input.roundtable.pre_mortem) {
|
|
22475
22203
|
const defensiveText = JSON.stringify({
|
|
22476
22204
|
risks: input.finalPlan.risks,
|
|
22477
22205
|
assumptions: input.finalPlan.assumptions,
|
|
22478
22206
|
contingencies: input.finalPlan.workstreams.map((ws) => ws.contingency)
|
|
22479
22207
|
}).toLowerCase();
|
|
22480
|
-
const tokens = significantTokens(input.roundtable.
|
|
22208
|
+
const tokens = significantTokens(input.roundtable.pre_mortem);
|
|
22481
22209
|
if (tokens.length > 0 && !tokens.some((token) => defensiveText.includes(token))) {
|
|
22482
|
-
|
|
22210
|
+
pushIssue(
|
|
22483
22211
|
issues,
|
|
22484
|
-
"
|
|
22485
|
-
"Final risks and contingencies do not reflect
|
|
22212
|
+
"consultant_premortem_missing",
|
|
22213
|
+
"Final risks and contingencies do not reflect pre_mortem.",
|
|
22486
22214
|
"advisory"
|
|
22487
22215
|
);
|
|
22488
22216
|
}
|
|
22489
22217
|
}
|
|
22490
22218
|
return { issues, blocking: false };
|
|
22491
22219
|
}
|
|
22492
|
-
var
|
|
22220
|
+
var BANNED_SPEAKER_PATTERNS;
|
|
22493
22221
|
var init_roundtable_validate = __esm({
|
|
22494
22222
|
"src/ai/roundtable-validate.ts"() {
|
|
22495
22223
|
"use strict";
|
|
22496
|
-
init_gtm_counsel();
|
|
22497
22224
|
init_strategist_validate();
|
|
22498
|
-
|
|
22499
|
-
|
|
22500
|
-
|
|
22501
|
-
|
|
22502
|
-
|
|
22503
|
-
|
|
22225
|
+
BANNED_SPEAKER_PATTERNS = [
|
|
22226
|
+
/\bCRO\s+says\b/i,
|
|
22227
|
+
/\bCMO\s+says\b/i,
|
|
22228
|
+
/\bCS\s+says\b/i,
|
|
22229
|
+
/\bsales\s+says\b/i,
|
|
22230
|
+
/\bfrom a marketing lens\b/i,
|
|
22231
|
+
/\bfrom a sales lens\b/i,
|
|
22232
|
+
/\bcounsel_sales\b/,
|
|
22233
|
+
/\bcounsel_marketing\b/,
|
|
22234
|
+
/\bcounsel_revops\b/,
|
|
22235
|
+
/\bcounsel_cs\b/,
|
|
22236
|
+
/\bcounsel_exec\b/
|
|
22237
|
+
];
|
|
22504
22238
|
}
|
|
22505
22239
|
});
|
|
22506
22240
|
|
|
@@ -23077,9 +22811,7 @@ ${STRATEGIST_PLAN_SCHEMA_BLOCK}`
|
|
|
23077
22811
|
const observeRoundtableTool = (name, args) => {
|
|
23078
22812
|
if (name !== "get_counsel_detail") return;
|
|
23079
22813
|
const counselId = typeof args.counsel_id === "string" ? args.counsel_id : "";
|
|
23080
|
-
if (
|
|
23081
|
-
counselId
|
|
23082
|
-
)) {
|
|
22814
|
+
if (counselId === "counsel_gtm") {
|
|
23083
22815
|
counselPulls.add(counselId);
|
|
23084
22816
|
}
|
|
23085
22817
|
};
|
|
@@ -23099,7 +22831,7 @@ ${STRATEGIST_PLAN_SCHEMA_BLOCK}`
|
|
|
23099
22831
|
const roundtableText = yield* runStage(
|
|
23100
22832
|
"strategist",
|
|
23101
22833
|
ROUNDTABLE_MAX_ROUNDS,
|
|
23102
|
-
"Tool budget reached for
|
|
22834
|
+
"Tool budget reached for the plan check. Respond with the plan-check JSON now \u2014 strict JSON only.",
|
|
23103
22835
|
true,
|
|
23104
22836
|
observeRoundtableTool
|
|
23105
22837
|
);
|
|
@@ -23117,7 +22849,7 @@ ${STRATEGIST_PLAN_SCHEMA_BLOCK}`
|
|
|
23117
22849
|
const retryText = yield* runStage(
|
|
23118
22850
|
"strategist",
|
|
23119
22851
|
2,
|
|
23120
|
-
"Respond with ONLY corrected
|
|
22852
|
+
"Respond with ONLY corrected plan-check JSON now. No prose.",
|
|
23121
22853
|
true,
|
|
23122
22854
|
observeRoundtableTool
|
|
23123
22855
|
);
|
|
@@ -23132,13 +22864,13 @@ ${STRATEGIST_PLAN_SCHEMA_BLOCK}`
|
|
|
23132
22864
|
const roundtableIssues = [...roundtableValidation.issues];
|
|
23133
22865
|
let roundtableDigest = roundtableResult ? formatRoundtableDigestFromResult(roundtableResult) : "";
|
|
23134
22866
|
if (!roundtableResult) {
|
|
23135
|
-
roundtableDigest = "
|
|
22867
|
+
roundtableDigest = "Plan check didn't land. Stress-testing still runs.";
|
|
23136
22868
|
yield {
|
|
23137
22869
|
type: "notice",
|
|
23138
|
-
text:
|
|
22870
|
+
text: "Plan check didn't land. Stress-testing still runs."
|
|
23139
22871
|
};
|
|
23140
22872
|
} else {
|
|
23141
|
-
yield { type: "notice", text: "
|
|
22873
|
+
yield { type: "notice", text: "Recommendation locked. Stress-testing next." };
|
|
23142
22874
|
}
|
|
23143
22875
|
yield { type: "stage", stage: "stress", label: STAGE_LABELS.stress };
|
|
23144
22876
|
messages.push({ role: "user", content: buildStressTestMessage(roundtableDigest) });
|
|
@@ -23176,8 +22908,8 @@ Respond with ONLY the corrected plan JSON object in the required schema (title,
|
|
|
23176
22908
|
text: "Strategist JSON failed validation \u2014 emitting grounded fallback plan from live vitals."
|
|
23177
22909
|
};
|
|
23178
22910
|
}
|
|
23179
|
-
for (const
|
|
23180
|
-
yield { type: "notice", text:
|
|
22911
|
+
for (const issue of validated.issues) {
|
|
22912
|
+
yield { type: "notice", text: issue };
|
|
23181
22913
|
}
|
|
23182
22914
|
if (roundtableResult && candidatePlan) {
|
|
23183
22915
|
const reflection = checkRoundtableReflection({
|
|
@@ -23187,10 +22919,6 @@ Respond with ONLY the corrected plan JSON object in the required schema (title,
|
|
|
23187
22919
|
});
|
|
23188
22920
|
for (const reflectionIssue of reflection.issues) {
|
|
23189
22921
|
roundtableIssues.push(reflectionIssue);
|
|
23190
|
-
yield {
|
|
23191
|
-
type: "notice",
|
|
23192
|
-
text: `Counsel reflection (${reflectionIssue.code}): ${reflectionIssue.message}`
|
|
23193
|
-
};
|
|
23194
22922
|
}
|
|
23195
22923
|
}
|
|
23196
22924
|
yield {
|
|
@@ -23299,7 +23027,7 @@ var init_strategist2 = __esm({
|
|
|
23299
23027
|
STAGE_LABELS = {
|
|
23300
23028
|
ground: "Grounding \u2014 reading health, metrics, segments, history",
|
|
23301
23029
|
backcast: "Sequencing \u2014 backcasting from objective",
|
|
23302
|
-
roundtable: "
|
|
23030
|
+
roundtable: "Checking the plan",
|
|
23303
23031
|
stress: "Stress-testing \u2014 capacity, measurability, timeline"
|
|
23304
23032
|
};
|
|
23305
23033
|
}
|
|
@@ -23331,6 +23059,10 @@ function recordScoreStreak(job, score) {
|
|
|
23331
23059
|
}
|
|
23332
23060
|
job.last_critic_score = score;
|
|
23333
23061
|
}
|
|
23062
|
+
function formatBlockingGapForUser(gap) {
|
|
23063
|
+
if (gap.code === "roundtable_invalid") return "Plan check didn't land.";
|
|
23064
|
+
return `Plan gap (${gap.code}): ${gap.note}`;
|
|
23065
|
+
}
|
|
23334
23066
|
async function* strategistCraftSession(options) {
|
|
23335
23067
|
assertReplAi(options.ctx);
|
|
23336
23068
|
const todayIso = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -23373,8 +23105,8 @@ async function* strategistCraftSession(options) {
|
|
|
23373
23105
|
const gap = {
|
|
23374
23106
|
code: "roundtable_invalid",
|
|
23375
23107
|
severity: "blocking",
|
|
23376
|
-
note: !job.roundtable ? "No validated
|
|
23377
|
-
fix: "Re-run
|
|
23108
|
+
note: !job.roundtable ? "No validated plan check is attached to this plan." : job.roundtable.counsel_fingerprint !== currentFingerprint ? "The latest plan check is stale for the current plan." : "The latest plan check failed structural validation.",
|
|
23109
|
+
fix: "Re-run the plan check until it lands a single consultant recommendation with a pre-mortem."
|
|
23378
23110
|
};
|
|
23379
23111
|
scored.gaps.push(gap);
|
|
23380
23112
|
scored.blocking.push(gap);
|
|
@@ -23452,7 +23184,7 @@ async function* strategistCraftSession(options) {
|
|
|
23452
23184
|
};
|
|
23453
23185
|
const rubric = scorePlan(locked);
|
|
23454
23186
|
job.rubric_gaps = rubric.gaps;
|
|
23455
|
-
yield { ...event, plan: locked, issues: [...event.issues, ...rubric.blocking.map(
|
|
23187
|
+
yield { ...event, plan: locked, issues: [...event.issues, ...rubric.blocking.map(formatBlockingGapForUser)] };
|
|
23456
23188
|
} else if (event.type === "done") {
|
|
23457
23189
|
lastMeta = event.usage ?? lastMeta;
|
|
23458
23190
|
job.cumulative_input_tokens += event.usage?.input_tokens ?? 0;
|
|
@@ -23522,7 +23254,7 @@ async function* strategistCraftSession(options) {
|
|
|
23522
23254
|
planJson: JSON.stringify(plan),
|
|
23523
23255
|
rubricGaps: [
|
|
23524
23256
|
rubric.blocking.map((g) => `${g.code}: ${g.note}`).join("\n") || "(none)",
|
|
23525
|
-
lastRoundtableDigest ? `
|
|
23257
|
+
lastRoundtableDigest ? `PLAN CHECK DIGEST (address or reject with instruments):
|
|
23526
23258
|
${lastRoundtableDigest}` : ""
|
|
23527
23259
|
].filter(Boolean).join("\n\n"),
|
|
23528
23260
|
healthSnapshot
|
|
@@ -23634,7 +23366,7 @@ ${lastRoundtableDigest}` : ""
|
|
|
23634
23366
|
if (nextFp !== lastCounselFp) {
|
|
23635
23367
|
yield {
|
|
23636
23368
|
type: "notice",
|
|
23637
|
-
text: "Plan
|
|
23369
|
+
text: "Plan shifted. Checking the plan again."
|
|
23638
23370
|
};
|
|
23639
23371
|
try {
|
|
23640
23372
|
const profile = loadProfile();
|
|
@@ -23712,7 +23444,7 @@ ${lastRoundtableDigest}` : ""
|
|
|
23712
23444
|
requireCounselPulls: false
|
|
23713
23445
|
});
|
|
23714
23446
|
const result = validation.blocking ? null : validation.result;
|
|
23715
|
-
lastRoundtableDigest = result ? formatRoundtableDigestFromResult(result) : "
|
|
23447
|
+
lastRoundtableDigest = result ? formatRoundtableDigestFromResult(result) : "Plan check didn't land.";
|
|
23716
23448
|
const record = {
|
|
23717
23449
|
seating,
|
|
23718
23450
|
result,
|
|
@@ -23728,7 +23460,7 @@ ${lastRoundtableDigest}` : ""
|
|
|
23728
23460
|
lastCounselFp = "";
|
|
23729
23461
|
yield {
|
|
23730
23462
|
type: "notice",
|
|
23731
|
-
text:
|
|
23463
|
+
text: "Plan check didn't land."
|
|
23732
23464
|
};
|
|
23733
23465
|
} else {
|
|
23734
23466
|
lastCounselFp = nextFp;
|
|
@@ -23755,7 +23487,7 @@ ${lastRoundtableDigest}` : ""
|
|
|
23755
23487
|
}
|
|
23756
23488
|
yield {
|
|
23757
23489
|
type: "notice",
|
|
23758
|
-
text:
|
|
23490
|
+
text: "Plan check didn't land."
|
|
23759
23491
|
};
|
|
23760
23492
|
}
|
|
23761
23493
|
}
|
|
@@ -23776,7 +23508,7 @@ ${lastRoundtableDigest}` : ""
|
|
|
23776
23508
|
yield {
|
|
23777
23509
|
type: "plan",
|
|
23778
23510
|
plan: job.plan,
|
|
23779
|
-
issues: rubric.blocking.map(
|
|
23511
|
+
issues: rubric.blocking.map(formatBlockingGapForUser),
|
|
23780
23512
|
measurable_targets: measurable.measurable_targets,
|
|
23781
23513
|
total_targets: measurable.total_targets,
|
|
23782
23514
|
baseline_batch_id: options.baselineBatchId ?? null
|
|
@@ -23873,12 +23605,6 @@ function renderCraftHandoffMarkdown(opts) {
|
|
|
23873
23605
|
lines.push(formatWorkstreamMarkdown(ws).trimEnd());
|
|
23874
23606
|
lines.push("");
|
|
23875
23607
|
}
|
|
23876
|
-
if (opts.roundtableDigest) {
|
|
23877
|
-
lines.push("## Counsel roundtable");
|
|
23878
|
-
lines.push("");
|
|
23879
|
-
lines.push(opts.roundtableDigest);
|
|
23880
|
-
lines.push("");
|
|
23881
|
-
}
|
|
23882
23608
|
const risks = filterOperatorLines(plan.risks);
|
|
23883
23609
|
if (risks.length > 0) {
|
|
23884
23610
|
lines.push("## Risks");
|
|
@@ -24922,8 +24648,8 @@ var init_ghost_hints = __esm({
|
|
|
24922
24648
|
PHASE_GHOST_HINTS = {
|
|
24923
24649
|
orient: [
|
|
24924
24650
|
'try "pipeline health"',
|
|
24925
|
-
"try /
|
|
24926
|
-
"try /
|
|
24651
|
+
"try /tour",
|
|
24652
|
+
"try /tour guide",
|
|
24927
24653
|
'try "is our retention real for the board?"',
|
|
24928
24654
|
'try "what is the most expensive problem to solve?"',
|
|
24929
24655
|
'try "board deck on Q3"'
|
|
@@ -24940,7 +24666,7 @@ var init_ghost_hints = __esm({
|
|
|
24940
24666
|
'try "how should we fix this?"',
|
|
24941
24667
|
'try "which segment is weakest?"',
|
|
24942
24668
|
'try "what is ARR?"',
|
|
24943
|
-
"try /
|
|
24669
|
+
"try /tour freshness",
|
|
24944
24670
|
'try "ship a board deck"'
|
|
24945
24671
|
]
|
|
24946
24672
|
};
|
|
@@ -24999,7 +24725,7 @@ function buildSituationalAwarenessBlock(ctx, opts = {}) {
|
|
|
24999
24725
|
"Already done: scope confirmed; compute ran; do not re-propose scope or re-run compute unless the user asks to restart"
|
|
25000
24726
|
);
|
|
25001
24727
|
lines.push(
|
|
25002
|
-
"Available now: answer from the artifact; suggest at most one of /strategy, /handoff, /
|
|
24728
|
+
"Available now: answer from the artifact; suggest at most one of /strategy, /handoff, /tour <metric>, /thinkwithme, /playbook"
|
|
25003
24729
|
);
|
|
25004
24730
|
if (channel === "explore" && responseMode === "deep") {
|
|
25005
24731
|
lines.push(
|
|
@@ -25662,7 +25388,7 @@ function tryKeylessDefinitionAnswer(ctx, input) {
|
|
|
25662
25388
|
}
|
|
25663
25389
|
console.log();
|
|
25664
25390
|
console.log(
|
|
25665
|
-
" " + chalk23.dim("More: ") + paint("accent", `/
|
|
25391
|
+
" " + chalk23.dim("More: ") + paint("accent", `/tour ${explainer.id}`) + chalk23.dim(" \xB7 full tour: ") + paint("accent", "/tour")
|
|
25666
25392
|
);
|
|
25667
25393
|
console.log();
|
|
25668
25394
|
recordMessage(ctx, "user", input);
|
|
@@ -25739,7 +25465,7 @@ async function handleExploreWithoutKey(ctx, input) {
|
|
|
25739
25465
|
console.log();
|
|
25740
25466
|
console.log(" " + chalk24.yellow("No key is connected. Q&A stays off until you type ") + paint("accent", "/connect") + chalk24.yellow("."));
|
|
25741
25467
|
console.log(" " + chalk24.dim("These commands work without a key:"));
|
|
25742
|
-
console.log(" " + paint("accent", "/
|
|
25468
|
+
console.log(" " + paint("accent", "/tour") + chalk24.dim(" slides for numbers and how to use NTRP"));
|
|
25743
25469
|
console.log(" " + paint("accent", "/playbook") + chalk24.dim(" recommended plays from your computed vitals"));
|
|
25744
25470
|
console.log(" " + chalk24.cyan('"how should we fix this?"') + chalk24.dim(" a simple plan. No AI."));
|
|
25745
25471
|
console.log(" " + paint("accent", "/handoff") + chalk24.dim(" write this analysis for another tool"));
|
|
@@ -25747,7 +25473,7 @@ async function handleExploreWithoutKey(ctx, input) {
|
|
|
25747
25473
|
recordMessage(
|
|
25748
25474
|
ctx,
|
|
25749
25475
|
"agent",
|
|
25750
|
-
"No key is connected. Offered paths without a key (/
|
|
25476
|
+
"No key is connected. Offered paths without a key (/tour, /playbook, simple plan, /handoff)."
|
|
25751
25477
|
);
|
|
25752
25478
|
}
|
|
25753
25479
|
var NO_KEY_NUDGES;
|
|
@@ -26395,6 +26121,7 @@ var init_smoke_protocol = __esm({
|
|
|
26395
26121
|
// src/cli/nl.ts
|
|
26396
26122
|
var nl_exports = {};
|
|
26397
26123
|
__export(nl_exports, {
|
|
26124
|
+
runExploreExpandAnswer: () => runExploreExpandAnswer,
|
|
26398
26125
|
runNaturalLanguage: () => runNaturalLanguage
|
|
26399
26126
|
});
|
|
26400
26127
|
import chalk25 from "chalk";
|
|
@@ -26402,16 +26129,16 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
26402
26129
|
if (isSmokeProtocolTrigger(input)) {
|
|
26403
26130
|
recordMessage(ctx, "user", input);
|
|
26404
26131
|
console.log();
|
|
26405
|
-
const
|
|
26132
|
+
const spinner = makeSpinner("Running smoke protocol\u2026");
|
|
26406
26133
|
try {
|
|
26407
26134
|
const result = await runSmokeProtocol(input, ctx);
|
|
26408
|
-
|
|
26135
|
+
spinner.succeed("Smoke protocol complete");
|
|
26409
26136
|
printAnswer(result.answer);
|
|
26410
26137
|
recordMessage(ctx, "agent", result.answer);
|
|
26411
26138
|
console.log();
|
|
26412
26139
|
return extractSummary2(result.answer);
|
|
26413
26140
|
} catch (err) {
|
|
26414
|
-
|
|
26141
|
+
spinner.fail("Smoke protocol failed");
|
|
26415
26142
|
console.error(" " + chalk25.red(String(err.message ?? err)));
|
|
26416
26143
|
console.log();
|
|
26417
26144
|
return;
|
|
@@ -26426,6 +26153,22 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
26426
26153
|
return;
|
|
26427
26154
|
}
|
|
26428
26155
|
recordMessage(ctx, "user", input);
|
|
26156
|
+
const responseMode = resolveExploreResponseMode(input, ctx, ctx.conversation.length);
|
|
26157
|
+
return runExploreAgent(ctx, {
|
|
26158
|
+
userQuestion: input,
|
|
26159
|
+
responseMode,
|
|
26160
|
+
showExpandHint: responseMode === "brief"
|
|
26161
|
+
});
|
|
26162
|
+
}
|
|
26163
|
+
async function runExploreExpandAnswer(ctx, question) {
|
|
26164
|
+
return runExploreAgent(ctx, {
|
|
26165
|
+
userQuestion: question,
|
|
26166
|
+
responseMode: "deep",
|
|
26167
|
+
showExpandHint: false
|
|
26168
|
+
});
|
|
26169
|
+
}
|
|
26170
|
+
async function runExploreAgent(ctx, opts) {
|
|
26171
|
+
const { userQuestion, responseMode, showExpandHint } = opts;
|
|
26429
26172
|
let snapshot = ctx.snapshot.computeResult;
|
|
26430
26173
|
if (!snapshot) {
|
|
26431
26174
|
const metricsFirst = prefersMetricsFirstContext(ctx);
|
|
@@ -26442,7 +26185,7 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
26442
26185
|
}
|
|
26443
26186
|
}
|
|
26444
26187
|
console.log();
|
|
26445
|
-
const memoryBlock = await buildMemoryBlock(
|
|
26188
|
+
const memoryBlock = await buildMemoryBlock(userQuestion).catch((err) => {
|
|
26446
26189
|
debugError("nl.buildMemoryBlock", err);
|
|
26447
26190
|
return "";
|
|
26448
26191
|
});
|
|
@@ -26453,13 +26196,12 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
26453
26196
|
setAgentContext(ctx);
|
|
26454
26197
|
try {
|
|
26455
26198
|
const analysisBlock = buildAnalysisBlock(ctx);
|
|
26456
|
-
const responseMode = resolveExploreResponseMode(input, ctx, ctx.conversation.length);
|
|
26457
26199
|
const conversationBlock = getConversationPhaseBlock(ctx, { responseMode });
|
|
26458
26200
|
const bundle = await loadSessionAnalysisBundle();
|
|
26459
26201
|
const sessionArtifact = hasAnyAnalysis(bundle) ? buildExploreContextBlock(bundle, ctx) : "";
|
|
26460
26202
|
for await (const event of agenticFindings(snapshot, ctx.snapshot.divergences, {
|
|
26461
26203
|
mode: "fresh",
|
|
26462
|
-
userQuestion
|
|
26204
|
+
userQuestion,
|
|
26463
26205
|
sessionContext: ctx.resumedSessionSummary,
|
|
26464
26206
|
includeMetrics: true,
|
|
26465
26207
|
analysisBlock,
|
|
@@ -26500,7 +26242,7 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
26500
26242
|
const { isLlmAuthError: isLlmAuthError2 } = await Promise.resolve().then(() => (init_errors2(), errors_exports));
|
|
26501
26243
|
if (isLlmAuthError2(err)) {
|
|
26502
26244
|
const { queuePendingAsk: queuePendingAsk2 } = await Promise.resolve().then(() => (init_pending_ask(), pending_ask_exports));
|
|
26503
|
-
if (!ctx.pendingAsk) queuePendingAsk2(ctx,
|
|
26245
|
+
if (!ctx.pendingAsk) queuePendingAsk2(ctx, userQuestion, "explore");
|
|
26504
26246
|
const { recoverFromAuthFailure: recoverFromAuthFailure2 } = await Promise.resolve().then(() => (init_auth_recovery(), auth_recovery_exports));
|
|
26505
26247
|
await recoverFromAuthFailure2(ctx, err);
|
|
26506
26248
|
return;
|
|
@@ -26525,8 +26267,18 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
26525
26267
|
saveSessionState(ctx);
|
|
26526
26268
|
}
|
|
26527
26269
|
creditNlAnswer(ctx, Math.floor(ctx.messages.length / 2));
|
|
26528
|
-
recordAnalysis({
|
|
26529
|
-
|
|
26270
|
+
recordAnalysis({
|
|
26271
|
+
question: userQuestion,
|
|
26272
|
+
answer: lastAnswer,
|
|
26273
|
+
tools: toolsUsed,
|
|
26274
|
+
session_id: ctx.sessionId
|
|
26275
|
+
});
|
|
26276
|
+
ctx.lastExchange = { question: userQuestion, answer: lastAnswer, mode: responseMode };
|
|
26277
|
+
if (showExpandHint) {
|
|
26278
|
+
console.log(
|
|
26279
|
+
" " + chalk25.dim("Type ") + chalk25.cyan("expand") + chalk25.dim(" for more reasoning.")
|
|
26280
|
+
);
|
|
26281
|
+
}
|
|
26530
26282
|
}
|
|
26531
26283
|
console.log();
|
|
26532
26284
|
const { promptQueuedAiStrategist: promptQueuedAiStrategist2 } = await Promise.resolve().then(() => (init_strategist_flow(), strategist_flow_exports));
|
|
@@ -26553,7 +26305,7 @@ function printFindingInline2(finding) {
|
|
|
26553
26305
|
if (play) console.log(" " + chalk25.dim("\u2192 " + play.play_name + " \u2014 " + play.rationale));
|
|
26554
26306
|
if (finding.recommended_focus) {
|
|
26555
26307
|
console.log(
|
|
26556
|
-
" " + chalk25.dim("How this works: ") + paint("accent", `/
|
|
26308
|
+
" " + chalk25.dim("How this works: ") + paint("accent", `/tour ${finding.recommended_focus}`)
|
|
26557
26309
|
);
|
|
26558
26310
|
}
|
|
26559
26311
|
}
|
|
@@ -31177,8 +30929,8 @@ var init_diagnose = __esm({
|
|
|
31177
30929
|
function summarizeEntityDetails(details = []) {
|
|
31178
30930
|
const summary = {};
|
|
31179
30931
|
for (const detail of details) {
|
|
31180
|
-
const
|
|
31181
|
-
summary[
|
|
30932
|
+
const issue = typeof detail.issue === "string" ? detail.issue : "unclassified";
|
|
30933
|
+
summary[issue] = (summary[issue] ?? 0) + 1;
|
|
31182
30934
|
}
|
|
31183
30935
|
return summary;
|
|
31184
30936
|
}
|
|
@@ -31658,8 +31410,8 @@ async function handleGetVitalSignDetail(input, ctx) {
|
|
|
31658
31410
|
if (!vital) return { error: `Vital sign '${vitalSign}' not found` };
|
|
31659
31411
|
const entitySummary = {};
|
|
31660
31412
|
for (const detail of vital.entity_details) {
|
|
31661
|
-
const
|
|
31662
|
-
const key =
|
|
31413
|
+
const issue = detail.issue;
|
|
31414
|
+
const key = issue ?? "unclassified";
|
|
31663
31415
|
entitySummary[key] = (entitySummary[key] ?? 0) + 1;
|
|
31664
31416
|
}
|
|
31665
31417
|
return {
|
|
@@ -31974,27 +31726,6 @@ async function handleGetCounselDetail(input) {
|
|
|
31974
31726
|
}
|
|
31975
31727
|
return formatCounselDetail2(pack, getActiveOrgTree2());
|
|
31976
31728
|
}
|
|
31977
|
-
async function handleGetRoleCard(input) {
|
|
31978
|
-
const roleId = typeof input.role_id === "string" ? input.role_id.trim() : "";
|
|
31979
|
-
const {
|
|
31980
|
-
getRoleCardById: getRoleCardById2,
|
|
31981
|
-
listRoleCardIds: listRoleCardIds2,
|
|
31982
|
-
formatRoleCardDetail: formatRoleCardDetail2,
|
|
31983
|
-
getActiveOrgTree: getActiveOrgTree2,
|
|
31984
|
-
listOrgTreeIds: listOrgTreeIds2
|
|
31985
|
-
} = await Promise.resolve().then(() => (init_gtm_counsel(), gtm_counsel_exports));
|
|
31986
|
-
const card = roleId ? getRoleCardById2(roleId) : void 0;
|
|
31987
|
-
if (!card) {
|
|
31988
|
-
return {
|
|
31989
|
-
error: `Unknown role id '${roleId}'.`,
|
|
31990
|
-
valid_role_ids: listRoleCardIds2()
|
|
31991
|
-
};
|
|
31992
|
-
}
|
|
31993
|
-
const treeArg = typeof input.org_tree === "string" ? input.org_tree.trim() : "";
|
|
31994
|
-
const trees = listOrgTreeIds2();
|
|
31995
|
-
const tree = trees.includes(treeArg) ? treeArg : getActiveOrgTree2();
|
|
31996
|
-
return formatRoleCardDetail2(card, tree);
|
|
31997
|
-
}
|
|
31998
31729
|
async function handleGetRevenueMetrics(_input, ctx) {
|
|
31999
31730
|
if (!ctx.metrics || ctx.metrics.length === 0) {
|
|
32000
31731
|
const { computeFullMetrics: computeFullMetrics2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
|
|
@@ -32291,7 +32022,6 @@ var init_tool_handlers = __esm({
|
|
|
32291
32022
|
get_play_detail: (input, _) => handleGetPlayDetail(input),
|
|
32292
32023
|
get_framework_detail: (input, _) => handleGetFrameworkDetail(input),
|
|
32293
32024
|
get_counsel_detail: (input, _) => handleGetCounselDetail(input),
|
|
32294
|
-
get_role_card: (input, _) => handleGetRoleCard(input),
|
|
32295
32025
|
get_session_brief: (input, _) => handleGetSessionBrief(input),
|
|
32296
32026
|
get_revenue_metrics: handleGetRevenueMetrics,
|
|
32297
32027
|
get_revenue_metrics_timeseries: (input, _) => handleGetRevenueMetricsTimeseries(input),
|