snipara-companion 3.2.28 → 3.2.30
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 +5 -1
- package/dist/index.d.ts +53 -0
- package/dist/index.js +269 -12
- package/docs/FULL_REFERENCE.md +5 -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.30
|
|
6
|
+
|
|
7
|
+
- Persists an explicit `--ack-review-only` as a 15-minute, one-use
|
|
8
|
+
acknowledgement bound to the exact guard profile, action, and finding
|
|
9
|
+
fingerprint, so the immediately following Git hook can consume it safely.
|
|
10
|
+
- Invalidates that acknowledgement when findings change and never applies it
|
|
11
|
+
to required acknowledgements, active conflicts, or hard blocks.
|
|
12
|
+
- Resolves collaboration state from `SNIPARA_WORKSPACE_DIR` or the Git
|
|
13
|
+
top-level directory, preventing filtered package commands from writing a
|
|
14
|
+
nested `packages/cli/.snipara/` session.
|
|
15
|
+
|
|
16
|
+
## New In 3.2.29
|
|
17
|
+
|
|
18
|
+
- Extends `workflow producer-report` with attributed gated-execution samples
|
|
19
|
+
joined to their persisted supervisor reviews.
|
|
20
|
+
- Adds per-`workerId`/`workCategory` `workerTrust` breakdowns with execution,
|
|
21
|
+
review, accepted, blocked, incomplete receipt-family, and workflow fingerprint
|
|
22
|
+
counts.
|
|
23
|
+
- Keeps the read model deliberately supervised: every pair remains
|
|
24
|
+
`probation_supervised`, `hardGateReady=false`, and lists the evidence still
|
|
25
|
+
required before the separate Trust Promotion gates can be implemented.
|
|
26
|
+
|
|
5
27
|
## New In 3.2.28
|
|
6
28
|
|
|
7
29
|
- Adds bounded Outcome Loop retrieval correlation to generated Codex, Claude,
|
package/README.md
CHANGED
|
@@ -257,7 +257,11 @@ redacted Coding Intelligence Ledger, not automatic durable memory, worker
|
|
|
257
257
|
execution, calibrated confidence, or server-side attestation. Use
|
|
258
258
|
`workflow producer-report` to inspect local adoption, reason-code counts, sample
|
|
259
259
|
size, reviewed/rejected/unreviewed counts, invalid artifacts, and calibration
|
|
260
|
-
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`.
|
|
261
265
|
The report also recognizes exported PR Answer Pack decision-capture artifacts
|
|
262
266
|
with producer kind `pr_answer_pack_decision_capture`, so calibration can track
|
|
263
267
|
more than the workflow producer once those artifacts are present locally.
|
package/dist/index.d.ts
CHANGED
|
@@ -2831,6 +2831,7 @@ declare function buildCodingIntelligenceLedger(options?: CodingLedgerBuildOption
|
|
|
2831
2831
|
declare function codingLedgerExportCommand(options: CodingLedgerExportCommandOptions): Promise<void>;
|
|
2832
2832
|
|
|
2833
2833
|
declare const COLLABORATION_STATE_RELATIVE_PATH: string;
|
|
2834
|
+
type CollaborationGuardProfile = "edit" | "pre-commit" | "pre-push" | "pre-deploy" | "migration" | "schema" | "release-package";
|
|
2834
2835
|
type CollaborationGuardActionKind = "safe_to_ack" | "needs_handoff" | "needs_test" | "needs_package_review" | "blocking_conflict" | "guard_unavailable";
|
|
2835
2836
|
interface CollaborationGuardActionCard {
|
|
2836
2837
|
kind: CollaborationGuardActionKind;
|
|
@@ -2910,6 +2911,13 @@ interface CollaborationLocalLeaseRecord {
|
|
|
2910
2911
|
expiresAt?: string | null;
|
|
2911
2912
|
workSessionId?: string;
|
|
2912
2913
|
}
|
|
2914
|
+
interface CollaborationReviewOnlyAcknowledgement {
|
|
2915
|
+
profile: CollaborationGuardProfile;
|
|
2916
|
+
action: string;
|
|
2917
|
+
findingFingerprint: string;
|
|
2918
|
+
acknowledgedAt: string;
|
|
2919
|
+
expiresAt: string;
|
|
2920
|
+
}
|
|
2913
2921
|
interface CollaborationLocalState {
|
|
2914
2922
|
schemaVersion: "snipara.collaboration.v1";
|
|
2915
2923
|
updatedAt: string;
|
|
@@ -2926,6 +2934,7 @@ interface CollaborationLocalState {
|
|
|
2926
2934
|
files: string[];
|
|
2927
2935
|
resources: CollaborationResource[];
|
|
2928
2936
|
leases: CollaborationLocalLeaseRecord[];
|
|
2937
|
+
reviewOnlyAcknowledgements: CollaborationReviewOnlyAcknowledgement[];
|
|
2929
2938
|
lastGuard?: {
|
|
2930
2939
|
decision: CollaborationGuardDecision;
|
|
2931
2940
|
severity: string;
|
|
@@ -3543,6 +3552,39 @@ interface ProducerLoopArtifactReportSummary {
|
|
|
3543
3552
|
reviewedAt?: string;
|
|
3544
3553
|
reviewer?: string;
|
|
3545
3554
|
}
|
|
3555
|
+
interface WorkerExecutionReceiptReportSummary {
|
|
3556
|
+
receiptId: string;
|
|
3557
|
+
schemaVersion: string;
|
|
3558
|
+
recordedAt?: string;
|
|
3559
|
+
workerId: string;
|
|
3560
|
+
workCategory: string;
|
|
3561
|
+
routingCardRef?: string;
|
|
3562
|
+
workflowFingerprint?: string;
|
|
3563
|
+
executionActor: string;
|
|
3564
|
+
status?: string;
|
|
3565
|
+
reviewStatus: "review_pending" | "accepted" | "blocked";
|
|
3566
|
+
executed: boolean;
|
|
3567
|
+
receiptFamilyComplete: boolean;
|
|
3568
|
+
missingReceiptFamilies: string[];
|
|
3569
|
+
path: string;
|
|
3570
|
+
relativePath: string;
|
|
3571
|
+
reviewPath?: string;
|
|
3572
|
+
reviewRelativePath?: string;
|
|
3573
|
+
}
|
|
3574
|
+
interface WorkerTrustReportRow {
|
|
3575
|
+
workerId: string;
|
|
3576
|
+
workCategory: string;
|
|
3577
|
+
state: "probation_supervised";
|
|
3578
|
+
sampleSize: number;
|
|
3579
|
+
executedSampleSize: number;
|
|
3580
|
+
reviewedSampleSize: number;
|
|
3581
|
+
verifiedSampleSize: number;
|
|
3582
|
+
blockedSampleSize: number;
|
|
3583
|
+
incompleteReceiptSampleSize: number;
|
|
3584
|
+
workflowFingerprints: string[];
|
|
3585
|
+
hardGateReady: false;
|
|
3586
|
+
nextRequired: string[];
|
|
3587
|
+
}
|
|
3546
3588
|
interface ProducerLoopReport {
|
|
3547
3589
|
version: typeof PRODUCER_LOOP_REPORT_VERSION;
|
|
3548
3590
|
generatedAt: string;
|
|
@@ -3566,6 +3608,17 @@ interface ProducerLoopReport {
|
|
|
3566
3608
|
reasonCodes: {
|
|
3567
3609
|
counts: Record<string, number>;
|
|
3568
3610
|
};
|
|
3611
|
+
workerReceipts: {
|
|
3612
|
+
sourceDirectories: string[];
|
|
3613
|
+
sampleSize: number;
|
|
3614
|
+
samples: WorkerExecutionReceiptReportSummary[];
|
|
3615
|
+
invalidArtifacts: Array<{
|
|
3616
|
+
path: string;
|
|
3617
|
+
relativePath: string;
|
|
3618
|
+
error: string;
|
|
3619
|
+
}>;
|
|
3620
|
+
};
|
|
3621
|
+
workerTrust: WorkerTrustReportRow[];
|
|
3569
3622
|
calibration: {
|
|
3570
3623
|
status: "no_samples" | "insufficient_samples" | "reviewable_sample_set";
|
|
3571
3624
|
sampleSize: number;
|
package/dist/index.js
CHANGED
|
@@ -15400,6 +15400,7 @@ var DEFAULT_LOCAL_CODE_MAX_FILES = 2e3;
|
|
|
15400
15400
|
var HOSTED_GUARD_MAX_FILES = 450;
|
|
15401
15401
|
var HOSTED_GUARD_MAX_RESOURCES = 850;
|
|
15402
15402
|
var HOSTED_GUARD_MAX_SYMBOL_RESOURCES = 160;
|
|
15403
|
+
var REVIEW_ONLY_ACK_TTL_MS = 15 * 60 * 1e3;
|
|
15403
15404
|
var HOOK_BLOCK_PREFIX2 = "snipara:collaboration-guard";
|
|
15404
15405
|
var CHECKOUT_GIT_WRITE_RESOURCE = {
|
|
15405
15406
|
kind: "SURFACE",
|
|
@@ -15460,7 +15461,8 @@ function createEmptyCollaborationState(now = /* @__PURE__ */ new Date()) {
|
|
|
15460
15461
|
updatedAt: now.toISOString(),
|
|
15461
15462
|
files: [],
|
|
15462
15463
|
resources: [],
|
|
15463
|
-
leases: []
|
|
15464
|
+
leases: [],
|
|
15465
|
+
reviewOnlyAcknowledgements: []
|
|
15464
15466
|
};
|
|
15465
15467
|
}
|
|
15466
15468
|
function getCollaborationStatePath(rootDir = process.cwd()) {
|
|
@@ -15493,13 +15495,18 @@ function loadCollaborationState(rootDir = process.cwd()) {
|
|
|
15493
15495
|
files: normalizeFiles(parsed.files),
|
|
15494
15496
|
resources: normalizeResources(parsed.resources),
|
|
15495
15497
|
leases: normalizeLocalLeases(parsed.leases),
|
|
15498
|
+
reviewOnlyAcknowledgements: normalizeReviewOnlyAcknowledgements(
|
|
15499
|
+
parsed.reviewOnlyAcknowledgements
|
|
15500
|
+
),
|
|
15496
15501
|
lastGuard: normalizeLastGuard(parsed.lastGuard)
|
|
15497
15502
|
};
|
|
15498
15503
|
expireLocalLeases(state);
|
|
15504
|
+
expireReviewOnlyAcknowledgements(state);
|
|
15499
15505
|
return state;
|
|
15500
15506
|
}
|
|
15501
15507
|
function saveCollaborationState(state, rootDir = process.cwd()) {
|
|
15502
15508
|
expireLocalLeases(state);
|
|
15509
|
+
expireReviewOnlyAcknowledgements(state);
|
|
15503
15510
|
const statePath = getCollaborationStatePath(rootDir);
|
|
15504
15511
|
fs20.mkdirSync(path19.dirname(statePath), { recursive: true });
|
|
15505
15512
|
const tempPath = `${statePath}.${process.pid}.${Date.now()}.tmp`;
|
|
@@ -15814,13 +15821,14 @@ async function collaborationGuardCommand(options) {
|
|
|
15814
15821
|
const context = resolveCollaborationContext(options);
|
|
15815
15822
|
const state = loadCollaborationState(context.rootDir);
|
|
15816
15823
|
const profile = normalizeGuardProfile(options.profile) ?? "edit";
|
|
15824
|
+
const action = normalizeOptionalString3(options.action) ?? profile;
|
|
15817
15825
|
const files = resolveGuardFiles(context, options, profile);
|
|
15818
15826
|
const resources = resolveCommandResources(context, files, options, profile);
|
|
15819
15827
|
ensureFilesOrResources(files, resources, "guard");
|
|
15820
15828
|
const hostedPayload = buildHostedGuardPayload(files, resources);
|
|
15821
15829
|
const hosted = await maybeEvaluateHostedGuard(context, {
|
|
15822
15830
|
workSessionId: options.workSessionId ?? state.workSessionId,
|
|
15823
|
-
action
|
|
15831
|
+
action,
|
|
15824
15832
|
files: hostedPayload.files,
|
|
15825
15833
|
resources: hostedPayload.resources,
|
|
15826
15834
|
persist: options.persist !== false
|
|
@@ -15832,26 +15840,39 @@ async function collaborationGuardCommand(options) {
|
|
|
15832
15840
|
decision: evaluation.decision,
|
|
15833
15841
|
severity: evaluation.severity,
|
|
15834
15842
|
checkedAt: evaluation.evaluatedAt,
|
|
15835
|
-
action
|
|
15843
|
+
action,
|
|
15836
15844
|
resources: evaluation.resources,
|
|
15837
15845
|
conflictCount: evaluation.conflicts.length
|
|
15838
15846
|
};
|
|
15839
15847
|
state.files = mergeStrings(state.files, files);
|
|
15840
15848
|
state.resources = mergeResources(state.resources, evaluation.resources);
|
|
15849
|
+
}
|
|
15850
|
+
const explicitReviewOnlyAck = Boolean(options.enforce) && Boolean(options.ackReviewOnly) && isReviewOnlyGuardEvaluation(evaluation);
|
|
15851
|
+
const findingFingerprint = evaluation && isReviewOnlyGuardEvaluation(evaluation) ? fingerprintReviewOnlyGuardEvaluation(evaluation) : void 0;
|
|
15852
|
+
const persistedReviewOnlyAck = Boolean(options.enforce) && !options.ackReviewOnly && Boolean(
|
|
15853
|
+
findingFingerprint && consumeReviewOnlyAcknowledgement(state, profile, action, findingFingerprint)
|
|
15854
|
+
);
|
|
15855
|
+
if (explicitReviewOnlyAck && findingFingerprint) {
|
|
15856
|
+
persistReviewOnlyAcknowledgement(state, profile, action, findingFingerprint);
|
|
15857
|
+
} else if (evaluation && !persistedReviewOnlyAck) {
|
|
15858
|
+
invalidateReviewOnlyAcknowledgements(state, profile, action);
|
|
15859
|
+
}
|
|
15860
|
+
const reviewOnlyAcknowledged = explicitReviewOnlyAck || persistedReviewOnlyAck;
|
|
15861
|
+
const ackSource = explicitReviewOnlyAck ? "explicit" : persistedReviewOnlyAck ? "persisted" : "none";
|
|
15862
|
+
if (evaluation) {
|
|
15841
15863
|
saveCollaborationState(state, context.rootDir);
|
|
15842
15864
|
}
|
|
15843
|
-
const reviewOnlyAcknowledged = Boolean(options.enforce) && Boolean(options.ackReviewOnly) && isReviewOnlyGuardEvaluation(evaluation);
|
|
15844
15865
|
const actionCards = buildCollaborationGuardActionCards(evaluation, {
|
|
15845
15866
|
profile,
|
|
15846
15867
|
enforced: Boolean(options.enforce),
|
|
15847
|
-
ackReviewOnly:
|
|
15868
|
+
ackReviewOnly: reviewOnlyAcknowledged,
|
|
15848
15869
|
reviewOnlyAcknowledged
|
|
15849
15870
|
});
|
|
15850
15871
|
const guardFailed = shouldFailCollaborationGuard({
|
|
15851
15872
|
hostedStatus: hosted.status,
|
|
15852
15873
|
evaluation,
|
|
15853
15874
|
enforce: Boolean(options.enforce),
|
|
15854
|
-
ackReviewOnly:
|
|
15875
|
+
ackReviewOnly: reviewOnlyAcknowledged
|
|
15855
15876
|
});
|
|
15856
15877
|
if (guardFailed) {
|
|
15857
15878
|
process.exitCode = 2;
|
|
@@ -15867,6 +15888,7 @@ async function collaborationGuardCommand(options) {
|
|
|
15867
15888
|
enforced: Boolean(options.enforce),
|
|
15868
15889
|
ackReviewOnly: Boolean(options.ackReviewOnly),
|
|
15869
15890
|
reviewOnlyAcknowledged,
|
|
15891
|
+
ackSource,
|
|
15870
15892
|
failed: guardFailed
|
|
15871
15893
|
}
|
|
15872
15894
|
},
|
|
@@ -16036,7 +16058,8 @@ async function collaborationIdeStatusCommand(options) {
|
|
|
16036
16058
|
console.log(`Active leases: ${payload.hosted.activeLeases.length}`);
|
|
16037
16059
|
}
|
|
16038
16060
|
function resolveCollaborationContext(options) {
|
|
16039
|
-
const
|
|
16061
|
+
const requestedDir = normalizeOptionalString3(options.dir) ?? normalizeOptionalString3(process.env.SNIPARA_WORKSPACE_DIR) ?? process.cwd();
|
|
16062
|
+
const rootDir = resolveRepoRoot3(requestedDir);
|
|
16040
16063
|
const config = loadConfig({ cwd: rootDir });
|
|
16041
16064
|
const actor = buildCollaborationActor(options, config);
|
|
16042
16065
|
return {
|
|
@@ -17138,6 +17161,68 @@ function normalizeLocalLeases(value) {
|
|
|
17138
17161
|
return lease;
|
|
17139
17162
|
}).filter((item) => Boolean(item));
|
|
17140
17163
|
}
|
|
17164
|
+
function normalizeReviewOnlyAcknowledgements(value, now = /* @__PURE__ */ new Date()) {
|
|
17165
|
+
if (!Array.isArray(value)) {
|
|
17166
|
+
return [];
|
|
17167
|
+
}
|
|
17168
|
+
const nowMs = now.getTime();
|
|
17169
|
+
return value.map((item) => {
|
|
17170
|
+
const parsed = item;
|
|
17171
|
+
const profile = normalizeGuardProfile(parsed.profile);
|
|
17172
|
+
const action = normalizeOptionalString3(parsed.action);
|
|
17173
|
+
const findingFingerprint = normalizeOptionalString3(parsed.findingFingerprint);
|
|
17174
|
+
const acknowledgedAt = normalizeOptionalString3(parsed.acknowledgedAt);
|
|
17175
|
+
const expiresAt = normalizeOptionalString3(parsed.expiresAt);
|
|
17176
|
+
if (!profile || !action || !findingFingerprint || !acknowledgedAt || !expiresAt || !Number.isFinite(Date.parse(expiresAt)) || Date.parse(expiresAt) <= nowMs) {
|
|
17177
|
+
return null;
|
|
17178
|
+
}
|
|
17179
|
+
return { profile, action, findingFingerprint, acknowledgedAt, expiresAt };
|
|
17180
|
+
}).filter((item) => Boolean(item));
|
|
17181
|
+
}
|
|
17182
|
+
function expireReviewOnlyAcknowledgements(state, now = /* @__PURE__ */ new Date()) {
|
|
17183
|
+
state.reviewOnlyAcknowledgements = normalizeReviewOnlyAcknowledgements(
|
|
17184
|
+
state.reviewOnlyAcknowledgements,
|
|
17185
|
+
now
|
|
17186
|
+
);
|
|
17187
|
+
}
|
|
17188
|
+
function fingerprintReviewOnlyGuardEvaluation(evaluation) {
|
|
17189
|
+
const findings = evaluation.conflicts.map((conflict) => ({
|
|
17190
|
+
code: conflict.code,
|
|
17191
|
+
decision: conflict.decision,
|
|
17192
|
+
severity: conflict.severity,
|
|
17193
|
+
resource: `${conflict.resource.kind}:${conflict.resource.id}`,
|
|
17194
|
+
actor: conflict.conflictingActor ? `${conflict.conflictingActor.actorType}:${conflict.conflictingActor.actorId}` : null,
|
|
17195
|
+
workSessionId: conflict.workSessionId ?? null,
|
|
17196
|
+
reason: conflict.reason
|
|
17197
|
+
})).sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
17198
|
+
return (0, import_node_crypto6.createHash)("sha256").update(JSON.stringify(findings)).digest("hex");
|
|
17199
|
+
}
|
|
17200
|
+
function persistReviewOnlyAcknowledgement(state, profile, action, findingFingerprint, now = /* @__PURE__ */ new Date()) {
|
|
17201
|
+
invalidateReviewOnlyAcknowledgements(state, profile, action);
|
|
17202
|
+
state.reviewOnlyAcknowledgements.push({
|
|
17203
|
+
profile,
|
|
17204
|
+
action,
|
|
17205
|
+
findingFingerprint,
|
|
17206
|
+
acknowledgedAt: now.toISOString(),
|
|
17207
|
+
expiresAt: new Date(now.getTime() + REVIEW_ONLY_ACK_TTL_MS).toISOString()
|
|
17208
|
+
});
|
|
17209
|
+
}
|
|
17210
|
+
function consumeReviewOnlyAcknowledgement(state, profile, action, findingFingerprint) {
|
|
17211
|
+
expireReviewOnlyAcknowledgements(state);
|
|
17212
|
+
const index = state.reviewOnlyAcknowledgements.findIndex(
|
|
17213
|
+
(ack) => ack.profile === profile && ack.action === action && ack.findingFingerprint === findingFingerprint
|
|
17214
|
+
);
|
|
17215
|
+
if (index < 0) {
|
|
17216
|
+
return false;
|
|
17217
|
+
}
|
|
17218
|
+
state.reviewOnlyAcknowledgements.splice(index, 1);
|
|
17219
|
+
return true;
|
|
17220
|
+
}
|
|
17221
|
+
function invalidateReviewOnlyAcknowledgements(state, profile, action) {
|
|
17222
|
+
state.reviewOnlyAcknowledgements = state.reviewOnlyAcknowledgements.filter(
|
|
17223
|
+
(ack) => ack.profile !== profile || ack.action !== action
|
|
17224
|
+
);
|
|
17225
|
+
}
|
|
17141
17226
|
function normalizeLastGuard(value) {
|
|
17142
17227
|
const parsed = value;
|
|
17143
17228
|
if (!parsed || !parsed.decision || !parsed.checkedAt) {
|
|
@@ -21549,6 +21634,154 @@ function producerLoopCalibrationStatus(sampleSize, reviewedSampleSize, minReview
|
|
|
21549
21634
|
}
|
|
21550
21635
|
return "reviewable_sample_set";
|
|
21551
21636
|
}
|
|
21637
|
+
var WORKER_RECEIPT_RELATIVE_DIR = path22.join(".snipara", "orchestrator", "executions");
|
|
21638
|
+
var WORKER_REVIEW_RELATIVE_DIR = path22.join(".snipara", "orchestrator", "reviews");
|
|
21639
|
+
var REQUIRED_WORKER_RECEIPT_FAMILIES = [
|
|
21640
|
+
"handoffReceiptId",
|
|
21641
|
+
"claimId",
|
|
21642
|
+
"proofReceiptIds",
|
|
21643
|
+
"outcomeReceiptId",
|
|
21644
|
+
"brainUpdateReceiptId"
|
|
21645
|
+
];
|
|
21646
|
+
function listJsonFiles(relativeDir, cwd) {
|
|
21647
|
+
const directory = path22.join(cwd, relativeDir);
|
|
21648
|
+
if (!fs23.existsSync(directory)) {
|
|
21649
|
+
return [];
|
|
21650
|
+
}
|
|
21651
|
+
return fs23.readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => path22.join(directory, entry.name)).sort();
|
|
21652
|
+
}
|
|
21653
|
+
function readWorkerReviewFiles(cwd) {
|
|
21654
|
+
const reviews = /* @__PURE__ */ new Map();
|
|
21655
|
+
const invalid = [];
|
|
21656
|
+
for (const filePath of listJsonFiles(WORKER_REVIEW_RELATIVE_DIR, cwd)) {
|
|
21657
|
+
try {
|
|
21658
|
+
const parsed = JSON.parse(fs23.readFileSync(filePath, "utf-8"));
|
|
21659
|
+
if (!isRecord10(parsed)) {
|
|
21660
|
+
throw new Error("review must be a JSON object");
|
|
21661
|
+
}
|
|
21662
|
+
const receiptId = stringValue7(parsed.receiptId);
|
|
21663
|
+
if (!receiptId) {
|
|
21664
|
+
throw new Error("review is missing receiptId");
|
|
21665
|
+
}
|
|
21666
|
+
reviews.set(receiptId, { value: parsed, path: filePath });
|
|
21667
|
+
} catch (error) {
|
|
21668
|
+
invalid.push({
|
|
21669
|
+
path: filePath,
|
|
21670
|
+
relativePath: toProjectRelativePath3(filePath, cwd),
|
|
21671
|
+
error: error instanceof Error ? error.message : String(error)
|
|
21672
|
+
});
|
|
21673
|
+
}
|
|
21674
|
+
}
|
|
21675
|
+
return { reviews, invalid };
|
|
21676
|
+
}
|
|
21677
|
+
function missingWorkerReceiptFamilies(receipt) {
|
|
21678
|
+
const refs = isRecord10(receipt.receiptRefs) ? receipt.receiptRefs : isRecord10(receipt.receipt) ? receipt.receipt : {};
|
|
21679
|
+
return REQUIRED_WORKER_RECEIPT_FAMILIES.filter((family) => {
|
|
21680
|
+
const value = refs[family];
|
|
21681
|
+
return family === "proofReceiptIds" ? !Array.isArray(value) || value.filter((item) => Boolean(stringValue7(item))).length === 0 : !stringValue7(value);
|
|
21682
|
+
});
|
|
21683
|
+
}
|
|
21684
|
+
function summarizeWorkerExecutionReceipts(cwd) {
|
|
21685
|
+
const reviewFiles = readWorkerReviewFiles(cwd);
|
|
21686
|
+
const invalid = [...reviewFiles.invalid];
|
|
21687
|
+
const samples = [];
|
|
21688
|
+
for (const filePath of listJsonFiles(WORKER_RECEIPT_RELATIVE_DIR, cwd)) {
|
|
21689
|
+
try {
|
|
21690
|
+
const parsed = JSON.parse(fs23.readFileSync(filePath, "utf-8"));
|
|
21691
|
+
if (!isRecord10(parsed)) {
|
|
21692
|
+
throw new Error("receipt must be a JSON object");
|
|
21693
|
+
}
|
|
21694
|
+
const receiptId = stringValue7(parsed.receiptId);
|
|
21695
|
+
const schemaVersion = stringValue7(parsed.schemaVersion);
|
|
21696
|
+
if (!receiptId || !schemaVersion) {
|
|
21697
|
+
throw new Error("receipt is missing receiptId or schemaVersion");
|
|
21698
|
+
}
|
|
21699
|
+
const attribution = isRecord10(parsed.workerAttribution) ? parsed.workerAttribution : {};
|
|
21700
|
+
const gate2 = isRecord10(parsed.gate) ? parsed.gate : {};
|
|
21701
|
+
const review = reviewFiles.reviews.get(receiptId);
|
|
21702
|
+
const reviewStatusValue = stringValue7(review?.value.reviewStatus);
|
|
21703
|
+
const reviewStatus = reviewStatusValue === "accepted" ? "accepted" : reviewStatusValue === "blocked" ? "blocked" : "review_pending";
|
|
21704
|
+
const workerId = stringValue7(parsed.workerId) ?? stringValue7(attribution.workerId) ?? stringValue7(parsed.selectedWorkerCandidateId) ?? stringValue7(gate2.selectedWorkerCandidateId) ?? "main_agent";
|
|
21705
|
+
const workCategory = stringValue7(parsed.workCategory) ?? stringValue7(attribution.workCategory) ?? "unknown";
|
|
21706
|
+
const executionActor = stringValue7(parsed.executionActor) ?? stringValue7(attribution.executionActor) ?? (Number(parsed.workersSpawned ?? 0) > 0 ? "worker" : "main_agent");
|
|
21707
|
+
const missingReceiptFamilies = missingWorkerReceiptFamilies(parsed);
|
|
21708
|
+
samples.push({
|
|
21709
|
+
receiptId,
|
|
21710
|
+
schemaVersion,
|
|
21711
|
+
recordedAt: stringValue7(parsed.recordedAt),
|
|
21712
|
+
workerId,
|
|
21713
|
+
workCategory,
|
|
21714
|
+
routingCardRef: stringValue7(parsed.routingCardRef) ?? stringValue7(attribution.routingCardRef),
|
|
21715
|
+
workflowFingerprint: stringValue7(parsed.workflowFingerprint) ?? stringValue7(attribution.workflowFingerprint),
|
|
21716
|
+
executionActor,
|
|
21717
|
+
status: stringValue7(parsed.status),
|
|
21718
|
+
reviewStatus,
|
|
21719
|
+
executed: executionActor === "worker" || Number(parsed.workersSpawned ?? 0) > 0,
|
|
21720
|
+
receiptFamilyComplete: missingReceiptFamilies.length === 0,
|
|
21721
|
+
missingReceiptFamilies,
|
|
21722
|
+
path: filePath,
|
|
21723
|
+
relativePath: toProjectRelativePath3(filePath, cwd),
|
|
21724
|
+
...review ? {
|
|
21725
|
+
reviewPath: review.path,
|
|
21726
|
+
reviewRelativePath: toProjectRelativePath3(review.path, cwd)
|
|
21727
|
+
} : {}
|
|
21728
|
+
});
|
|
21729
|
+
} catch (error) {
|
|
21730
|
+
invalid.push({
|
|
21731
|
+
path: filePath,
|
|
21732
|
+
relativePath: toProjectRelativePath3(filePath, cwd),
|
|
21733
|
+
error: error instanceof Error ? error.message : String(error)
|
|
21734
|
+
});
|
|
21735
|
+
}
|
|
21736
|
+
}
|
|
21737
|
+
samples.sort(
|
|
21738
|
+
(left, right) => (left.recordedAt ?? left.receiptId).localeCompare(right.recordedAt ?? right.receiptId)
|
|
21739
|
+
);
|
|
21740
|
+
return { samples, invalid };
|
|
21741
|
+
}
|
|
21742
|
+
function buildWorkerTrustRows(samples, minReviewSampleSize) {
|
|
21743
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
21744
|
+
for (const sample of samples) {
|
|
21745
|
+
const key = `${sample.workerId}\0${sample.workCategory}`;
|
|
21746
|
+
grouped.set(key, [...grouped.get(key) ?? [], sample]);
|
|
21747
|
+
}
|
|
21748
|
+
return [...grouped.values()].map((group) => {
|
|
21749
|
+
const reviewedSampleSize = group.filter(
|
|
21750
|
+
(sample) => sample.reviewStatus !== "review_pending"
|
|
21751
|
+
).length;
|
|
21752
|
+
const verifiedSampleSize = group.filter(
|
|
21753
|
+
(sample) => sample.reviewStatus === "accepted"
|
|
21754
|
+
).length;
|
|
21755
|
+
const incompleteReceiptSampleSize = group.filter(
|
|
21756
|
+
(sample) => !sample.receiptFamilyComplete
|
|
21757
|
+
).length;
|
|
21758
|
+
const nextRequired = [
|
|
21759
|
+
reviewedSampleSize < minReviewSampleSize ? `${minReviewSampleSize - reviewedSampleSize} more supervised review sample(s)` : void 0,
|
|
21760
|
+
verifiedSampleSize < 3 ? `${3 - verifiedSampleSize} more accepted verified sample(s)` : void 0,
|
|
21761
|
+
incompleteReceiptSampleSize > 0 ? `${incompleteReceiptSampleSize} sample(s) need complete receipt families` : void 0,
|
|
21762
|
+
group[0].workCategory === "unknown" ? "classify legacy samples by work category" : void 0,
|
|
21763
|
+
"Trust Promotion gate implementation remains required"
|
|
21764
|
+
].filter((item) => Boolean(item));
|
|
21765
|
+
return {
|
|
21766
|
+
workerId: group[0].workerId,
|
|
21767
|
+
workCategory: group[0].workCategory,
|
|
21768
|
+
state: "probation_supervised",
|
|
21769
|
+
sampleSize: group.length,
|
|
21770
|
+
executedSampleSize: group.filter((sample) => sample.executed).length,
|
|
21771
|
+
reviewedSampleSize,
|
|
21772
|
+
verifiedSampleSize,
|
|
21773
|
+
blockedSampleSize: group.filter((sample) => sample.reviewStatus === "blocked").length,
|
|
21774
|
+
incompleteReceiptSampleSize,
|
|
21775
|
+
workflowFingerprints: uniqueStrings18(
|
|
21776
|
+
group.map((sample) => sample.workflowFingerprint).filter((value) => Boolean(value))
|
|
21777
|
+
),
|
|
21778
|
+
hardGateReady: false,
|
|
21779
|
+
nextRequired
|
|
21780
|
+
};
|
|
21781
|
+
}).sort(
|
|
21782
|
+
(left, right) => left.workerId === right.workerId ? left.workCategory.localeCompare(right.workCategory) : left.workerId.localeCompare(right.workerId)
|
|
21783
|
+
);
|
|
21784
|
+
}
|
|
21552
21785
|
function buildProducerLoopReport(options = {}) {
|
|
21553
21786
|
const cwd = path22.resolve(options.cwd ?? process.cwd());
|
|
21554
21787
|
const minReviewSampleSize = Math.max(
|
|
@@ -21589,12 +21822,16 @@ function buildProducerLoopReport(options = {}) {
|
|
|
21589
21822
|
reviewedSampleSize,
|
|
21590
21823
|
minReviewSampleSize
|
|
21591
21824
|
);
|
|
21825
|
+
const workerReceiptReport = summarizeWorkerExecutionReceipts(cwd);
|
|
21826
|
+
const workerTrust = buildWorkerTrustRows(workerReceiptReport.samples, minReviewSampleSize);
|
|
21592
21827
|
const recommendedActions = [
|
|
21593
21828
|
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,
|
|
21594
21829
|
artifacts.length > 0 && reviewedSampleSize < minReviewSampleSize ? `Review at least ${minReviewSampleSize} local Producer Loop samples before considering any calibration signal.` : void 0,
|
|
21595
21830
|
unreviewedSampleSize > 0 ? "Mark local samples with workflow producer-review after checking the embedded evidence." : void 0,
|
|
21596
21831
|
rejectedSampleSize > 0 ? "Inspect rejected Producer Loop samples for false positives, missing context, or reason-code drift." : void 0,
|
|
21597
21832
|
invalidArtifacts.length > 0 ? "Inspect invalid Producer Loop artifacts before trusting adoption counts." : void 0,
|
|
21833
|
+
workerReceiptReport.samples.length === 0 ? "Run agents execute-gated and persist agents review-gated results to create worker-attributed receipt samples." : void 0,
|
|
21834
|
+
workerReceiptReport.invalid.length > 0 ? "Inspect invalid worker execution receipts or reviews before trusting per-worker counts." : void 0,
|
|
21598
21835
|
"Review false positives, missing outcomes, and reason-code drift before any future enforcement."
|
|
21599
21836
|
].filter((item) => Boolean(item));
|
|
21600
21837
|
return {
|
|
@@ -21616,6 +21853,13 @@ function buildProducerLoopReport(options = {}) {
|
|
|
21616
21853
|
reasonCodes: {
|
|
21617
21854
|
counts: reasonCodeCounts
|
|
21618
21855
|
},
|
|
21856
|
+
workerReceipts: {
|
|
21857
|
+
sourceDirectories: [WORKER_RECEIPT_RELATIVE_DIR, WORKER_REVIEW_RELATIVE_DIR],
|
|
21858
|
+
sampleSize: workerReceiptReport.samples.length,
|
|
21859
|
+
samples: workerReceiptReport.samples,
|
|
21860
|
+
invalidArtifacts: workerReceiptReport.invalid
|
|
21861
|
+
},
|
|
21862
|
+
workerTrust,
|
|
21619
21863
|
calibration: {
|
|
21620
21864
|
status: calibrationStatus,
|
|
21621
21865
|
sampleSize: artifacts.length,
|
|
@@ -21815,6 +22059,19 @@ function printProducerLoopReport(report) {
|
|
|
21815
22059
|
if (report.invalidArtifacts.length > 0) {
|
|
21816
22060
|
printKeyValue2("Invalid artifacts:", report.invalidArtifacts.length);
|
|
21817
22061
|
}
|
|
22062
|
+
printKeyValue2("Worker receipts:", report.workerReceipts.sampleSize);
|
|
22063
|
+
if (report.workerReceipts.invalidArtifacts.length > 0) {
|
|
22064
|
+
printKeyValue2("Invalid worker evidence:", report.workerReceipts.invalidArtifacts.length);
|
|
22065
|
+
}
|
|
22066
|
+
if (report.workerTrust.length > 0) {
|
|
22067
|
+
console.log("");
|
|
22068
|
+
console.log(import_chalk6.default.bold("Worker Trust (supervised probation)"));
|
|
22069
|
+
for (const row of report.workerTrust) {
|
|
22070
|
+
console.log(
|
|
22071
|
+
`- ${row.workerId} / ${row.workCategory}: ${row.verifiedSampleSize} accepted, ${row.blockedSampleSize} blocked, ${row.reviewedSampleSize}/${row.sampleSize} reviewed`
|
|
22072
|
+
);
|
|
22073
|
+
}
|
|
22074
|
+
}
|
|
21818
22075
|
if (report.recommendedActions.length > 0) {
|
|
21819
22076
|
console.log("");
|
|
21820
22077
|
console.log(import_chalk6.default.bold("Recommended Actions"));
|
|
@@ -33407,7 +33664,7 @@ function applyLocalContextMutationPlan(options) {
|
|
|
33407
33664
|
writtenFiles
|
|
33408
33665
|
};
|
|
33409
33666
|
}
|
|
33410
|
-
function
|
|
33667
|
+
function listJsonFiles2(dir) {
|
|
33411
33668
|
if (!fs30.existsSync(dir)) return [];
|
|
33412
33669
|
return fs30.readdirSync(dir).filter((name) => name.endsWith(".json")).map((name) => path30.join(dir, name)).sort((left, right) => left.localeCompare(right));
|
|
33413
33670
|
}
|
|
@@ -33534,7 +33791,7 @@ function collectWorkflowDriftSignals(cwd) {
|
|
|
33534
33791
|
}
|
|
33535
33792
|
function collectDecisionDriftSignals(cwd) {
|
|
33536
33793
|
const pendingDir = path30.join(cwd, ".snipara", "decisions", "pending");
|
|
33537
|
-
const pending =
|
|
33794
|
+
const pending = listJsonFiles2(pendingDir).map((file) => toProjectRelativePath4(file, cwd));
|
|
33538
33795
|
return [
|
|
33539
33796
|
pending.length > 0 ? {
|
|
33540
33797
|
id: "decision-requests-pending",
|
|
@@ -33558,8 +33815,8 @@ function collectDecisionDriftSignals(cwd) {
|
|
|
33558
33815
|
];
|
|
33559
33816
|
}
|
|
33560
33817
|
function collectContextControlDriftSignals(cwd) {
|
|
33561
|
-
const planFiles =
|
|
33562
|
-
const receiptFiles =
|
|
33818
|
+
const planFiles = listJsonFiles2(path30.join(cwd, CONTEXT_CONTROL_PLANS_RELATIVE_DIR));
|
|
33819
|
+
const receiptFiles = listJsonFiles2(path30.join(cwd, CONTEXT_CONTROL_APPLIED_RELATIVE_DIR));
|
|
33563
33820
|
const receiptPlanHashes = new Set(
|
|
33564
33821
|
receiptFiles.map((file) => readJsonFileSafe(file).value).filter(isContextMutationApplyReceipt).map((receipt) => receipt.planHash)
|
|
33565
33822
|
);
|
|
@@ -37325,7 +37582,7 @@ collaboration.command("guard").description("Check files or resources against act
|
|
|
37325
37582
|
"edit"
|
|
37326
37583
|
).option("-a, --action <action>", "Guarded action label", "edit").option("--actor <actor>", "Developer or agent display name").option("--actor-id <actorId>", "Stable developer or agent id").option("--actor-type <actorType>", "HUMAN|AGENT|SYSTEM", "AGENT").option("--session-id <sessionId>", "Automation/session id").option("--work-session-id <workSessionId>", "Existing hosted work session id").option("--client <client>", "Client label", "snipara-companion").option("--repository <repository>", "Repository id").option("-b, --branch <branch>", "Current branch name").option("--worktree <worktree>", "Worktree path").option("--max-files <number>", "Maximum files for local code resource expansion", "2000").option("--no-persist", "Evaluate without storing a guard event").option("--enforce", "Exit non-zero for REVIEW_REQUIRED or REQUIRES_ACK, not only BLOCKED").option(
|
|
37327
37584
|
"--ack-review-only",
|
|
37328
|
-
"Under --enforce,
|
|
37585
|
+
"Under --enforce, persist one exact review-only acknowledgement for the next hook rerun"
|
|
37329
37586
|
).option("-d, --dir <directory>", "Repository directory (default: current)").option("--json", "Print raw JSON").option("--verbose", "Print the full guard report even when the guard passes").action(async (options) => {
|
|
37330
37587
|
await collaborationGuardCommand({
|
|
37331
37588
|
files: options.files,
|
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
|