snipara-companion 3.5.8 → 3.5.9
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 +8 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +114 -31
- package/docs/FULL_REFERENCE.md +11 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Release notes for `snipara-companion`, newest first.
|
|
4
4
|
|
|
5
|
+
## New In 3.5.9
|
|
6
|
+
|
|
7
|
+
- Routes explicit `workflow phase-commit` and `final-commit` rationale through
|
|
8
|
+
the hosted atomic `why` block instead of issuing a second Why Capture request.
|
|
9
|
+
- Exposes decision, rationale, alternatives, constraints, and observed outcome
|
|
10
|
+
inputs on workflow commits while keeping candidates review-pending.
|
|
11
|
+
- Preserves handoff-only final commits when no structured rationale is supplied.
|
|
12
|
+
|
|
5
13
|
## New In 3.5.8
|
|
6
14
|
|
|
7
15
|
- Preserves the exact collaboration actor identity on hosted lease heartbeat
|
package/dist/index.d.ts
CHANGED
|
@@ -519,6 +519,13 @@ interface WhyCaptureResult {
|
|
|
519
519
|
failed?: Array<Record<string, unknown>>;
|
|
520
520
|
};
|
|
521
521
|
}
|
|
522
|
+
interface EndOfTaskCommitWhyInput {
|
|
523
|
+
decision?: string;
|
|
524
|
+
rationale?: string;
|
|
525
|
+
alternatives?: string[];
|
|
526
|
+
constraints?: string[];
|
|
527
|
+
observedOutcome?: string;
|
|
528
|
+
}
|
|
522
529
|
interface JournalAppendResult {
|
|
523
530
|
success?: boolean;
|
|
524
531
|
date?: string;
|
|
@@ -1516,6 +1523,8 @@ declare class RLMClient {
|
|
|
1516
1523
|
getSessionMemories(maxCriticalTokens?: number, maxDailyTokens?: number, includeYesterday?: boolean): Promise<SessionMemoriesResult>;
|
|
1517
1524
|
endOfTaskCommit(args: {
|
|
1518
1525
|
summary: string;
|
|
1526
|
+
task?: string;
|
|
1527
|
+
why?: EndOfTaskCommitWhyInput;
|
|
1519
1528
|
category?: string;
|
|
1520
1529
|
outcome?: "completed" | "partial" | "blocked" | "abandoned";
|
|
1521
1530
|
filesTouched?: string[];
|
package/dist/index.js
CHANGED
|
@@ -1724,6 +1724,16 @@ var RLMClient = class {
|
|
|
1724
1724
|
async endOfTaskCommit(args) {
|
|
1725
1725
|
return this.mcpCall("snipara_end_of_task_commit", {
|
|
1726
1726
|
summary: args.summary,
|
|
1727
|
+
...args.task ? { task: args.task } : {},
|
|
1728
|
+
...args.why ? {
|
|
1729
|
+
why: {
|
|
1730
|
+
...args.why.decision ? { decision: args.why.decision } : {},
|
|
1731
|
+
...args.why.rationale ? { rationale: args.why.rationale } : {},
|
|
1732
|
+
...args.why.alternatives ? { alternatives: args.why.alternatives } : {},
|
|
1733
|
+
...args.why.constraints ? { constraints: args.why.constraints } : {},
|
|
1734
|
+
...args.why.observedOutcome ? { observed_outcome: args.why.observedOutcome } : {}
|
|
1735
|
+
}
|
|
1736
|
+
} : {},
|
|
1727
1737
|
category: args.category,
|
|
1728
1738
|
outcome: args.outcome || "completed",
|
|
1729
1739
|
files_touched: args.filesTouched || [],
|
|
@@ -17901,16 +17911,16 @@ function previewCandidateReceipt(value) {
|
|
|
17901
17911
|
if (!isRecord9(value)) {
|
|
17902
17912
|
return void 0;
|
|
17903
17913
|
}
|
|
17904
|
-
const whyFields = isRecord9(value.whyFields) ? value.whyFields : void 0;
|
|
17914
|
+
const whyFields = isRecord9(value.whyFields) ? value.whyFields : isRecord9(value.why_fields) ? value.why_fields : void 0;
|
|
17905
17915
|
const decision = boundedText(whyFields?.decision);
|
|
17906
|
-
const rationale = boundedText(whyFields?.why);
|
|
17907
|
-
const text = boundedText(value.content) ?? decision;
|
|
17916
|
+
const rationale = boundedText(whyFields?.why ?? whyFields?.rationale);
|
|
17917
|
+
const text = boundedText(value.content ?? value.text) ?? decision;
|
|
17908
17918
|
if (!text) {
|
|
17909
17919
|
return void 0;
|
|
17910
17920
|
}
|
|
17911
17921
|
return {
|
|
17912
17922
|
text,
|
|
17913
|
-
...boundedText(value.type, 80) ? { type: boundedText(value.type, 80) } : {},
|
|
17923
|
+
...boundedText(value.type ?? value.memory_type, 80) ? { type: boundedText(value.type ?? value.memory_type, 80) } : {},
|
|
17914
17924
|
...boundedText(value.category, 120) ? { category: boundedText(value.category, 120) } : {},
|
|
17915
17925
|
...decision ? { decision } : {},
|
|
17916
17926
|
...rationale ? { rationale } : {}
|
|
@@ -17921,23 +17931,25 @@ function pendingMemoryReceipt(value) {
|
|
|
17921
17931
|
return void 0;
|
|
17922
17932
|
}
|
|
17923
17933
|
const memoryId = boundedText(value.memory_id ?? value.memoryId ?? value.id, 200);
|
|
17924
|
-
const text = boundedText(value.content) ?? (memoryId ? `Memory ${memoryId}` : void 0);
|
|
17934
|
+
const text = boundedText(value.content ?? value.text) ?? (memoryId ? `Memory ${memoryId}` : void 0);
|
|
17925
17935
|
if (!text) {
|
|
17926
17936
|
return void 0;
|
|
17927
17937
|
}
|
|
17928
17938
|
return {
|
|
17929
17939
|
...memoryId ? { memoryId } : {},
|
|
17930
17940
|
text,
|
|
17931
|
-
...boundedText(value.type, 80) ? { type: boundedText(value.type, 80) } : {},
|
|
17932
|
-
|
|
17933
|
-
reviewStatus: boundedText(value.review_status ?? value.reviewStatus, 80) ?? "
|
|
17941
|
+
...boundedText(value.type ?? value.memory_type, 80) ? { type: boundedText(value.type ?? value.memory_type, 80) } : {},
|
|
17942
|
+
category: boundedText(value.category, 120) ?? "why-capture",
|
|
17943
|
+
reviewStatus: boundedText(value.review_status ?? value.reviewStatus, 80) ?? "PENDING"
|
|
17934
17944
|
};
|
|
17935
17945
|
}
|
|
17936
17946
|
function issueReceipt(value, fallbackReason) {
|
|
17937
17947
|
if (!isRecord9(value)) {
|
|
17938
17948
|
return void 0;
|
|
17939
17949
|
}
|
|
17940
|
-
const text = boundedText(
|
|
17950
|
+
const text = boundedText(
|
|
17951
|
+
value.content ?? value.decision ?? value.title ?? value.error ?? value.reason ?? value.decision_id ?? value.id
|
|
17952
|
+
) ?? "Why Capture item";
|
|
17941
17953
|
const reason = boundedText(value.reason ?? value.error, 160) ?? fallbackReason;
|
|
17942
17954
|
return { text, ...reason ? { reason } : {} };
|
|
17943
17955
|
}
|
|
@@ -17975,6 +17987,47 @@ function readLatestWorkflowCommands(cwd) {
|
|
|
17975
17987
|
}
|
|
17976
17988
|
return [];
|
|
17977
17989
|
}
|
|
17990
|
+
function recordItems(value) {
|
|
17991
|
+
return Array.isArray(value) ? value.filter(isRecord9) : [];
|
|
17992
|
+
}
|
|
17993
|
+
function taskCommitWhyCaptureReceipt(result, sourceKind, cwd = process.cwd()) {
|
|
17994
|
+
const commitSha = readCommitSha(cwd);
|
|
17995
|
+
const candidates = recordItems(result.candidates).filter(
|
|
17996
|
+
(candidate) => isRecord9(candidate.why_fields) || isRecord9(candidate.whyFields)
|
|
17997
|
+
);
|
|
17998
|
+
const storedCandidates = recordItems(result.stored_candidates).filter(
|
|
17999
|
+
(candidate) => isRecord9(candidate.why_fields) || isRecord9(candidate.whyFields)
|
|
18000
|
+
);
|
|
18001
|
+
const decisionCapture = isRecord9(result.decision_capture) ? result.decision_capture : {};
|
|
18002
|
+
const duplicates = recordItems(decisionCapture.duplicates).map((item) => issueReceipt(item, "duplicate")).filter((item) => Boolean(item));
|
|
18003
|
+
const failed = recordItems(decisionCapture.failed).map((item) => issueReceipt(item, "capture_failed")).filter((item) => Boolean(item));
|
|
18004
|
+
const caveats = Array.isArray(result.caveats) ? result.caveats.filter((item) => typeof item === "string") : [];
|
|
18005
|
+
const hostedPhaseCommit = isRecord9(result.hosted_phase_commit) ? result.hosted_phase_commit : void 0;
|
|
18006
|
+
const hostedFinalCommit = isRecord9(result.hosted_final_commit) ? result.hosted_final_commit : void 0;
|
|
18007
|
+
const hostedError = boundedText(hostedPhaseCommit?.error ?? hostedFinalCommit?.error);
|
|
18008
|
+
const previewCandidates = candidates.map(previewCandidateReceipt).filter((item) => Boolean(item));
|
|
18009
|
+
const pendingMemories = storedCandidates.map(pendingMemoryReceipt).filter((item) => Boolean(item));
|
|
18010
|
+
const baseReceipt = {
|
|
18011
|
+
sourceKind,
|
|
18012
|
+
previewCandidateCount: previewCandidates.length,
|
|
18013
|
+
capturedCount: pendingMemories.length,
|
|
18014
|
+
previewCandidates,
|
|
18015
|
+
pendingMemories,
|
|
18016
|
+
duplicates,
|
|
18017
|
+
failed,
|
|
18018
|
+
...commitSha ? { commitSha } : {}
|
|
18019
|
+
};
|
|
18020
|
+
if (hostedError) {
|
|
18021
|
+
return { ...baseReceipt, status: "error", error: hostedError };
|
|
18022
|
+
}
|
|
18023
|
+
if (caveats.includes("why_requires_human_owner")) {
|
|
18024
|
+
return { ...baseReceipt, status: "skipped", error: "why_requires_human_owner" };
|
|
18025
|
+
}
|
|
18026
|
+
if (candidates.length === 0) {
|
|
18027
|
+
return { ...baseReceipt, status: "no_candidates" };
|
|
18028
|
+
}
|
|
18029
|
+
return { ...baseReceipt, status: "captured" };
|
|
18030
|
+
}
|
|
17978
18031
|
async function captureCompanionWhy(input) {
|
|
17979
18032
|
const cwd = input.cwd ?? process.cwd();
|
|
17980
18033
|
const commitSha = readCommitSha(cwd);
|
|
@@ -20739,7 +20792,7 @@ function recordList(value) {
|
|
|
20739
20792
|
return Array.isArray(value) ? value.filter(isRecord11) : [];
|
|
20740
20793
|
}
|
|
20741
20794
|
function buildWorkflowPhaseCommitReceipt(input) {
|
|
20742
|
-
const stored = recordList(input.result.stored_candidates).map((item) => normalizedMemoryItem(item, "phase_commit", input.phaseId)).filter((item) => Boolean(item));
|
|
20795
|
+
const stored = recordList(input.result.stored_candidates).filter((item) => !isRecord11(item.why_fields) && !isRecord11(item.whyFields)).map((item) => normalizedMemoryItem(item, "phase_commit", input.phaseId)).filter((item) => Boolean(item));
|
|
20743
20796
|
const skipped = recordList(input.result.skipped_candidates).map((item) => normalizedMemoryItem(item, "phase_commit", input.phaseId)).filter((item) => Boolean(item));
|
|
20744
20797
|
const hostedPhaseCommit = isRecord11(input.result.hosted_phase_commit) ? input.result.hosted_phase_commit : void 0;
|
|
20745
20798
|
return {
|
|
@@ -32226,6 +32279,8 @@ async function commitTaskMemory(options) {
|
|
|
32226
32279
|
const client = createClient(TASK_COMMIT_TIMEOUT_MS);
|
|
32227
32280
|
return client.endOfTaskCommit({
|
|
32228
32281
|
summary: options.summary,
|
|
32282
|
+
task: options.task,
|
|
32283
|
+
why: options.why,
|
|
32229
32284
|
category: options.category,
|
|
32230
32285
|
outcome: options.outcome,
|
|
32231
32286
|
filesTouched: options.files
|
|
@@ -32298,9 +32353,11 @@ async function commitFinalTaskMemory(options) {
|
|
|
32298
32353
|
});
|
|
32299
32354
|
const callHosted = async (summary, timeoutMs) => {
|
|
32300
32355
|
const client = createClient(timeoutMs);
|
|
32301
|
-
const handoffOnly = isFinalCommitCategory(category);
|
|
32356
|
+
const handoffOnly = isFinalCommitCategory(category) && !options.why;
|
|
32302
32357
|
return client.endOfTaskCommit({
|
|
32303
32358
|
summary,
|
|
32359
|
+
task: options.task,
|
|
32360
|
+
why: options.why,
|
|
32304
32361
|
category,
|
|
32305
32362
|
outcome: options.outcome,
|
|
32306
32363
|
filesTouched: options.files,
|
|
@@ -32354,6 +32411,28 @@ async function commitFinalTaskMemory(options) {
|
|
|
32354
32411
|
message: "Hosted final-commit failed; local fallback handoff created"
|
|
32355
32412
|
};
|
|
32356
32413
|
}
|
|
32414
|
+
function structuredTaskCommitWhy(options) {
|
|
32415
|
+
const rationale = compactWhitespace2(options.why ?? "");
|
|
32416
|
+
const explicitDecision = compactWhitespace2(options.decision ?? "");
|
|
32417
|
+
const alternatives = [
|
|
32418
|
+
...new Set((options.alternatives ?? []).map(compactWhitespace2).filter(Boolean))
|
|
32419
|
+
];
|
|
32420
|
+
const constraints = [
|
|
32421
|
+
...new Set((options.constraints ?? []).map(compactWhitespace2).filter(Boolean))
|
|
32422
|
+
];
|
|
32423
|
+
const observedOutcome = compactWhitespace2(options.observedOutcome ?? "");
|
|
32424
|
+
const decision = explicitDecision || (rationale ? compactWhitespace2(options.summary) : "");
|
|
32425
|
+
if (!decision && !rationale && alternatives.length === 0 && constraints.length === 0 && !observedOutcome) {
|
|
32426
|
+
return void 0;
|
|
32427
|
+
}
|
|
32428
|
+
return {
|
|
32429
|
+
...decision ? { decision } : {},
|
|
32430
|
+
...rationale ? { rationale } : {},
|
|
32431
|
+
...alternatives.length > 0 ? { alternatives } : {},
|
|
32432
|
+
...constraints.length > 0 ? { constraints } : {},
|
|
32433
|
+
...observedOutcome ? { observedOutcome } : {}
|
|
32434
|
+
};
|
|
32435
|
+
}
|
|
32357
32436
|
function printJournalWarning(result) {
|
|
32358
32437
|
if (result?.status === "error" && result.error) {
|
|
32359
32438
|
console.log(`Journal checkpoint: ${result.error}`);
|
|
@@ -32399,8 +32478,11 @@ async function workflowPhaseCommitCommand(options) {
|
|
|
32399
32478
|
phase,
|
|
32400
32479
|
summary: options.summary
|
|
32401
32480
|
});
|
|
32481
|
+
const structuredWhy = structuredTaskCommitWhy(options);
|
|
32402
32482
|
const result = await commitPhaseTaskMemory({
|
|
32403
32483
|
summary: durableSummary,
|
|
32484
|
+
task: `${state.goal} / ${phase.title}`,
|
|
32485
|
+
why: structuredWhy,
|
|
32404
32486
|
category,
|
|
32405
32487
|
outcome,
|
|
32406
32488
|
files
|
|
@@ -32475,14 +32557,7 @@ async function workflowPhaseCommitCommand(options) {
|
|
|
32475
32557
|
journalAttempted: true,
|
|
32476
32558
|
teamSyncCompletionAttempted: shouldCompleteTeamSyncWork
|
|
32477
32559
|
});
|
|
32478
|
-
const whyCapture =
|
|
32479
|
-
sourceKind: "phase_commit",
|
|
32480
|
-
sourceSessionId: state.workflowId,
|
|
32481
|
-
task: `${state.goal} / ${phase.title}`,
|
|
32482
|
-
summary: options.summary,
|
|
32483
|
-
files,
|
|
32484
|
-
commands: readLatestWorkflowCommands(process.cwd())
|
|
32485
|
-
});
|
|
32560
|
+
const whyCapture = taskCommitWhyCaptureReceipt(result, "phase_commit");
|
|
32486
32561
|
phaseCommitReceipt.whyCapture = whyCapture;
|
|
32487
32562
|
writeWorkflowState(state);
|
|
32488
32563
|
const coordinationRelease = await releaseWorkflowCoordination(
|
|
@@ -32580,8 +32655,11 @@ async function finalCommitCommand(options) {
|
|
|
32580
32655
|
});
|
|
32581
32656
|
const outcome = options.outcome ?? "completed";
|
|
32582
32657
|
const category = normalizeFinalCommitCategory(options.category);
|
|
32658
|
+
const structuredWhy = structuredTaskCommitWhy(options);
|
|
32583
32659
|
const result = await commitFinalTaskMemory({
|
|
32584
32660
|
workflowId: state?.workflowId,
|
|
32661
|
+
task: state?.goal,
|
|
32662
|
+
why: structuredWhy,
|
|
32585
32663
|
summary: options.summary,
|
|
32586
32664
|
category,
|
|
32587
32665
|
outcome,
|
|
@@ -32633,15 +32711,7 @@ async function finalCommitCommand(options) {
|
|
|
32633
32711
|
journalAttempted: true,
|
|
32634
32712
|
teamSyncCompletionAttempted: outcome === "completed"
|
|
32635
32713
|
});
|
|
32636
|
-
const whyCapture =
|
|
32637
|
-
sourceKind: "final_commit",
|
|
32638
|
-
sourceSessionId: state?.workflowId ?? loadConfig().sessionId,
|
|
32639
|
-
task: state?.goal,
|
|
32640
|
-
summary: options.summary,
|
|
32641
|
-
why: options.why,
|
|
32642
|
-
files: options.files,
|
|
32643
|
-
commands: readLatestWorkflowCommands(process.cwd())
|
|
32644
|
-
});
|
|
32714
|
+
const whyCapture = taskCommitWhyCaptureReceipt(result, "final_commit");
|
|
32645
32715
|
const coordinationRelease = state ? await releaseWorkflowCoordination(state, `Workflow ${state.workflowId} final-commit.`) : void 0;
|
|
32646
32716
|
const report = buildFinalCommitReport({
|
|
32647
32717
|
state,
|
|
@@ -40034,7 +40104,7 @@ workflow.command("runtime-checkpoint").description(
|
|
|
40034
40104
|
});
|
|
40035
40105
|
workflow.command("phase-commit").description(
|
|
40036
40106
|
"Persist a phase outcome through snipara_end_of_task_commit and advance the workflow"
|
|
40037
|
-
).argument("<phaseId>", "Workflow phase id").requiredOption("-s, --summary <summary>", "Phase outcome summary").option("-c, --category <category>", "Memory category", "workflow-phase").option("-o, --outcome <outcome>", "completed|partial|blocked|abandoned", "completed").option("-f, --files <files...>", "Files touched").option(
|
|
40107
|
+
).argument("<phaseId>", "Workflow phase id").requiredOption("-s, --summary <summary>", "Phase outcome summary").option("--decision <decision>", "Decision text; defaults to summary when --why is set").option("--why <why>", "Decision rationale; never inferred when absent").option("--alternative <alternative>", "Rejected alternative; repeatable", collectOption, []).option("--constraint <constraint>", "Decision constraint; repeatable", collectOption, []).option("--observed-outcome <outcome>", "Observed result, distinct from execution status").option("-c, --category <category>", "Memory category", "workflow-phase").option("-o, --outcome <outcome>", "completed|partial|blocked|abandoned", "completed").option("-f, --files <files...>", "Files touched").option(
|
|
40038
40108
|
"--evidence <evidence>",
|
|
40039
40109
|
"Phase evidence as passed|failed|not-run|unknown:text; repeatable",
|
|
40040
40110
|
collectOption,
|
|
@@ -40043,6 +40113,11 @@ workflow.command("phase-commit").description(
|
|
|
40043
40113
|
await workflowPhaseCommitCommand({
|
|
40044
40114
|
phaseId,
|
|
40045
40115
|
summary: options.summary,
|
|
40116
|
+
decision: options.decision,
|
|
40117
|
+
why: options.why,
|
|
40118
|
+
alternatives: options.alternative,
|
|
40119
|
+
constraints: options.constraint,
|
|
40120
|
+
observedOutcome: options.observedOutcome,
|
|
40046
40121
|
category: options.category,
|
|
40047
40122
|
outcome: options.outcome,
|
|
40048
40123
|
files: options.files,
|
|
@@ -40050,7 +40125,7 @@ workflow.command("phase-commit").description(
|
|
|
40050
40125
|
json: options.json
|
|
40051
40126
|
});
|
|
40052
40127
|
});
|
|
40053
|
-
workflow.command("final-commit").description("Persist the final workflow outcome and close the local workflow state").requiredOption("-s, --summary <summary>", "Final outcome summary").option("--why <why>", "Decision rationale; never inferred when absent").option("-c, --category <category>", "Memory category", "final-commit").option("-o, --outcome <outcome>", "completed|partial|blocked|abandoned", "completed").option("-f, --files <files...>", "Files touched").option(
|
|
40128
|
+
workflow.command("final-commit").description("Persist the final workflow outcome and close the local workflow state").requiredOption("-s, --summary <summary>", "Final outcome summary").option("--decision <decision>", "Decision text; defaults to summary when --why is set").option("--why <why>", "Decision rationale; never inferred when absent").option("--alternative <alternative>", "Rejected alternative; repeatable", collectOption, []).option("--constraint <constraint>", "Decision constraint; repeatable", collectOption, []).option("--observed-outcome <outcome>", "Observed result, distinct from execution status").option("-c, --category <category>", "Memory category", "final-commit").option("-o, --outcome <outcome>", "completed|partial|blocked|abandoned", "completed").option("-f, --files <files...>", "Files touched").option(
|
|
40054
40129
|
"--evidence <evidence>",
|
|
40055
40130
|
"Verification evidence as passed|failed|not-run|unknown:text; repeatable",
|
|
40056
40131
|
collectOption,
|
|
@@ -40058,7 +40133,11 @@ workflow.command("final-commit").description("Persist the final workflow outcome
|
|
|
40058
40133
|
).option("--risk <risk>", "Known residual risk; repeatable", collectOption, []).option("--next-step <nextStep>", "Recommended next action").option("--json", "Print raw JSON").action(async (options) => {
|
|
40059
40134
|
await finalCommitCommand({
|
|
40060
40135
|
summary: options.summary,
|
|
40136
|
+
decision: options.decision,
|
|
40061
40137
|
why: options.why,
|
|
40138
|
+
alternatives: options.alternative,
|
|
40139
|
+
constraints: options.constraint,
|
|
40140
|
+
observedOutcome: options.observedOutcome,
|
|
40062
40141
|
category: options.category,
|
|
40063
40142
|
outcome: options.outcome,
|
|
40064
40143
|
files: options.files,
|
|
@@ -41083,7 +41162,7 @@ program.command("task-commit").description("Persist durable outcomes after meani
|
|
|
41083
41162
|
json: options.json
|
|
41084
41163
|
});
|
|
41085
41164
|
});
|
|
41086
|
-
program.command("final-commit").description("Persist the final managed workflow outcome through snipara_end_of_task_commit").requiredOption("-s, --summary <summary>", "Final outcome summary").option("--why <why>", "Decision rationale; never inferred when absent").option("-c, --category <category>", "Category", "final-commit").option("-o, --outcome <outcome>", "Outcome", "completed").option("-f, --files <files...>", "Files touched").option(
|
|
41165
|
+
program.command("final-commit").description("Persist the final managed workflow outcome through snipara_end_of_task_commit").requiredOption("-s, --summary <summary>", "Final outcome summary").option("--decision <decision>", "Decision text; defaults to summary when --why is set").option("--why <why>", "Decision rationale; never inferred when absent").option("--alternative <alternative>", "Rejected alternative; repeatable", collectOption, []).option("--constraint <constraint>", "Decision constraint; repeatable", collectOption, []).option("--observed-outcome <outcome>", "Observed result, distinct from execution status").option("-c, --category <category>", "Category", "final-commit").option("-o, --outcome <outcome>", "Outcome", "completed").option("-f, --files <files...>", "Files touched").option(
|
|
41087
41166
|
"--evidence <evidence>",
|
|
41088
41167
|
"Verification evidence as passed|failed|not-run|unknown:text; repeatable",
|
|
41089
41168
|
collectOption,
|
|
@@ -41091,7 +41170,11 @@ program.command("final-commit").description("Persist the final managed workflow
|
|
|
41091
41170
|
).option("--risk <risk>", "Known residual risk; repeatable", collectOption, []).option("--next-step <nextStep>", "Recommended next action").option("--json", "Print raw JSON").action(async (options) => {
|
|
41092
41171
|
await finalCommitCommand({
|
|
41093
41172
|
summary: options.summary,
|
|
41173
|
+
decision: options.decision,
|
|
41094
41174
|
why: options.why,
|
|
41175
|
+
alternatives: options.alternative,
|
|
41176
|
+
constraints: options.constraint,
|
|
41177
|
+
observedOutcome: options.observedOutcome,
|
|
41095
41178
|
category: options.category,
|
|
41096
41179
|
outcome: options.outcome,
|
|
41097
41180
|
files: options.files,
|
package/docs/FULL_REFERENCE.md
CHANGED
|
@@ -1632,10 +1632,17 @@ Companion separates two concepts:
|
|
|
1632
1632
|
`workflow phase-commit` and `final-commit` keep local workflow state moving on transient
|
|
1633
1633
|
hosted commit timeouts and surface that local fallback explicitly in the result.
|
|
1634
1634
|
|
|
1635
|
-
`final-commit`
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1635
|
+
`final-commit` remains handoff-only when no structured rationale is supplied.
|
|
1636
|
+
When `--why` or another structured Why Capture field is present, Companion sends
|
|
1637
|
+
one atomic `why` block with the handoff commit; it does not issue a second capture
|
|
1638
|
+
request. `--decision` overrides the decision text, while `--why` uses the commit
|
|
1639
|
+
summary as the decision fallback. Repeat `--alternative` and `--constraint` as
|
|
1640
|
+
needed, and use `--observed-outcome` only for an observed result rather than the
|
|
1641
|
+
execution status. `workflow phase-commit` exposes the same fields. The resulting
|
|
1642
|
+
candidate remains pending review. The seven-section final report reads durable
|
|
1643
|
+
phase receipts already stored, marks Why Capture candidates as pending review,
|
|
1644
|
+
lists skipped/duplicate/failed items as not persisted, and never treats the final
|
|
1645
|
+
summary or handoff as newly approved durable memory.
|
|
1639
1646
|
|
|
1640
1647
|
Do not call `snipara_end_of_task_commit` mechanically for every Git commit. For risky commits,
|
|
1641
1648
|
package releases, or retries after failures, run Memory Guard first so the agent sees relevant
|