snipara-companion 3.2.27 → 3.2.29
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 +22 -0
- package/README.md +17 -1
- package/dist/index.d.ts +44 -0
- package/dist/index.js +190 -7
- package/docs/FULL_REFERENCE.md +14 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
Release notes for `snipara-companion`, newest first.
|
|
4
4
|
|
|
5
|
+
## New In 3.2.29
|
|
6
|
+
|
|
7
|
+
- Extends `workflow producer-report` with attributed gated-execution samples
|
|
8
|
+
joined to their persisted supervisor reviews.
|
|
9
|
+
- Adds per-`workerId`/`workCategory` `workerTrust` breakdowns with execution,
|
|
10
|
+
review, accepted, blocked, incomplete receipt-family, and workflow fingerprint
|
|
11
|
+
counts.
|
|
12
|
+
- Keeps the read model deliberately supervised: every pair remains
|
|
13
|
+
`probation_supervised`, `hardGateReady=false`, and lists the evidence still
|
|
14
|
+
required before the separate Trust Promotion gates can be implemented.
|
|
15
|
+
|
|
16
|
+
## New In 3.2.28
|
|
17
|
+
|
|
18
|
+
- Adds bounded Outcome Loop retrieval correlation to generated Codex, Claude,
|
|
19
|
+
Cursor, VS Code/Continue, and generic HTTP MCP references through
|
|
20
|
+
`SNIPARA_SESSION_ID` / `X-Snipara-Session-Id`.
|
|
21
|
+
- Prints the active correlation session during `init` and documents the
|
|
22
|
+
explicit `correlation_context.session_id` fallback for MCP clients that
|
|
23
|
+
cannot inject environment-backed headers.
|
|
24
|
+
- Keeps the correlation identifier telemetry-only and project-scoped; it does
|
|
25
|
+
not alter authentication or authorization.
|
|
26
|
+
|
|
5
27
|
## New In 3.2.27
|
|
6
28
|
|
|
7
29
|
- Scopes `context-control drift` dirty Git detection to the ProjectContext
|
package/README.md
CHANGED
|
@@ -49,6 +49,18 @@ Use this after `create-snipara` activation. `create-snipara` remains the
|
|
|
49
49
|
canonical engine for first workspace setup; Companion owns the repeatable local
|
|
50
50
|
continuity loop after that.
|
|
51
51
|
|
|
52
|
+
## Retrieval And Outcome Correlation
|
|
53
|
+
|
|
54
|
+
`snipara-companion init` creates and prints a bounded workspace session ID.
|
|
55
|
+
Export it as `SNIPARA_SESSION_ID` before starting Codex, Claude, Cursor, VS Code,
|
|
56
|
+
Continue, or another HTTP MCP client. Generated configs forward it as
|
|
57
|
+
`X-Snipara-Session-Id`, while canonical execution events use the same Companion
|
|
58
|
+
session automatically. Clients without environment-backed headers can pass the
|
|
59
|
+
same value as `correlation_context.session_id` on retrieval tools.
|
|
60
|
+
|
|
61
|
+
The identifier is opaque, project-scoped telemetry. It does not grant access or
|
|
62
|
+
change authorization, and explicit per-call correlation remains authoritative.
|
|
63
|
+
|
|
52
64
|
Example output excerpt:
|
|
53
65
|
|
|
54
66
|
```text
|
|
@@ -245,7 +257,11 @@ redacted Coding Intelligence Ledger, not automatic durable memory, worker
|
|
|
245
257
|
execution, calibrated confidence, or server-side attestation. Use
|
|
246
258
|
`workflow producer-report` to inspect local adoption, reason-code counts, sample
|
|
247
259
|
size, reviewed/rejected/unreviewed counts, invalid artifacts, and calibration
|
|
248
|
-
caveats before any future hard gate.
|
|
260
|
+
caveats before any future hard gate. The report also joins attributed gated
|
|
261
|
+
receipts from `.snipara/orchestrator/executions/` with persisted supervisor
|
|
262
|
+
reviews, then emits `workerReceipts` and a per-`workerId`/`workCategory`
|
|
263
|
+
`workerTrust` breakdown. This is observability only: every pair remains
|
|
264
|
+
`probation_supervised` and `hardGateReady=false`.
|
|
249
265
|
The report also recognizes exported PR Answer Pack decision-capture artifacts
|
|
250
266
|
with producer kind `pr_answer_pack_decision_capture`, so calibration can track
|
|
251
267
|
more than the workflow producer once those artifacts are present locally.
|
package/dist/index.d.ts
CHANGED
|
@@ -3543,6 +3543,39 @@ interface ProducerLoopArtifactReportSummary {
|
|
|
3543
3543
|
reviewedAt?: string;
|
|
3544
3544
|
reviewer?: string;
|
|
3545
3545
|
}
|
|
3546
|
+
interface WorkerExecutionReceiptReportSummary {
|
|
3547
|
+
receiptId: string;
|
|
3548
|
+
schemaVersion: string;
|
|
3549
|
+
recordedAt?: string;
|
|
3550
|
+
workerId: string;
|
|
3551
|
+
workCategory: string;
|
|
3552
|
+
routingCardRef?: string;
|
|
3553
|
+
workflowFingerprint?: string;
|
|
3554
|
+
executionActor: string;
|
|
3555
|
+
status?: string;
|
|
3556
|
+
reviewStatus: "review_pending" | "accepted" | "blocked";
|
|
3557
|
+
executed: boolean;
|
|
3558
|
+
receiptFamilyComplete: boolean;
|
|
3559
|
+
missingReceiptFamilies: string[];
|
|
3560
|
+
path: string;
|
|
3561
|
+
relativePath: string;
|
|
3562
|
+
reviewPath?: string;
|
|
3563
|
+
reviewRelativePath?: string;
|
|
3564
|
+
}
|
|
3565
|
+
interface WorkerTrustReportRow {
|
|
3566
|
+
workerId: string;
|
|
3567
|
+
workCategory: string;
|
|
3568
|
+
state: "probation_supervised";
|
|
3569
|
+
sampleSize: number;
|
|
3570
|
+
executedSampleSize: number;
|
|
3571
|
+
reviewedSampleSize: number;
|
|
3572
|
+
verifiedSampleSize: number;
|
|
3573
|
+
blockedSampleSize: number;
|
|
3574
|
+
incompleteReceiptSampleSize: number;
|
|
3575
|
+
workflowFingerprints: string[];
|
|
3576
|
+
hardGateReady: false;
|
|
3577
|
+
nextRequired: string[];
|
|
3578
|
+
}
|
|
3546
3579
|
interface ProducerLoopReport {
|
|
3547
3580
|
version: typeof PRODUCER_LOOP_REPORT_VERSION;
|
|
3548
3581
|
generatedAt: string;
|
|
@@ -3566,6 +3599,17 @@ interface ProducerLoopReport {
|
|
|
3566
3599
|
reasonCodes: {
|
|
3567
3600
|
counts: Record<string, number>;
|
|
3568
3601
|
};
|
|
3602
|
+
workerReceipts: {
|
|
3603
|
+
sourceDirectories: string[];
|
|
3604
|
+
sampleSize: number;
|
|
3605
|
+
samples: WorkerExecutionReceiptReportSummary[];
|
|
3606
|
+
invalidArtifacts: Array<{
|
|
3607
|
+
path: string;
|
|
3608
|
+
relativePath: string;
|
|
3609
|
+
error: string;
|
|
3610
|
+
}>;
|
|
3611
|
+
};
|
|
3612
|
+
workerTrust: WorkerTrustReportRow[];
|
|
3569
3613
|
calibration: {
|
|
3570
3614
|
status: "no_samples" | "insufficient_samples" | "reviewable_sample_set";
|
|
3571
3615
|
sampleSize: number;
|
package/dist/index.js
CHANGED
|
@@ -5641,6 +5641,7 @@ Claude Code should apply this workflow automatically for project-specific work;
|
|
|
5641
5641
|
|
|
5642
5642
|
- Bound Snipara project: \`${projectSlug}\`
|
|
5643
5643
|
- Hosted MCP endpoint: \`${buildHostedMcpEndpoint(projectSlug)}\`
|
|
5644
|
+
- Keep \`SNIPARA_SESSION_ID\` equal to the active Companion session. Generated MCP configs send it as \`X-Snipara-Session-Id\`; when a client cannot send that header, pass the same value as \`correlation_context.session_id\` on retrieval tools.
|
|
5644
5645
|
- At the start of substantial work, validate the hosted MCP surface with a tool-oriented call, then use \`snipara_recall\` and a targeted \`snipara_context_query\` before falling back to local search.
|
|
5645
5646
|
- Do not treat empty MCP resources/templates as an outage. If the tool surface looks incomplete, call \`snipara_help(list_all=true)\` and compare exact tool names.
|
|
5646
5647
|
- Use \`snipara_context_query\` for docs, business context, architecture notes, runbooks, and source truth. Use \`snipara_get_chunk\` for exact cited sections when references are returned.
|
|
@@ -5660,6 +5661,7 @@ function buildSharedWorkflowInstructionBlock(projectSlug, client) {
|
|
|
5660
5661
|
This workspace is bound to Snipara project \`${projectSlug}\` for ${formatClientName(client)}. Agents should use Snipara automatically for project-specific context, decisions, and workflow state.
|
|
5661
5662
|
|
|
5662
5663
|
- Hosted MCP endpoint: \`${buildHostedMcpEndpoint(projectSlug)}\`
|
|
5664
|
+
- Keep \`SNIPARA_SESSION_ID\` equal to the active Companion session. Generated MCP configs send it as \`X-Snipara-Session-Id\`; when a client cannot send that header, pass the same value as \`correlation_context.session_id\` on retrieval tools.
|
|
5663
5665
|
- At the start of substantial work, validate the hosted MCP surface with a tool-oriented call, then use \`snipara_recall\` and a targeted \`snipara_context_query\` before falling back to local search.
|
|
5664
5666
|
- Do not treat empty MCP resources/templates as an outage. If the tool surface looks incomplete, call \`snipara_help(list_all=true)\` and compare exact tool names.
|
|
5665
5667
|
- Use \`snipara_context_query\` for docs, business context, architecture notes, runbooks, and source truth. Use \`snipara_get_chunk\` for exact cited sections when references are returned.
|
|
@@ -5911,6 +5913,7 @@ function generateCodexConfigString(projectSlug) {
|
|
|
5911
5913
|
type = "streamable_http"
|
|
5912
5914
|
url = "https://api.snipara.com/mcp/${projectSlug}"
|
|
5913
5915
|
bearer_token_env_var = "SNIPARA_API_KEY"
|
|
5916
|
+
env_http_headers = { "X-Snipara-Session-Id" = "SNIPARA_SESSION_ID" }
|
|
5914
5917
|
`;
|
|
5915
5918
|
}
|
|
5916
5919
|
function generateGenericMcpReferenceString(client, projectSlug) {
|
|
@@ -5920,7 +5923,8 @@ function generateGenericMcpReferenceString(client, projectSlug) {
|
|
|
5920
5923
|
name: "snipara",
|
|
5921
5924
|
url: `https://api.snipara.com/mcp/${projectSlug}`,
|
|
5922
5925
|
headers: {
|
|
5923
|
-
"X-API-Key": "${SNIPARA_API_KEY}"
|
|
5926
|
+
"X-API-Key": "${SNIPARA_API_KEY}",
|
|
5927
|
+
"X-Snipara-Session-Id": "${SNIPARA_SESSION_ID}"
|
|
5924
5928
|
}
|
|
5925
5929
|
},
|
|
5926
5930
|
null,
|
|
@@ -5935,7 +5939,8 @@ function generateGenericMcpReferenceString(client, projectSlug) {
|
|
|
5935
5939
|
type: "http",
|
|
5936
5940
|
url: `https://api.snipara.com/mcp/${projectSlug}`,
|
|
5937
5941
|
headers: {
|
|
5938
|
-
"X-API-Key": "${SNIPARA_API_KEY}"
|
|
5942
|
+
"X-API-Key": "${SNIPARA_API_KEY}",
|
|
5943
|
+
"X-Snipara-Session-Id": "${SNIPARA_SESSION_ID}"
|
|
5939
5944
|
}
|
|
5940
5945
|
}
|
|
5941
5946
|
}
|
|
@@ -5995,6 +6000,7 @@ async function callSniparaTool(name: string, args: Record<string, unknown>): Pro
|
|
|
5995
6000
|
headers: {
|
|
5996
6001
|
"Content-Type": "application/json",
|
|
5997
6002
|
"X-API-Key": SNIPARA_API_KEY,
|
|
6003
|
+
"X-Snipara-Session-Id": process.env.SNIPARA_SESSION_ID || "",
|
|
5998
6004
|
},
|
|
5999
6005
|
body: JSON.stringify({
|
|
6000
6006
|
jsonrpc: "2.0",
|
|
@@ -6561,12 +6567,13 @@ async function initCommand(options) {
|
|
|
6561
6567
|
existingProjectId: existingConfig.projectId,
|
|
6562
6568
|
selectedProject
|
|
6563
6569
|
});
|
|
6570
|
+
const sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
6564
6571
|
saveConfig(
|
|
6565
6572
|
{
|
|
6566
6573
|
apiKey,
|
|
6567
6574
|
apiUrl: existingConfig.apiUrl,
|
|
6568
6575
|
projectId: projectIdentifier,
|
|
6569
|
-
sessionId
|
|
6576
|
+
sessionId,
|
|
6570
6577
|
client: selectedClient
|
|
6571
6578
|
},
|
|
6572
6579
|
{ cwd: projectDir, scope: "workspace" }
|
|
@@ -6688,6 +6695,10 @@ async function initCommand(options) {
|
|
|
6688
6695
|
console.log(`Companion config: ${configPaths.companion}`);
|
|
6689
6696
|
console.log(`Selected project: ${formatProjectChoice(selectedProject)}`);
|
|
6690
6697
|
console.log(`Selected client: ${formatClientName(selectedClient)}`);
|
|
6698
|
+
console.log(`Correlation session: ${sessionId}`);
|
|
6699
|
+
console.log(
|
|
6700
|
+
"Export SNIPARA_SESSION_ID with this value before starting the MCP client so served context and execution outcomes share one bounded session."
|
|
6701
|
+
);
|
|
6691
6702
|
if (options.withHooks) {
|
|
6692
6703
|
console.log("\nNext steps:");
|
|
6693
6704
|
if (hookClient === "cursor") {
|
|
@@ -21538,6 +21549,154 @@ function producerLoopCalibrationStatus(sampleSize, reviewedSampleSize, minReview
|
|
|
21538
21549
|
}
|
|
21539
21550
|
return "reviewable_sample_set";
|
|
21540
21551
|
}
|
|
21552
|
+
var WORKER_RECEIPT_RELATIVE_DIR = path22.join(".snipara", "orchestrator", "executions");
|
|
21553
|
+
var WORKER_REVIEW_RELATIVE_DIR = path22.join(".snipara", "orchestrator", "reviews");
|
|
21554
|
+
var REQUIRED_WORKER_RECEIPT_FAMILIES = [
|
|
21555
|
+
"handoffReceiptId",
|
|
21556
|
+
"claimId",
|
|
21557
|
+
"proofReceiptIds",
|
|
21558
|
+
"outcomeReceiptId",
|
|
21559
|
+
"brainUpdateReceiptId"
|
|
21560
|
+
];
|
|
21561
|
+
function listJsonFiles(relativeDir, cwd) {
|
|
21562
|
+
const directory = path22.join(cwd, relativeDir);
|
|
21563
|
+
if (!fs23.existsSync(directory)) {
|
|
21564
|
+
return [];
|
|
21565
|
+
}
|
|
21566
|
+
return fs23.readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => path22.join(directory, entry.name)).sort();
|
|
21567
|
+
}
|
|
21568
|
+
function readWorkerReviewFiles(cwd) {
|
|
21569
|
+
const reviews = /* @__PURE__ */ new Map();
|
|
21570
|
+
const invalid = [];
|
|
21571
|
+
for (const filePath of listJsonFiles(WORKER_REVIEW_RELATIVE_DIR, cwd)) {
|
|
21572
|
+
try {
|
|
21573
|
+
const parsed = JSON.parse(fs23.readFileSync(filePath, "utf-8"));
|
|
21574
|
+
if (!isRecord10(parsed)) {
|
|
21575
|
+
throw new Error("review must be a JSON object");
|
|
21576
|
+
}
|
|
21577
|
+
const receiptId = stringValue7(parsed.receiptId);
|
|
21578
|
+
if (!receiptId) {
|
|
21579
|
+
throw new Error("review is missing receiptId");
|
|
21580
|
+
}
|
|
21581
|
+
reviews.set(receiptId, { value: parsed, path: filePath });
|
|
21582
|
+
} catch (error) {
|
|
21583
|
+
invalid.push({
|
|
21584
|
+
path: filePath,
|
|
21585
|
+
relativePath: toProjectRelativePath3(filePath, cwd),
|
|
21586
|
+
error: error instanceof Error ? error.message : String(error)
|
|
21587
|
+
});
|
|
21588
|
+
}
|
|
21589
|
+
}
|
|
21590
|
+
return { reviews, invalid };
|
|
21591
|
+
}
|
|
21592
|
+
function missingWorkerReceiptFamilies(receipt) {
|
|
21593
|
+
const refs = isRecord10(receipt.receiptRefs) ? receipt.receiptRefs : isRecord10(receipt.receipt) ? receipt.receipt : {};
|
|
21594
|
+
return REQUIRED_WORKER_RECEIPT_FAMILIES.filter((family) => {
|
|
21595
|
+
const value = refs[family];
|
|
21596
|
+
return family === "proofReceiptIds" ? !Array.isArray(value) || value.filter((item) => Boolean(stringValue7(item))).length === 0 : !stringValue7(value);
|
|
21597
|
+
});
|
|
21598
|
+
}
|
|
21599
|
+
function summarizeWorkerExecutionReceipts(cwd) {
|
|
21600
|
+
const reviewFiles = readWorkerReviewFiles(cwd);
|
|
21601
|
+
const invalid = [...reviewFiles.invalid];
|
|
21602
|
+
const samples = [];
|
|
21603
|
+
for (const filePath of listJsonFiles(WORKER_RECEIPT_RELATIVE_DIR, cwd)) {
|
|
21604
|
+
try {
|
|
21605
|
+
const parsed = JSON.parse(fs23.readFileSync(filePath, "utf-8"));
|
|
21606
|
+
if (!isRecord10(parsed)) {
|
|
21607
|
+
throw new Error("receipt must be a JSON object");
|
|
21608
|
+
}
|
|
21609
|
+
const receiptId = stringValue7(parsed.receiptId);
|
|
21610
|
+
const schemaVersion = stringValue7(parsed.schemaVersion);
|
|
21611
|
+
if (!receiptId || !schemaVersion) {
|
|
21612
|
+
throw new Error("receipt is missing receiptId or schemaVersion");
|
|
21613
|
+
}
|
|
21614
|
+
const attribution = isRecord10(parsed.workerAttribution) ? parsed.workerAttribution : {};
|
|
21615
|
+
const gate2 = isRecord10(parsed.gate) ? parsed.gate : {};
|
|
21616
|
+
const review = reviewFiles.reviews.get(receiptId);
|
|
21617
|
+
const reviewStatusValue = stringValue7(review?.value.reviewStatus);
|
|
21618
|
+
const reviewStatus = reviewStatusValue === "accepted" ? "accepted" : reviewStatusValue === "blocked" ? "blocked" : "review_pending";
|
|
21619
|
+
const workerId = stringValue7(parsed.workerId) ?? stringValue7(attribution.workerId) ?? stringValue7(parsed.selectedWorkerCandidateId) ?? stringValue7(gate2.selectedWorkerCandidateId) ?? "main_agent";
|
|
21620
|
+
const workCategory = stringValue7(parsed.workCategory) ?? stringValue7(attribution.workCategory) ?? "unknown";
|
|
21621
|
+
const executionActor = stringValue7(parsed.executionActor) ?? stringValue7(attribution.executionActor) ?? (Number(parsed.workersSpawned ?? 0) > 0 ? "worker" : "main_agent");
|
|
21622
|
+
const missingReceiptFamilies = missingWorkerReceiptFamilies(parsed);
|
|
21623
|
+
samples.push({
|
|
21624
|
+
receiptId,
|
|
21625
|
+
schemaVersion,
|
|
21626
|
+
recordedAt: stringValue7(parsed.recordedAt),
|
|
21627
|
+
workerId,
|
|
21628
|
+
workCategory,
|
|
21629
|
+
routingCardRef: stringValue7(parsed.routingCardRef) ?? stringValue7(attribution.routingCardRef),
|
|
21630
|
+
workflowFingerprint: stringValue7(parsed.workflowFingerprint) ?? stringValue7(attribution.workflowFingerprint),
|
|
21631
|
+
executionActor,
|
|
21632
|
+
status: stringValue7(parsed.status),
|
|
21633
|
+
reviewStatus,
|
|
21634
|
+
executed: executionActor === "worker" || Number(parsed.workersSpawned ?? 0) > 0,
|
|
21635
|
+
receiptFamilyComplete: missingReceiptFamilies.length === 0,
|
|
21636
|
+
missingReceiptFamilies,
|
|
21637
|
+
path: filePath,
|
|
21638
|
+
relativePath: toProjectRelativePath3(filePath, cwd),
|
|
21639
|
+
...review ? {
|
|
21640
|
+
reviewPath: review.path,
|
|
21641
|
+
reviewRelativePath: toProjectRelativePath3(review.path, cwd)
|
|
21642
|
+
} : {}
|
|
21643
|
+
});
|
|
21644
|
+
} catch (error) {
|
|
21645
|
+
invalid.push({
|
|
21646
|
+
path: filePath,
|
|
21647
|
+
relativePath: toProjectRelativePath3(filePath, cwd),
|
|
21648
|
+
error: error instanceof Error ? error.message : String(error)
|
|
21649
|
+
});
|
|
21650
|
+
}
|
|
21651
|
+
}
|
|
21652
|
+
samples.sort(
|
|
21653
|
+
(left, right) => (left.recordedAt ?? left.receiptId).localeCompare(right.recordedAt ?? right.receiptId)
|
|
21654
|
+
);
|
|
21655
|
+
return { samples, invalid };
|
|
21656
|
+
}
|
|
21657
|
+
function buildWorkerTrustRows(samples, minReviewSampleSize) {
|
|
21658
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
21659
|
+
for (const sample of samples) {
|
|
21660
|
+
const key = `${sample.workerId}\0${sample.workCategory}`;
|
|
21661
|
+
grouped.set(key, [...grouped.get(key) ?? [], sample]);
|
|
21662
|
+
}
|
|
21663
|
+
return [...grouped.values()].map((group) => {
|
|
21664
|
+
const reviewedSampleSize = group.filter(
|
|
21665
|
+
(sample) => sample.reviewStatus !== "review_pending"
|
|
21666
|
+
).length;
|
|
21667
|
+
const verifiedSampleSize = group.filter(
|
|
21668
|
+
(sample) => sample.reviewStatus === "accepted"
|
|
21669
|
+
).length;
|
|
21670
|
+
const incompleteReceiptSampleSize = group.filter(
|
|
21671
|
+
(sample) => !sample.receiptFamilyComplete
|
|
21672
|
+
).length;
|
|
21673
|
+
const nextRequired = [
|
|
21674
|
+
reviewedSampleSize < minReviewSampleSize ? `${minReviewSampleSize - reviewedSampleSize} more supervised review sample(s)` : void 0,
|
|
21675
|
+
verifiedSampleSize < 3 ? `${3 - verifiedSampleSize} more accepted verified sample(s)` : void 0,
|
|
21676
|
+
incompleteReceiptSampleSize > 0 ? `${incompleteReceiptSampleSize} sample(s) need complete receipt families` : void 0,
|
|
21677
|
+
group[0].workCategory === "unknown" ? "classify legacy samples by work category" : void 0,
|
|
21678
|
+
"Trust Promotion gate implementation remains required"
|
|
21679
|
+
].filter((item) => Boolean(item));
|
|
21680
|
+
return {
|
|
21681
|
+
workerId: group[0].workerId,
|
|
21682
|
+
workCategory: group[0].workCategory,
|
|
21683
|
+
state: "probation_supervised",
|
|
21684
|
+
sampleSize: group.length,
|
|
21685
|
+
executedSampleSize: group.filter((sample) => sample.executed).length,
|
|
21686
|
+
reviewedSampleSize,
|
|
21687
|
+
verifiedSampleSize,
|
|
21688
|
+
blockedSampleSize: group.filter((sample) => sample.reviewStatus === "blocked").length,
|
|
21689
|
+
incompleteReceiptSampleSize,
|
|
21690
|
+
workflowFingerprints: uniqueStrings18(
|
|
21691
|
+
group.map((sample) => sample.workflowFingerprint).filter((value) => Boolean(value))
|
|
21692
|
+
),
|
|
21693
|
+
hardGateReady: false,
|
|
21694
|
+
nextRequired
|
|
21695
|
+
};
|
|
21696
|
+
}).sort(
|
|
21697
|
+
(left, right) => left.workerId === right.workerId ? left.workCategory.localeCompare(right.workCategory) : left.workerId.localeCompare(right.workerId)
|
|
21698
|
+
);
|
|
21699
|
+
}
|
|
21541
21700
|
function buildProducerLoopReport(options = {}) {
|
|
21542
21701
|
const cwd = path22.resolve(options.cwd ?? process.cwd());
|
|
21543
21702
|
const minReviewSampleSize = Math.max(
|
|
@@ -21578,12 +21737,16 @@ function buildProducerLoopReport(options = {}) {
|
|
|
21578
21737
|
reviewedSampleSize,
|
|
21579
21738
|
minReviewSampleSize
|
|
21580
21739
|
);
|
|
21740
|
+
const workerReceiptReport = summarizeWorkerExecutionReceipts(cwd);
|
|
21741
|
+
const workerTrust = buildWorkerTrustRows(workerReceiptReport.samples, minReviewSampleSize);
|
|
21581
21742
|
const recommendedActions = [
|
|
21582
21743
|
artifacts.length === 0 ? "Run a current Producer Loop producer, such as workflow phase-commit/final-commit or PR Answer Pack decision capture, to create samples." : void 0,
|
|
21583
21744
|
artifacts.length > 0 && reviewedSampleSize < minReviewSampleSize ? `Review at least ${minReviewSampleSize} local Producer Loop samples before considering any calibration signal.` : void 0,
|
|
21584
21745
|
unreviewedSampleSize > 0 ? "Mark local samples with workflow producer-review after checking the embedded evidence." : void 0,
|
|
21585
21746
|
rejectedSampleSize > 0 ? "Inspect rejected Producer Loop samples for false positives, missing context, or reason-code drift." : void 0,
|
|
21586
21747
|
invalidArtifacts.length > 0 ? "Inspect invalid Producer Loop artifacts before trusting adoption counts." : void 0,
|
|
21748
|
+
workerReceiptReport.samples.length === 0 ? "Run agents execute-gated and persist agents review-gated results to create worker-attributed receipt samples." : void 0,
|
|
21749
|
+
workerReceiptReport.invalid.length > 0 ? "Inspect invalid worker execution receipts or reviews before trusting per-worker counts." : void 0,
|
|
21587
21750
|
"Review false positives, missing outcomes, and reason-code drift before any future enforcement."
|
|
21588
21751
|
].filter((item) => Boolean(item));
|
|
21589
21752
|
return {
|
|
@@ -21605,6 +21768,13 @@ function buildProducerLoopReport(options = {}) {
|
|
|
21605
21768
|
reasonCodes: {
|
|
21606
21769
|
counts: reasonCodeCounts
|
|
21607
21770
|
},
|
|
21771
|
+
workerReceipts: {
|
|
21772
|
+
sourceDirectories: [WORKER_RECEIPT_RELATIVE_DIR, WORKER_REVIEW_RELATIVE_DIR],
|
|
21773
|
+
sampleSize: workerReceiptReport.samples.length,
|
|
21774
|
+
samples: workerReceiptReport.samples,
|
|
21775
|
+
invalidArtifacts: workerReceiptReport.invalid
|
|
21776
|
+
},
|
|
21777
|
+
workerTrust,
|
|
21608
21778
|
calibration: {
|
|
21609
21779
|
status: calibrationStatus,
|
|
21610
21780
|
sampleSize: artifacts.length,
|
|
@@ -21804,6 +21974,19 @@ function printProducerLoopReport(report) {
|
|
|
21804
21974
|
if (report.invalidArtifacts.length > 0) {
|
|
21805
21975
|
printKeyValue2("Invalid artifacts:", report.invalidArtifacts.length);
|
|
21806
21976
|
}
|
|
21977
|
+
printKeyValue2("Worker receipts:", report.workerReceipts.sampleSize);
|
|
21978
|
+
if (report.workerReceipts.invalidArtifacts.length > 0) {
|
|
21979
|
+
printKeyValue2("Invalid worker evidence:", report.workerReceipts.invalidArtifacts.length);
|
|
21980
|
+
}
|
|
21981
|
+
if (report.workerTrust.length > 0) {
|
|
21982
|
+
console.log("");
|
|
21983
|
+
console.log(import_chalk6.default.bold("Worker Trust (supervised probation)"));
|
|
21984
|
+
for (const row of report.workerTrust) {
|
|
21985
|
+
console.log(
|
|
21986
|
+
`- ${row.workerId} / ${row.workCategory}: ${row.verifiedSampleSize} accepted, ${row.blockedSampleSize} blocked, ${row.reviewedSampleSize}/${row.sampleSize} reviewed`
|
|
21987
|
+
);
|
|
21988
|
+
}
|
|
21989
|
+
}
|
|
21807
21990
|
if (report.recommendedActions.length > 0) {
|
|
21808
21991
|
console.log("");
|
|
21809
21992
|
console.log(import_chalk6.default.bold("Recommended Actions"));
|
|
@@ -33396,7 +33579,7 @@ function applyLocalContextMutationPlan(options) {
|
|
|
33396
33579
|
writtenFiles
|
|
33397
33580
|
};
|
|
33398
33581
|
}
|
|
33399
|
-
function
|
|
33582
|
+
function listJsonFiles2(dir) {
|
|
33400
33583
|
if (!fs30.existsSync(dir)) return [];
|
|
33401
33584
|
return fs30.readdirSync(dir).filter((name) => name.endsWith(".json")).map((name) => path30.join(dir, name)).sort((left, right) => left.localeCompare(right));
|
|
33402
33585
|
}
|
|
@@ -33523,7 +33706,7 @@ function collectWorkflowDriftSignals(cwd) {
|
|
|
33523
33706
|
}
|
|
33524
33707
|
function collectDecisionDriftSignals(cwd) {
|
|
33525
33708
|
const pendingDir = path30.join(cwd, ".snipara", "decisions", "pending");
|
|
33526
|
-
const pending =
|
|
33709
|
+
const pending = listJsonFiles2(pendingDir).map((file) => toProjectRelativePath4(file, cwd));
|
|
33527
33710
|
return [
|
|
33528
33711
|
pending.length > 0 ? {
|
|
33529
33712
|
id: "decision-requests-pending",
|
|
@@ -33547,8 +33730,8 @@ function collectDecisionDriftSignals(cwd) {
|
|
|
33547
33730
|
];
|
|
33548
33731
|
}
|
|
33549
33732
|
function collectContextControlDriftSignals(cwd) {
|
|
33550
|
-
const planFiles =
|
|
33551
|
-
const receiptFiles =
|
|
33733
|
+
const planFiles = listJsonFiles2(path30.join(cwd, CONTEXT_CONTROL_PLANS_RELATIVE_DIR));
|
|
33734
|
+
const receiptFiles = listJsonFiles2(path30.join(cwd, CONTEXT_CONTROL_APPLIED_RELATIVE_DIR));
|
|
33552
33735
|
const receiptPlanHashes = new Set(
|
|
33553
33736
|
receiptFiles.map((file) => readJsonFileSafe(file).value).filter(isContextMutationApplyReceipt).map((receipt) => receipt.planHash)
|
|
33554
33737
|
);
|
package/docs/FULL_REFERENCE.md
CHANGED
|
@@ -324,7 +324,11 @@ snipara-companion workflow resume --include-session-context
|
|
|
324
324
|
workflow phase/final commits or exported PR Answer Pack decision-capture
|
|
325
325
|
producers, then reports adoption, producer kinds, workflow ids, reason-code
|
|
326
326
|
counts, invalid artifacts, sample size, reviewed/rejected/unreviewed counts,
|
|
327
|
-
and calibration caveats with `hardGateReady=false`.
|
|
327
|
+
and calibration caveats with `hardGateReady=false`. It also scans attributed
|
|
328
|
+
gated receipts and persisted reviews under `.snipara/orchestrator/`, reports
|
|
329
|
+
receipt-family completeness, and groups supervised evidence by
|
|
330
|
+
`(workerId, workCategory)` in `workerTrust`. It never promotes a worker or
|
|
331
|
+
changes an execution gate.
|
|
328
332
|
- `workflow producer-review` marks one local Producer Loop artifact as
|
|
329
333
|
`sample_reviewed` or `sample_rejected` after operator review. Use
|
|
330
334
|
`--artifact <path|file|artifactId>` for an exact sample or `--latest` for the
|
|
@@ -821,6 +825,15 @@ Mistral generates MCP-first files (`MISTRAL.md`, Vibe config, Le Chat connector
|
|
|
821
825
|
reference, and LangChain `ChatMistralAI.bindTools` snippets); Mistral request
|
|
822
826
|
hooks are model request hooks, not local agent lifecycle hooks.
|
|
823
827
|
|
|
828
|
+
All generated HTTP/SSE references include a bounded correlation header sourced
|
|
829
|
+
from `SNIPARA_SESSION_ID`. Set it to the session printed by `init` before
|
|
830
|
+
starting Codex, Claude, Cursor, VS Code/Continue, GLM, or another generic MCP
|
|
831
|
+
host. The hosted server uses the header only for project-scoped retrieval
|
|
832
|
+
telemetry and ignores invalid values. If the host cannot inject environment
|
|
833
|
+
headers, pass the same value as `correlation_context.session_id` on
|
|
834
|
+
`snipara_context_query`, `snipara_recall`, `snipara_search`, `snipara_ask`, and
|
|
835
|
+
`snipara_get_chunk`.
|
|
836
|
+
|
|
824
837
|
OpenClaw hooks remain separate:
|
|
825
838
|
|
|
826
839
|
```bash
|