snipara-companion 3.7.0 → 3.7.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/CHANGELOG.md CHANGED
@@ -4,6 +4,14 @@ Release notes for `snipara-companion`, newest first.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## New In 3.7.1
8
+
9
+ - Preserves expected results and bounded source context in Controller task queries;
10
+ binds the projection version to the existing immutable bridge identity while retaining legacy exports.
11
+ - Separates caller-declared completion, source-verified local task proofs and unchecked objective acceptance in reports.
12
+ - Adds bugfix, feature and legacy presets to the existing workflow scaffold command.
13
+ - Adds a replayable repository validation campaign for local checkpoint continuation across native hosts.
14
+
7
15
  ## New In 3.7.0
8
16
 
9
17
  - Adds Controller progress and cockpit commands backed by Orchestrator receipts,
package/README.md CHANGED
@@ -713,3 +713,12 @@ PostToolUse hook runs the same preview-then-confirm flow when no managed
713
713
  workflow is active. It submits the commit message, commit SHA, and changed-file
714
714
  evidence as `sourceKind=commit`; ordinary messages produce no candidate, and
715
715
  any captured rationale remains pending human review.
716
+
717
+ ### Portable workflow starting points
718
+
719
+ Use `workflow scaffold --preset bugfix`, `--preset feature` or `--preset legacy`
720
+ with a concrete `--goal`. Edit the generated plan, then use the existing
721
+ `workflow start`, phase and resume commands. Completion declarations remain
722
+ separate from checked Controller proof sources and Feature acceptance.
723
+ See [portable workflows](docs/portable-methods.md) for context bounds, legacy
724
+ compatibility and the native continuation validation procedure.
package/dist/index.d.ts CHANGED
@@ -19,6 +19,7 @@ declare const CONTROLLER_BRIDGE_VERSION = "snipara.companion_controller_bridge.v
19
19
  interface ControllerBridgeArtifact {
20
20
  schemaVersion: typeof CONTROLLER_BRIDGE_VERSION;
21
21
  artifactId: string;
22
+ taskContextVersion?: "source-context-v1";
22
23
  sourceLeadPlan: {
23
24
  path: string;
24
25
  sha256: string;
@@ -62,7 +63,7 @@ interface ControllerBridgeExportOptions {
62
63
  dir?: string;
63
64
  json?: boolean;
64
65
  }
65
- declare function exportControllerBridge(options: ControllerBridgeExportOptions, writeArtifacts?: boolean): ControllerBridgeArtifact;
66
+ declare function exportControllerBridge(options: ControllerBridgeExportOptions, writeArtifacts?: boolean, preserveSourceContext?: boolean): ControllerBridgeArtifact;
66
67
 
67
68
  interface ControllerProgress {
68
69
  version: "snipara.companion_controller_progress.v1";
@@ -79,6 +80,11 @@ interface ControllerProgress {
79
80
  dependsOn: string[];
80
81
  workerId: string | null;
81
82
  }>;
83
+ completionEvidence: {
84
+ taskProofs: "not_checked";
85
+ objectiveAcceptance: "not_checked";
86
+ reportedObjectiveStatus: string;
87
+ };
82
88
  authority: "unverified_local_snapshot";
83
89
  executionAllowed: false;
84
90
  reviewRequired: true;
@@ -113,6 +119,12 @@ interface ControllerBridgeProjection {
113
119
  combinedPatchSha256: string;
114
120
  reportedReviewVerdict: string | null;
115
121
  };
122
+ completionEvidence: {
123
+ taskProofs: "verified" | "pending";
124
+ objectiveAcceptance: "not_checked";
125
+ reportedObjectiveStatus: string;
126
+ authority: "source_bound_local_artifacts";
127
+ };
116
128
  authority: "source_bound_local_artifacts";
117
129
  identityAuthenticated: false;
118
130
  executionAllowed: false;
@@ -4223,6 +4235,12 @@ interface FinalCommitReportV1 {
4223
4235
  generatedAt: string;
4224
4236
  workflowId?: string;
4225
4237
  outcome: FinalCommitOutcome;
4238
+ completionEvidence: {
4239
+ declaration: FinalCommitOutcome;
4240
+ taskProofs: "not_checked";
4241
+ objectiveAcceptance: "not_checked";
4242
+ authority: "caller_declaration";
4243
+ };
4226
4244
  changed: {
4227
4245
  summary: string;
4228
4246
  files: string[];
@@ -4441,10 +4459,10 @@ interface ProducerLoopReport {
4441
4459
  recommendedActions: string[];
4442
4460
  caveats: string[];
4443
4461
  }
4444
- type WorkflowPlanPreset = "memory-backend-unification" | "project-intelligence-continuity-layer";
4462
+ type WorkflowPlanPreset = "bugfix" | "feature" | "legacy" | "memory-backend-unification" | "project-intelligence-continuity-layer";
4445
4463
  interface WorkflowPlanScaffoldDocument {
4446
4464
  preset: WorkflowPlanPreset;
4447
- mode: "full";
4465
+ mode: "standard" | "full";
4448
4466
  goal: string;
4449
4467
  steps: Array<{
4450
4468
  id: string;
package/dist/index.js CHANGED
@@ -24709,6 +24709,12 @@ function buildFinalCommitReport(input) {
24709
24709
  generatedAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
24710
24710
  ...input.state?.workflowId ? { workflowId: input.state.workflowId } : {},
24711
24711
  outcome: input.outcome,
24712
+ completionEvidence: {
24713
+ declaration: input.outcome,
24714
+ taskProofs: "not_checked",
24715
+ objectiveAcceptance: "not_checked",
24716
+ authority: "caller_declaration"
24717
+ },
24712
24718
  changed: {
24713
24719
  summary: boundedText2(input.summary) ?? "No final summary provided.",
24714
24720
  files,
@@ -24777,7 +24783,8 @@ function formatFinalCommitReport(report) {
24777
24783
  "2. Why",
24778
24784
  report.rationale.text ? `- ${report.rationale.text} (${report.rationale.source})` : "- Not provided or detected.",
24779
24785
  "",
24780
- "3. Evidence"
24786
+ "3. Evidence",
24787
+ "- Completion is caller-declared; task proofs and objective acceptance were not independently checked by this report."
24781
24788
  ];
24782
24789
  if (report.evidence.items.length === 0) {
24783
24790
  lines.push("- No verification evidence declared.");
@@ -28129,6 +28136,9 @@ var PRODUCER_LOOP_REPORT_VERSION = "snipara.producer_loop_report.v0";
28129
28136
  var PRODUCER_LOOP_RELATIVE_DIR = path31.join(".snipara", "producer-loop");
28130
28137
  var PRODUCER_LOOP_MIN_REVIEW_SAMPLE_SIZE = 5;
28131
28138
  var WORKFLOW_PLAN_PRESET_IDS = [
28139
+ "bugfix",
28140
+ "feature",
28141
+ "legacy",
28132
28142
  "memory-backend-unification",
28133
28143
  "project-intelligence-continuity-layer"
28134
28144
  ];
@@ -29398,6 +29408,12 @@ function normalizeWorkflowPlanInput(input, fallbackGoal) {
29398
29408
  }
29399
29409
  function defaultWorkflowPlanGoal(preset) {
29400
29410
  switch (preset) {
29411
+ case "bugfix":
29412
+ return "Fix a reproduced defect with a bounded regression";
29413
+ case "feature":
29414
+ return "Deliver a scoped feature against explicit acceptance criteria";
29415
+ case "legacy":
29416
+ return "Change legacy behavior while preserving documented compatibility";
29401
29417
  case "memory-backend-unification":
29402
29418
  return "Unify backend memory on Memory V2 with four-phase delivery, sandbox checkpoints, and orchestrator-visible runtime gates";
29403
29419
  case "project-intelligence-continuity-layer":
@@ -29407,6 +29423,35 @@ function defaultWorkflowPlanGoal(preset) {
29407
29423
  function buildWorkflowPlanPresetDocument(preset, goal) {
29408
29424
  const resolvedGoal = goal ?? defaultWorkflowPlanGoal(preset);
29409
29425
  switch (preset) {
29426
+ case "bugfix":
29427
+ case "feature":
29428
+ case "legacy": {
29429
+ const legacy = preset === "legacy";
29430
+ const steps = [
29431
+ {
29432
+ id: "scope",
29433
+ title: legacy ? "Characterize existing behavior" : "Define scope and acceptance",
29434
+ query: legacy ? "Read the affected code and callers. Record existing behavior, compatibility constraints, source references and characterization tests before edits. Bound the write scope." : "Record the objective, relevant source references, allowed files and observable acceptance criteria. Reproduce the defect for a bugfix; inspect existing capabilities before adding a feature.",
29435
+ acceptance: "Scope, source references and acceptance criteria are explicit; selected checks are runnable in this repository.",
29436
+ gates: [legacy ? "characterization tests" : "reproduction or acceptance example"]
29437
+ },
29438
+ {
29439
+ id: "implement",
29440
+ title: "Implement the smallest scoped change",
29441
+ query: "Use the approved sources and scope. Reuse existing contracts. Preserve unrelated behavior and record decisions, artifacts and the next action in the current workflow.",
29442
+ acceptance: "The change satisfies the scoped examples and preserves established compatibility.",
29443
+ gates: ["selected regression tests"]
29444
+ },
29445
+ {
29446
+ id: "verify",
29447
+ title: "Verify the result and prepare handoff",
29448
+ query: "Run the selected regression and compatibility checks. Record exact commands, results, untested cases and artifact references. Distinguish declared completion from independently verified proofs and objective acceptance. Check affected package surfaces before release.",
29449
+ acceptance: "Acceptance examples and regressions pass, or blockers remain explicit; the handoff contains decisions, checks and remaining work.",
29450
+ gates: ["acceptance examples", "compatibility checks", "package surface review"]
29451
+ }
29452
+ ];
29453
+ return { preset, mode: preset === "bugfix" ? "standard" : "full", goal: resolvedGoal, steps };
29454
+ }
29410
29455
  case "project-intelligence-continuity-layer":
29411
29456
  return {
29412
29457
  preset,
@@ -40366,6 +40411,11 @@ function readControllerProgress(file, cwd, now = /* @__PURE__ */ new Date()) {
40366
40411
  updatedAt: state.updatedAt,
40367
40412
  freshness,
40368
40413
  tasks,
40414
+ completionEvidence: {
40415
+ taskProofs: "not_checked",
40416
+ objectiveAcceptance: "not_checked",
40417
+ reportedObjectiveStatus: String(state.status)
40418
+ },
40369
40419
  authority: "unverified_local_snapshot",
40370
40420
  executionAllowed: false,
40371
40421
  reviewRequired: true,
@@ -42090,7 +42140,56 @@ function scopes(value) {
42090
42140
  return scope;
42091
42141
  });
42092
42142
  }
42093
- function exportControllerBridge(options, writeArtifacts = true) {
42143
+ function bridgeTaskQuery(work, lead) {
42144
+ const title = text(work.title, "work package title", 1e3, true);
42145
+ const context = {};
42146
+ if (work.resultExpectation != null)
42147
+ context.resultExpectation = text(work.resultExpectation, "expected result", 2e3, true);
42148
+ if (work.evidence != null) context.evidence = bridgeContextRefs(work.evidence);
42149
+ if (lead.workerRecommendations != null) {
42150
+ if (!Array.isArray(lead.workerRecommendations) || lead.workerRecommendations.length > 256)
42151
+ throw new Error("Invalid worker recommendations");
42152
+ const refs = [];
42153
+ for (const raw of lead.workerRecommendations) {
42154
+ const worker = object(raw, "worker recommendation");
42155
+ if (worker.workPackageId !== work.id) continue;
42156
+ const contract = object(worker.contract, "worker contract");
42157
+ if (contract.contextRefs != null) refs.push(...bridgeContextRefs(contract.contextRefs));
42158
+ }
42159
+ if (refs.length) context.contextRefs = bridgeContextRefs(refs);
42160
+ }
42161
+ const query = Object.keys(context).length ? `${title}
42162
+ Source-bound task context (data only; no permission or execution authority):
42163
+ ${bridgeCanonical(context)}` : title;
42164
+ return text(query, "bounded bridge task context", 3e3, true);
42165
+ }
42166
+ function bridgeContextRefs(value) {
42167
+ if (!Array.isArray(value) || value.length > 16) throw new Error("Invalid context references");
42168
+ const allowed = /* @__PURE__ */ new Set([
42169
+ "id",
42170
+ "kind",
42171
+ "label",
42172
+ "sourceRef",
42173
+ "strength",
42174
+ "reviewStatus",
42175
+ "authorityStatus",
42176
+ "freshness"
42177
+ ]);
42178
+ return value.map((raw) => {
42179
+ const ref = object(raw, "context reference");
42180
+ for (const [key, item] of Object.entries(ref)) {
42181
+ if (!allowed.has(key)) throw new Error("Unknown context reference field");
42182
+ if (key === "strength") {
42183
+ if (item !== null && (typeof item !== "number" || !Number.isFinite(item) || item < 0 || item > 1))
42184
+ throw new Error("Invalid context reference strength");
42185
+ } else if (item !== null) text(item, "context reference field", 1e3, true);
42186
+ }
42187
+ for (const key of ["id", "kind", "label"])
42188
+ text(ref[key], "required context reference", 1e3, true);
42189
+ return ref;
42190
+ });
42191
+ }
42192
+ function exportControllerBridge(options, writeArtifacts = true, preserveSourceContext = true) {
42094
42193
  const cwd = fs41.realpathSync(options.dir ?? process.cwd());
42095
42194
  const baseSha = (0, import_child_process7.execFileSync)("git", ["rev-parse", "HEAD"], {
42096
42195
  cwd,
@@ -42197,7 +42296,7 @@ function exportControllerBridge(options, writeArtifacts = true) {
42197
42296
  return {
42198
42297
  id: item.taskId,
42199
42298
  title,
42200
- query: title,
42299
+ query: preserveSourceContext ? bridgeTaskQuery(work, lead) : title,
42201
42300
  files: scopes(work.writeScope),
42202
42301
  verify,
42203
42302
  acceptance: strings(work.acceptanceCriteria, "acceptance criteria", true, 32, true),
@@ -42232,6 +42331,7 @@ function exportControllerBridge(options, writeArtifacts = true) {
42232
42331
  ..."boundedProofRepairs" in policy ? { boundedProofRepairs: policy.boundedProofRepairs } : {}
42233
42332
  };
42234
42333
  const identity = {
42334
+ ...preserveSourceContext ? { taskContextVersion: "source-context-v1" } : {},
42235
42335
  sourceLeadPlan: { path: source3.relative, sha256: source3.hash },
42236
42336
  sourcePolicy: { path: policySource.relative, sha256: policySource.hash },
42237
42337
  controllerId,
@@ -42316,6 +42416,8 @@ function projectControllerBridge(options) {
42316
42416
  const artifact = artifactRead.value;
42317
42417
  if (artifact.schemaVersion !== CONTROLLER_BRIDGE_VERSION || artifact.executionAllowed !== false)
42318
42418
  throw new Error("Unsupported bridge artifact schema or authority");
42419
+ if (artifact.taskContextVersion != null && artifact.taskContextVersion !== "source-context-v1")
42420
+ throw new Error("Unsupported task context version");
42319
42421
  const leadRef = object2(artifact.sourceLeadPlan);
42320
42422
  const policyRef = object2(artifact.sourcePolicy);
42321
42423
  const workflowRef = object2(artifact.workflowPlan);
@@ -42330,7 +42432,8 @@ function projectControllerBridge(options) {
42330
42432
  ),
42331
42433
  dir: cwd
42332
42434
  },
42333
- false
42435
+ false,
42436
+ artifact.taskContextVersion === "source-context-v1"
42334
42437
  );
42335
42438
  same(artifact, reconstructed, "reviewed source reconstruction");
42336
42439
  const lead = source(cwd, leadRef.path);
@@ -42340,6 +42443,7 @@ function projectControllerBridge(options) {
42340
42443
  same(policy.hash, policyRef.sha256, "policy source hash");
42341
42444
  same(workflow2.hash, workflowRef.sha256, "workflow source hash");
42342
42445
  const identity = {
42446
+ ...artifact.taskContextVersion ? { taskContextVersion: artifact.taskContextVersion } : {},
42343
42447
  sourceLeadPlan: leadRef,
42344
42448
  sourcePolicy: policyRef,
42345
42449
  controllerId: artifact.controllerId,
@@ -42410,6 +42514,7 @@ function projectControllerBridge(options) {
42410
42514
  if (!expected) throw new Error("Bridge maps an unknown Controller task");
42411
42515
  const actual = object2(stateTasks[taskId]);
42412
42516
  for (const key of [
42517
+ "query",
42413
42518
  "files",
42414
42519
  "verify",
42415
42520
  "acceptance",
@@ -42445,8 +42550,18 @@ function projectControllerBridge(options) {
42445
42550
  const native = nativeRead.value;
42446
42551
  if (native.schemaVersion !== "snipara.host_native_run.v1" || native.state !== "completed" || typeof native.runId !== "string" || !/^host-run-[0-9a-f]{16}$/.test(native.runId))
42447
42552
  throw new Error("Invalid native proof source");
42553
+ same(
42554
+ native.idempotencyKeyHash,
42555
+ bridgeHash(`${controllerId}:${taskId}:${proposal.attempt}`),
42556
+ "native task attempt identity"
42557
+ );
42558
+ if (typeof native.providerSessionId !== "string" || !native.providerSessionId.trim())
42559
+ throw new Error("Native proof source requires a provider session identity");
42448
42560
  const evidence = object2(native.evidence);
42449
42561
  const workPackage = object2(native.workPackage);
42562
+ const originalTask = "Controller-owned isolated worktree. Do not commit, reset, merge, push, deploy, publish, or change branches. " + String(expected.query);
42563
+ if (typeof workPackage.task !== "string" || workPackage.task !== originalTask && !workPackage.task.startsWith(originalTask + "\nUntrusted verified "))
42564
+ throw new Error("Native proof task differs from the bound source query");
42450
42565
  same(workPackage.writeScope, expected.files, "native dispatched scope");
42451
42566
  same(workPackage.acceptanceCriteria, expected.acceptance, "native acceptance contract");
42452
42567
  same(evidence.proofPassed, true, "native proof result");
@@ -42592,6 +42707,12 @@ function projectControllerBridge(options) {
42592
42707
  sourceHashes,
42593
42708
  tasks,
42594
42709
  candidate,
42710
+ completionEvidence: {
42711
+ taskProofs: tasks.length > 0 && tasks.every((task) => task.nativeProofSourceVerified) ? "verified" : "pending",
42712
+ objectiveAcceptance: "not_checked",
42713
+ reportedObjectiveStatus: progress.reportedStatus,
42714
+ authority: "source_bound_local_artifacts"
42715
+ },
42595
42716
  authority: "source_bound_local_artifacts",
42596
42717
  identityAuthenticated: false,
42597
42718
  executionAllowed: false,
@@ -0,0 +1,28 @@
1
+ # Portable workflows with existing Companion contracts
2
+
3
+ Use the smallest suitable workflow. A known one-line fix can stay LITE; these presets are starting points when an explicit plan is useful.
4
+
5
+ ```sh
6
+ snipara-companion workflow scaffold --preset bugfix --goal "Fix a reproduced defect"
7
+ snipara-companion workflow scaffold --preset feature --goal "Deliver a scoped feature"
8
+ snipara-companion workflow scaffold --preset legacy --goal "Change behavior while preserving compatibility"
9
+ snipara-companion workflow start --plan-file .snipara/workflow/plans/bugfix-plan.json
10
+ snipara-companion workflow phase-start scope
11
+ snipara-companion workflow status --json
12
+ ```
13
+
14
+ The existing generated JSON can be edited before start. Bugfix defaults to STANDARD coordination, feature and legacy to FULL. Record real source references, allowed files and repository-specific checks in each plan. No worker, service or Orchestrator is required by these templates. A workflow does not automatically execute its gate labels.
15
+
16
+ ## Passing work to Orchestrator
17
+
18
+ The existing Engineering Lead plan and controller-bridge export remain the path for governed execution. New exports add `taskContextVersion: source-context-v1` to the hash-bound bridge identity. The existing task query contains the exact expected result, work-package evidence and matching worker context references as bounded JSON data. Source references are not downloaded, executed or treated as permissions. Unknown reference fields, malformed values and context over 3000 UTF-16 code units are refused before export rather than truncated. Shorten the reference descriptions or split the work explicitly. The Python initializer reconstructs the same query; source drift or a changed query is rejected. Legacy artifacts without the context version retain their original title-only semantics. Legacy compatibility does not assert that an old export transported rich context.
19
+
20
+ ## Reading completion evidence
21
+
22
+ `final-commit` records a declaration and handoff, not independent proof execution. Its `completionEvidence` always identifies caller authority and unchecked task proofs/objective acceptance, even when explicit evidence labels say passed. STRICT mode validates declared passing evidence; it does not authenticate the person who supplied it.
23
+
24
+ A Controller snapshot is also unverified. `controller-bridge project` can separately report `taskProofs: verified` only after checking exact local sources, native proof floor, scope and proposal bindings. Its `objectiveAcceptance` stays `not_checked`: a reported accepted Controller status or candidate review is not an independently evaluated feature result. The projection cannot close a task or grant execution. Existing Orchestrator Feature acceptance and exact-tree evaluation remain the path for objective validation.
25
+
26
+ ## Reproducing native continuation
27
+
28
+ In the monorepo, use `scripts/validation/portable-methods.py --help`. Run with the Orchestrator development environment. Native execution requires `--execute-native` and an explicit fresh output directory. The campaign uses a synthetic fixture, existing host adapters, interruption and failing-proof controls, and equal frozen context in files-only/Companion arms. Saved workflow v2 state is read through the real local `workflow status` command. This tests local checkpoint rehydration, not hosted-memory resume or live-process migration. Outputs preserve failures, durations and observed usage; unknown measurements remain null. The benchmark script is repository validation tooling, not a new installed workflow engine.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "3.7.0",
3
+ "version": "3.7.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": {