snipara-companion 3.1.0 → 3.1.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/README.md CHANGED
@@ -94,8 +94,10 @@ sample from `sample_unreviewed` to `sample_reviewed` or `sample_rejected`.
94
94
  For conversational human review, run `workflow producer-triage` to create a
95
95
  batched Decision Request V0 artifact, `workflow decisions --json` to give the
96
96
  LLM client the exact question/evidence/options to ask, and `workflow decide`
97
- only after the human answers. Decision requests never resolve by timeout or
98
- default, and only `workflow decide` applies the existing `producer-review` path.
97
+ only after the human answers. Batched requests include readable evidence items
98
+ with artifact summaries, statuses, file hints, and metadata instead of only
99
+ opaque refs. Decision requests never resolve by timeout or default, and only
100
+ `workflow decide` applies the existing `producer-review` path.
99
101
  Other producers such as `outcome-capture preview --emit-decisions`,
100
102
  `workflow decision-producer memory`, and `workflow decision-producer context-risk`
101
103
  emit requests with their existing apply paths declared; they do not write
package/dist/index.d.ts CHANGED
@@ -2814,7 +2814,9 @@ interface ProducerLoopArtifactReportSummary {
2814
2814
  producerKind: ProducerLoopProducerKind;
2815
2815
  workflowId?: string;
2816
2816
  phaseId?: string;
2817
+ phaseTitle?: string;
2817
2818
  outcome?: TaskCommitOutcome;
2819
+ summary?: string;
2818
2820
  path: string;
2819
2821
  relativePath: string;
2820
2822
  artifactHash: string;
package/dist/index.js CHANGED
@@ -12533,6 +12533,7 @@ function buildDecisionRequest(input) {
12533
12533
  evidence: {
12534
12534
  summary: input.evidence.summary,
12535
12535
  refs: uniqueStrings8(input.evidence.refs),
12536
+ ...input.evidence.items ? { items: normalizeEvidenceItems(input.evidence.items) } : {},
12536
12537
  reasonCodes: uniqueStrings8(input.evidence.reasonCodes),
12537
12538
  ...input.evidence.files ? { files: uniqueStrings8(input.evidence.files) } : {},
12538
12539
  ...input.evidence.applyPath ? { applyPath: input.evidence.applyPath } : {},
@@ -12595,6 +12596,30 @@ function uniqueStrings8(values) {
12595
12596
  }
12596
12597
  return result;
12597
12598
  }
12599
+ function normalizeEvidenceItems(items) {
12600
+ const seen = /* @__PURE__ */ new Set();
12601
+ const normalized = [];
12602
+ for (const item of items) {
12603
+ const ref = item.ref.trim();
12604
+ if (!ref || seen.has(ref)) continue;
12605
+ seen.add(ref);
12606
+ normalized.push({
12607
+ ref,
12608
+ ...normalizeOptionalString(item.title) ? { title: normalizeOptionalString(item.title) } : {},
12609
+ ...normalizeOptionalString(item.summary) ? { summary: normalizeOptionalString(item.summary) } : {},
12610
+ ...normalizeOptionalString(item.kind) ? { kind: normalizeOptionalString(item.kind) } : {},
12611
+ ...normalizeOptionalString(item.status) ? { status: normalizeOptionalString(item.status) } : {},
12612
+ ...item.files ? { files: uniqueStrings8(item.files) } : {},
12613
+ ...item.metadata ? { metadata: normalizeMetadata(item.metadata) } : {}
12614
+ });
12615
+ }
12616
+ return normalized;
12617
+ }
12618
+ function normalizeMetadata(metadata) {
12619
+ return Object.fromEntries(
12620
+ Object.entries(metadata).filter(([, value]) => value !== void 0).sort(([left], [right]) => left.localeCompare(right))
12621
+ );
12622
+ }
12598
12623
  function isRecord6(value) {
12599
12624
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
12600
12625
  }
@@ -16025,6 +16050,7 @@ function summarizeProducerLoopArtifactFile(filePath, cwd) {
16025
16050
  );
16026
16051
  }
16027
16052
  const producer = isRecord8(parsed.producer) ? parsed.producer : void 0;
16053
+ const source2 = isRecord8(parsed.source) ? parsed.source : void 0;
16028
16054
  const ledger = isRecord8(parsed.ledger) ? parsed.ledger : void 0;
16029
16055
  const calibration = isRecord8(parsed.calibration) ? parsed.calibration : void 0;
16030
16056
  const review = isRecord8(parsed.review) ? parsed.review : void 0;
@@ -16051,7 +16077,9 @@ function summarizeProducerLoopArtifactFile(filePath, cwd) {
16051
16077
  producerKind,
16052
16078
  workflowId: stringValue5(producer.workflowId),
16053
16079
  phaseId: stringValue5(producer.phaseId),
16080
+ phaseTitle: stringValue5(producer.phaseTitle),
16054
16081
  outcome: isTaskCommitOutcome(outcome) ? outcome : void 0,
16082
+ summary: stringValue5(source2?.summary),
16055
16083
  path: filePath,
16056
16084
  relativePath: relativePath2,
16057
16085
  artifactHash: `sha256:${hashContent(content)}`,
@@ -16422,6 +16450,23 @@ function printDecisionRequests(entries) {
16422
16450
  console.log(` recommendation: ${request.recommendation}`);
16423
16451
  }
16424
16452
  console.log(` evidence: ${request.evidence.summary}`);
16453
+ if (request.evidence.items?.length) {
16454
+ console.log(" items:");
16455
+ for (const item of request.evidence.items) {
16456
+ console.log(` - ${item.ref}${item.title ? `: ${item.title}` : ""}`);
16457
+ if (item.status || item.kind) {
16458
+ console.log(` ${[item.kind, item.status].filter(Boolean).join(" | ")}`);
16459
+ }
16460
+ if (item.summary) {
16461
+ console.log(` ${item.summary}`);
16462
+ }
16463
+ if (item.files?.length) {
16464
+ console.log(
16465
+ ` files: ${item.files.slice(0, 5).join(", ")}${item.files.length > 5 ? ` (+${item.files.length - 5})` : ""}`
16466
+ );
16467
+ }
16468
+ }
16469
+ }
16425
16470
  if (request.evidence.applyCommand) {
16426
16471
  console.log(` apply: ${request.evidence.applyCommand}`);
16427
16472
  }
@@ -16531,6 +16576,23 @@ function buildProducerTriageRequest(report) {
16531
16576
  return null;
16532
16577
  }
16533
16578
  const refs = candidates.map((artifact) => artifact.artifactId);
16579
+ const items = candidates.map((artifact) => ({
16580
+ ref: artifact.artifactId,
16581
+ title: producerLoopTriageItemTitle(artifact),
16582
+ summary: artifact.summary ?? `${artifact.producerKind} sample for review.`,
16583
+ kind: artifact.producerKind,
16584
+ status: artifact.reviewStatus,
16585
+ files: artifact.files.slice(0, 12),
16586
+ metadata: {
16587
+ generatedAt: artifact.generatedAt,
16588
+ workflowId: artifact.workflowId ?? null,
16589
+ phaseId: artifact.phaseId ?? null,
16590
+ outcome: artifact.outcome ?? null,
16591
+ artifactHash: artifact.artifactHash,
16592
+ ledgerHash: artifact.ledgerHash ?? null,
16593
+ relativePath: artifact.relativePath
16594
+ }
16595
+ }));
16534
16596
  return buildDecisionRequest({
16535
16597
  producer: {
16536
16598
  kind: "producer_loop_triage",
@@ -16542,6 +16604,7 @@ function buildProducerTriageRequest(report) {
16542
16604
  evidence: {
16543
16605
  summary: `${candidates.length} unreviewed Producer Loop samples; ${report.invalidArtifacts.length} invalid artifacts; hardGateReady remains false.`,
16544
16606
  refs,
16607
+ items,
16545
16608
  reasonCodes: uniqueStrings10([
16546
16609
  "triage_rules_v0",
16547
16610
  "producer_loop_report_valid",
@@ -16561,6 +16624,11 @@ function buildProducerTriageRequest(report) {
16561
16624
  ]
16562
16625
  });
16563
16626
  }
16627
+ function producerLoopTriageItemTitle(artifact) {
16628
+ const workflow2 = artifact.workflowId ?? "producer-loop";
16629
+ const phase = artifact.phaseTitle ?? artifact.phaseId;
16630
+ return phase ? `${workflow2} / ${phase}` : workflow2;
16631
+ }
16564
16632
  async function workflowProducerTriageCommand(options) {
16565
16633
  const report = buildProducerLoopReport({ minReviewSampleSize: options.minReviewSamples });
16566
16634
  const request = buildProducerTriageRequest(report);
@@ -222,7 +222,8 @@ snipara-companion workflow resume --include-session-context
222
222
  It never marks samples reviewed by itself.
223
223
  - `workflow decisions` lists pending decision requests in a compact shape an LLM
224
224
  client can render as a human question, including evidence, options,
225
- recommendation, and declared apply path.
225
+ recommendation, declared apply path, and readable evidence items for batched
226
+ decisions.
226
227
  - `workflow decide <request-id> --choose <option> --reviewer <name>` writes a
227
228
  Decision Response V0 receipt under `.snipara/decisions/resolved/`. For
228
229
  Producer Loop triage accept/reject choices, it applies the existing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "3.1.0",
3
+ "version": "3.1.1",
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": {