snipara-companion 3.2.31 → 3.2.32

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/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  Release notes for `snipara-companion`, newest first.
4
4
 
5
+ ## New In 3.2.32
6
+
7
+ - Promotes a Hosted MCP `servedJudgmentId` into the first-class Project
8
+ Intelligence brief so `run` can persist Advisor Influence receipts without a
9
+ fragile nested lookup or manual flag when the hosted brief already supplies
10
+ the identity.
11
+ - Adds an explicit Advisor measurement funnel to JSON and text output:
12
+ identity linked/missing, targeted versus unscoped, acknowledged, applied,
13
+ verified, blocked, unmeasured, and receipt coverage.
14
+ - Stores a bounded measurement state with each first-party receipt so a runtime
15
+ acknowledgement is never presented as recommendation-scoped application.
16
+ - Emits one stable `project-intelligence.judgment-run-envelope.v1` per `run`
17
+ invocation. It reuses a bounded Snipara or Codex session id when available,
18
+ otherwise generates an opaque run id, and attaches the same envelope to every
19
+ first-party Advisor receipt.
20
+
5
21
  ## New In 3.2.31
6
22
 
7
23
  - Sends phase commits, final commits, and Team Sync handoffs to project-scoped
package/dist/index.d.ts CHANGED
@@ -2086,6 +2086,7 @@ interface ProjectIntelligenceBriefOptions {
2086
2086
  interface ProjectIntelligenceBrief {
2087
2087
  version: "project-intelligence-brief-v1";
2088
2088
  generatedAt: string;
2089
+ servedJudgmentId?: string;
2089
2090
  branch?: string;
2090
2091
  task?: string;
2091
2092
  changedFiles: string[];
@@ -2616,10 +2617,27 @@ interface ProjectRunAdvisorReceiptWrite {
2616
2617
  agentDecision?: AdvisorInfluenceAgentDecision;
2617
2618
  changedBecauseOfRecommendation?: boolean;
2618
2619
  lifecycleState?: AdvisorInfluenceLifecycleState;
2620
+ measurementState?: ProjectRunAdvisorMeasurementState;
2621
+ targeted?: boolean;
2619
2622
  result?: RecordAdvisorInfluenceReceiptResult;
2620
2623
  reason?: ProjectRunAdvisorReceiptSkipReason;
2621
2624
  error?: string;
2622
2625
  }
2626
+ type ProjectRunAdvisorMeasurementState = "unmeasured" | "acknowledged_unscoped" | "acknowledged" | "applied" | "verified" | "blocked";
2627
+ interface ProjectRunAdvisorMeasurementCoverage {
2628
+ version: "project-intelligence.advisor-measurement-coverage.v1";
2629
+ identityStatus: "linked" | "missing";
2630
+ recommendationCount: number;
2631
+ recordedCount: number;
2632
+ targetedCount: number;
2633
+ unscopedCount: number;
2634
+ acknowledgedCount: number;
2635
+ appliedCount: number;
2636
+ verifiedCount: number;
2637
+ blockedCount: number;
2638
+ unmeasuredCount: number;
2639
+ receiptCoverage: number | null;
2640
+ }
2623
2641
  type ProjectRunAdvisorReceiptSkipReason = "explicitly_skipped" | "no_advisor_recommendations" | "missing_served_judgment_id" | "no_plan_adaptation" | "write_limit_exceeded";
2624
2642
  interface ProjectRunAdvisorReceiptCapture {
2625
2643
  status: "skipped" | "recorded" | "partial" | "error";
@@ -2630,11 +2648,13 @@ interface ProjectRunAdvisorReceiptCapture {
2630
2648
  recordedCount: number;
2631
2649
  skippedCount: number;
2632
2650
  writes: ProjectRunAdvisorReceiptWrite[];
2651
+ measurement: ProjectRunAdvisorMeasurementCoverage;
2633
2652
  reason?: ProjectRunAdvisorReceiptSkipReason;
2634
2653
  }
2635
2654
  interface ProjectIntelligenceRunResult {
2636
2655
  version: "project-intelligence.production-run.v1";
2637
2656
  generatedAt: string;
2657
+ runEnvelope: ProjectIntelligenceRunEnvelope;
2638
2658
  release: boolean;
2639
2659
  brief: ProjectIntelligenceBrief;
2640
2660
  guard?: ProjectRunGuardResult;
@@ -2646,6 +2666,12 @@ interface ProjectIntelligenceRunResult {
2646
2666
  judgmentCard: ProjectIntelligenceJudgmentCard;
2647
2667
  suggestedCommands: string[];
2648
2668
  }
2669
+ interface ProjectIntelligenceRunEnvelope {
2670
+ version: "project-intelligence.judgment-run-envelope.v1";
2671
+ runId: string;
2672
+ identitySource: "snipara_session" | "codex_session" | "generated";
2673
+ startedAt: string;
2674
+ }
2649
2675
  interface ProjectRunPolicyDecisionRequests {
2650
2676
  version: "project-intelligence.policy-decision-requests.v1";
2651
2677
  emitted: boolean;
package/dist/index.js CHANGED
@@ -32860,6 +32860,32 @@ async function verifyCommand(options) {
32860
32860
  }
32861
32861
 
32862
32862
  // src/commands/intelligence.ts
32863
+ function servedJudgmentIdFromContext(value, depth = 0) {
32864
+ if (depth > 5 || value === null || value === void 0) return void 0;
32865
+ if (Array.isArray(value)) {
32866
+ for (const item of value.slice(0, 12)) {
32867
+ const found = servedJudgmentIdFromContext(item, depth + 1);
32868
+ if (found) return found;
32869
+ }
32870
+ return void 0;
32871
+ }
32872
+ if (!isRecord16(value)) return void 0;
32873
+ const direct = value.servedJudgmentId ?? value.served_judgment_id;
32874
+ if (typeof direct === "string" && direct.trim()) return direct.trim();
32875
+ for (const key of [
32876
+ "projectIntelligence",
32877
+ "project_intelligence",
32878
+ "brief",
32879
+ "judgment",
32880
+ "resumeContext",
32881
+ "resume_context",
32882
+ "data"
32883
+ ]) {
32884
+ const found = servedJudgmentIdFromContext(value[key], depth + 1);
32885
+ if (found) return found;
32886
+ }
32887
+ return void 0;
32888
+ }
32863
32889
  function isRecord16(value) {
32864
32890
  return typeof value === "object" && value !== null && !Array.isArray(value);
32865
32891
  }
@@ -33082,6 +33108,8 @@ async function buildProjectIntelligenceBrief(options) {
33082
33108
  ...changedFiles.length > 0 ? { changedFiles } : {},
33083
33109
  max_tokens: options.maxTokens ?? 4e3
33084
33110
  });
33111
+ const servedJudgmentId = servedJudgmentIdFromContext(brief.resumeContext);
33112
+ if (servedJudgmentId) brief.servedJudgmentId = servedJudgmentId;
33085
33113
  } catch (error) {
33086
33114
  errors.push({
33087
33115
  surface: "resume_context",
@@ -34686,6 +34714,7 @@ async function referencesIngestCommand(options) {
34686
34714
 
34687
34715
  // src/commands/run.ts
34688
34716
  var import_node_child_process15 = require("child_process");
34717
+ var import_node_crypto10 = require("crypto");
34689
34718
  var fs33 = __toESM(require("fs"));
34690
34719
  var path33 = __toESM(require("path"));
34691
34720
  var import_chalk14 = __toESM(require("chalk"));
@@ -35116,6 +35145,16 @@ function formatPolicyGateDecision(gateDecision) {
35116
35145
  var GUARD_MAX_BUFFER_BYTES = 16 * 1024 * 1024;
35117
35146
  var RAW_OUTPUT_PREVIEW_BYTES = 64e3;
35118
35147
  var ADVISOR_RECEIPT_WRITE_LIMIT = 6;
35148
+ function buildProjectIntelligenceRunEnvelope(startedAt = /* @__PURE__ */ new Date()) {
35149
+ const sniparaSessionId = stringValue14(process.env.SNIPARA_SESSION_ID)?.slice(0, 200);
35150
+ const codexSessionId = stringValue14(process.env.CODEX_SESSION_ID)?.slice(0, 200);
35151
+ return {
35152
+ version: "project-intelligence.judgment-run-envelope.v1",
35153
+ runId: sniparaSessionId ?? codexSessionId ?? `judgment-run:${(0, import_node_crypto10.randomUUID)()}`,
35154
+ identitySource: sniparaSessionId ? "snipara_session" : codexSessionId ? "codex_session" : "generated",
35155
+ startedAt: startedAt.toISOString()
35156
+ };
35157
+ }
35119
35158
  function normalizeStringList6(values) {
35120
35159
  return [...new Set((values ?? []).map((value) => value.trim()).filter(Boolean))];
35121
35160
  }
@@ -35171,39 +35210,32 @@ function outputPreview(value) {
35171
35210
  function stringValue14(value) {
35172
35211
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
35173
35212
  }
35174
- function servedJudgmentIdFromUnknown(value, depth = 0) {
35175
- if (depth > 5 || value === null || value === void 0) {
35176
- return void 0;
35177
- }
35178
- if (!isRecord18(value)) {
35179
- if (Array.isArray(value)) {
35180
- for (const item of value.slice(0, 12)) {
35181
- const found = servedJudgmentIdFromUnknown(item, depth + 1);
35182
- if (found) return found;
35183
- }
35184
- }
35185
- return void 0;
35186
- }
35187
- const direct = stringValue14(value.servedJudgmentId) ?? stringValue14(value.served_judgment_id);
35188
- if (direct) {
35189
- return direct;
35190
- }
35191
- for (const key of [
35192
- "projectIntelligence",
35193
- "project_intelligence",
35194
- "brief",
35195
- "judgment",
35196
- "resumeContext",
35197
- "resume_context",
35198
- "data"
35199
- ]) {
35200
- const found = servedJudgmentIdFromUnknown(value[key], depth + 1);
35201
- if (found) return found;
35202
- }
35203
- return void 0;
35204
- }
35205
35213
  function servedJudgmentIdForRun(options, brief) {
35206
- return stringValue14(options.servedJudgmentId) ?? servedJudgmentIdFromUnknown(brief.resumeContext) ?? servedJudgmentIdFromUnknown(brief.verificationPlan);
35214
+ return stringValue14(options.servedJudgmentId) ?? stringValue14(brief.servedJudgmentId) ?? servedJudgmentIdFromContext(brief.resumeContext) ?? servedJudgmentIdFromContext(brief.verificationPlan);
35215
+ }
35216
+ function advisorMeasurementState(args) {
35217
+ if (args.agentDecision === "blocked") return "blocked";
35218
+ if (args.lifecycle.state === "verified") return "verified";
35219
+ if (args.lifecycle.state === "applied") return "applied";
35220
+ return args.planScope.targeted ? "acknowledged" : "acknowledged_unscoped";
35221
+ }
35222
+ function advisorMeasurementCoverage(args) {
35223
+ const states = args.writes.map((write) => write.measurementState ?? "unmeasured");
35224
+ const recordedCount = args.writes.filter((write) => write.status === "recorded").length;
35225
+ return {
35226
+ version: "project-intelligence.advisor-measurement-coverage.v1",
35227
+ identityStatus: args.servedJudgmentId ? "linked" : "missing",
35228
+ recommendationCount: args.recommendationCount,
35229
+ recordedCount,
35230
+ targetedCount: args.writes.filter((write) => write.targeted === true).length,
35231
+ unscopedCount: states.filter((state) => state === "acknowledged_unscoped").length,
35232
+ acknowledgedCount: states.filter((state) => state === "acknowledged").length,
35233
+ appliedCount: states.filter((state) => state === "applied").length,
35234
+ verifiedCount: states.filter((state) => state === "verified").length,
35235
+ blockedCount: states.filter((state) => state === "blocked").length,
35236
+ unmeasuredCount: Math.max(0, args.recommendationCount - recordedCount),
35237
+ receiptCoverage: args.recommendationCount > 0 ? recordedCount / args.recommendationCount : null
35238
+ };
35207
35239
  }
35208
35240
  function normalizeAdvisorSource(value) {
35209
35241
  if (value === "judgment" || value === "outcome_calibration" || value === "historical_impact" || value === "safety" || value === "verification" || value === "context_quality") {
@@ -35351,7 +35383,8 @@ function advisorReceiptMetadata(args) {
35351
35383
  source: "snipara-companion:run",
35352
35384
  firstParty: true,
35353
35385
  runVersion: "project-intelligence.production-run.v1",
35354
- runId: process.env.SNIPARA_SESSION_ID ?? process.env.CODEX_SESSION_ID ?? null,
35386
+ runId: args.runEnvelope.runId,
35387
+ runEnvelope: args.runEnvelope,
35355
35388
  generatedAt: args.judgmentCard.generatedAt,
35356
35389
  release: Boolean(args.options.release),
35357
35390
  task: args.brief.task ?? args.options.task ?? null,
@@ -35363,6 +35396,12 @@ function advisorReceiptMetadata(args) {
35363
35396
  changedBecauseOfRecommendation: args.lifecycle.planChange.changed,
35364
35397
  advisorInfluenceLifecycle: args.lifecycle,
35365
35398
  advisorPlanScope: args.planScope,
35399
+ advisorMeasurement: {
35400
+ version: "project-intelligence.advisor-measurement.v1",
35401
+ state: args.measurementState,
35402
+ targeted: args.planScope.targeted,
35403
+ identityLinked: true
35404
+ },
35366
35405
  toolActions,
35367
35406
  humanOverride: null,
35368
35407
  judgmentState: args.judgmentCard.state,
@@ -35504,7 +35543,9 @@ function emitPolicyDecisionRequests(run) {
35504
35543
  }
35505
35544
  async function recordFirstPartyAdvisorReceipts(args) {
35506
35545
  const allRecommendations = args.judgmentCard.advisorRecommendations;
35546
+ const servedJudgmentId = servedJudgmentIdForRun(args.options, args.brief);
35507
35547
  if (args.options.skipAdvisorReceipts) {
35548
+ const writes2 = [];
35508
35549
  return {
35509
35550
  status: "skipped",
35510
35551
  totalRecommendationCount: allRecommendations.length,
@@ -35512,11 +35553,17 @@ async function recordFirstPartyAdvisorReceipts(args) {
35512
35553
  attemptedCount: 0,
35513
35554
  recordedCount: 0,
35514
35555
  skippedCount: allRecommendations.length,
35515
- writes: [],
35556
+ writes: writes2,
35557
+ measurement: advisorMeasurementCoverage({
35558
+ servedJudgmentId,
35559
+ recommendationCount: allRecommendations.length,
35560
+ writes: writes2
35561
+ }),
35516
35562
  reason: "explicitly_skipped"
35517
35563
  };
35518
35564
  }
35519
35565
  if (allRecommendations.length === 0) {
35566
+ const writes2 = [];
35520
35567
  return {
35521
35568
  status: "skipped",
35522
35569
  totalRecommendationCount: 0,
@@ -35524,11 +35571,11 @@ async function recordFirstPartyAdvisorReceipts(args) {
35524
35571
  attemptedCount: 0,
35525
35572
  recordedCount: 0,
35526
35573
  skippedCount: 0,
35527
- writes: [],
35574
+ writes: writes2,
35575
+ measurement: advisorMeasurementCoverage({ servedJudgmentId, recommendationCount: 0, writes: writes2 }),
35528
35576
  reason: "no_advisor_recommendations"
35529
35577
  };
35530
35578
  }
35531
- const servedJudgmentId = servedJudgmentIdForRun(args.options, args.brief);
35532
35579
  if (!servedJudgmentId) {
35533
35580
  const writes2 = allRecommendations.slice(0, ADVISOR_RECEIPT_WRITE_LIMIT).map(
35534
35581
  (recommendation) => skippedAdvisorReceiptWrite(recommendation, "missing_served_judgment_id")
@@ -35541,6 +35588,10 @@ async function recordFirstPartyAdvisorReceipts(args) {
35541
35588
  recordedCount: 0,
35542
35589
  skippedCount: writes2.length,
35543
35590
  writes: writes2,
35591
+ measurement: advisorMeasurementCoverage({
35592
+ recommendationCount: allRecommendations.length,
35593
+ writes: writes2
35594
+ }),
35544
35595
  reason: "missing_served_judgment_id"
35545
35596
  };
35546
35597
  }
@@ -35576,6 +35627,11 @@ async function recordFirstPartyAdvisorReceipts(args) {
35576
35627
  );
35577
35628
  const changedBecauseOfRecommendation = lifecycle.planChange.changed;
35578
35629
  const verificationExecuted = verificationExecutedFromLifecycle(lifecycle);
35630
+ const measurementState = advisorMeasurementState({
35631
+ agentDecision,
35632
+ lifecycle,
35633
+ planScope
35634
+ });
35579
35635
  try {
35580
35636
  const behaviorChange = advisorReceiptBehaviorChange({
35581
35637
  recommendation,
@@ -35600,7 +35656,9 @@ async function recordFirstPartyAdvisorReceipts(args) {
35600
35656
  totalRecommendations: allRecommendations.length,
35601
35657
  verificationEvidence,
35602
35658
  lifecycle,
35603
- planScope
35659
+ planScope,
35660
+ measurementState,
35661
+ runEnvelope: args.runEnvelope
35604
35662
  })
35605
35663
  });
35606
35664
  return {
@@ -35609,6 +35667,8 @@ async function recordFirstPartyAdvisorReceipts(args) {
35609
35667
  agentDecision,
35610
35668
  changedBecauseOfRecommendation,
35611
35669
  lifecycleState: lifecycle.state,
35670
+ measurementState,
35671
+ targeted: planScope.targeted,
35612
35672
  result
35613
35673
  };
35614
35674
  } catch (error) {
@@ -35618,6 +35678,8 @@ async function recordFirstPartyAdvisorReceipts(args) {
35618
35678
  agentDecision,
35619
35679
  changedBecauseOfRecommendation,
35620
35680
  lifecycleState: lifecycle.state,
35681
+ measurementState: "unmeasured",
35682
+ targeted: planScope.targeted,
35621
35683
  error: error instanceof Error ? error.message : String(error)
35622
35684
  };
35623
35685
  }
@@ -35643,7 +35705,12 @@ async function recordFirstPartyAdvisorReceipts(args) {
35643
35705
  attemptedCount: eligibleCount,
35644
35706
  recordedCount,
35645
35707
  skippedCount,
35646
- writes
35708
+ writes,
35709
+ measurement: advisorMeasurementCoverage({
35710
+ servedJudgmentId,
35711
+ recommendationCount: allRecommendations.length,
35712
+ writes
35713
+ })
35647
35714
  };
35648
35715
  }
35649
35716
  function runGuard(options, changedFiles) {
@@ -35721,6 +35788,7 @@ function runPackageReview(options) {
35721
35788
  }
35722
35789
  }
35723
35790
  async function buildProjectIntelligenceRun(options) {
35791
+ const runEnvelope = buildProjectIntelligenceRunEnvelope();
35724
35792
  const changedFiles = normalizeStringList6(options.changedFiles);
35725
35793
  const brief = await buildProjectIntelligenceBrief({
35726
35794
  task: options.task,
@@ -35803,7 +35871,8 @@ async function buildProjectIntelligenceRun(options) {
35803
35871
  brief,
35804
35872
  judgmentCard,
35805
35873
  verificationEvidence,
35806
- outcomeReceipts
35874
+ outcomeReceipts,
35875
+ runEnvelope
35807
35876
  });
35808
35877
  const suggestedCommands3 = [
35809
35878
  ...brief.suggestedCommands,
@@ -35815,7 +35884,8 @@ async function buildProjectIntelligenceRun(options) {
35815
35884
  ];
35816
35885
  const result = {
35817
35886
  version: "project-intelligence.production-run.v1",
35818
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
35887
+ generatedAt: runEnvelope.startedAt,
35888
+ runEnvelope,
35819
35889
  release: Boolean(options.release),
35820
35890
  brief,
35821
35891
  ...guard ? { guard } : {},
@@ -35841,6 +35911,7 @@ async function projectIntelligenceRunCommand(options) {
35841
35911
  console.log(`Task: ${result.brief.task}`);
35842
35912
  }
35843
35913
  console.log(`Release: ${result.release ? "yes" : "no"}`);
35914
+ console.log(`Run: ${result.runEnvelope.runId} (${result.runEnvelope.identitySource})`);
35844
35915
  console.log("");
35845
35916
  console.log(import_chalk14.default.bold("Project Judgment"));
35846
35917
  for (const line of formatProjectJudgmentCard(result.judgmentCard)) {
@@ -35888,6 +35959,10 @@ async function projectIntelligenceRunCommand(options) {
35888
35959
  console.log(
35889
35960
  `Recorded: ${result.advisorReceiptCapture.recordedCount}/${result.advisorReceiptCapture.attemptedCount}`
35890
35961
  );
35962
+ const measurement = result.advisorReceiptCapture.measurement;
35963
+ console.log(
35964
+ `Measurement: ${measurement.identityStatus}; targeted ${measurement.targetedCount}, unscoped ${measurement.unscopedCount}, applied ${measurement.appliedCount}, verified ${measurement.verifiedCount}, unmeasured ${measurement.unmeasuredCount}`
35965
+ );
35891
35966
  if (result.advisorReceiptCapture.skippedCount > 0) {
35892
35967
  console.log(`Skipped: ${result.advisorReceiptCapture.skippedCount}`);
35893
35968
  }
@@ -1240,8 +1240,11 @@ snipara-companion run \
1240
1240
  package-surface review, verification plan, and final Judgment Card. Review-only
1241
1241
  guard findings can be acknowledged with the printed guard action card command;
1242
1242
  blocking conflicts still make the release judgment non-proceedable.
1243
- When a served judgment id is available, add `--served-judgment-id` so `run` can
1244
- write first-party Advisor Influence receipts. Receipts now follow an explicit
1243
+ When Hosted MCP returns a served judgment id, Companion promotes it to the
1244
+ first-class `brief.servedJudgmentId` field and uses it automatically. The
1245
+ `--served-judgment-id` option remains an explicit override and compatibility
1246
+ path. Without either identity, receipt capture remains fail-closed and reports
1247
+ `missing_served_judgment_id`. Receipts follow an explicit
1245
1248
  `proposed -> acknowledged -> applied -> verified` lifecycle. A served
1246
1249
  recommendation is only `acknowledged` by default: its `expectedBehaviorChange`,
1247
1250
  severity, required actions, and recommended checks never prove adaptation.
@@ -1266,6 +1269,21 @@ plan changed. Without a selector, or when the selector matches none of them,
1266
1269
  every recommendation remains `acknowledged`; one global plan diff is never
1267
1270
  credited to all recommendations.
1268
1271
 
1272
+ The `advisorReceiptCapture.measurement` object makes that boundary measurable.
1273
+ It reports linked or missing judgment identity, targeted and unscoped receipt
1274
+ counts, acknowledged/applied/verified/blocked states, unmeasured
1275
+ recommendations, and total receipt coverage. An unscoped acknowledgement proves
1276
+ only that the first-party runtime saw the recommendation; it does not prove the
1277
+ agent selected, applied, or verified it.
1278
+
1279
+ Every invocation also emits one
1280
+ `project-intelligence.judgment-run-envelope.v1` at top-level. The envelope
1281
+ contains a bounded opaque `runId`, its identity source, and `startedAt`; the
1282
+ same object is attached to every first-party Advisor receipt created by that
1283
+ invocation. A hosted Snipara session id wins, then a Codex session id, with a
1284
+ generated UUID fallback. This makes execution-level funnels available even
1285
+ when the caller did not inject a session environment variable.
1286
+
1269
1287
  Missing, partial, or hash-identical snapshots remain `acknowledged`. An applied
1270
1288
  receipt becomes `verified` only when `--outcome-receipts` supplies a receipt
1271
1289
  whose `decision.advisorRecommendationIds` contains that recommendation id and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "3.2.31",
3
+ "version": "3.2.32",
4
4
  "description": "Local-first CLI that asks your repo what breaks before an AI coding agent edits it.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {