@quantiya/codevibe-claude-plugin 2.0.14 → 2.0.16

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.
@@ -3442,7 +3442,7 @@ var AppSyncGraphQLError = class extends Error {
3442
3442
  function isRetiredSessionError(error) {
3443
3443
  return (error instanceof Error ? error.message : String(error)).includes("RETIRED_SESSION_CANNOT_REACTIVATE");
3444
3444
  }
3445
- function isSubmitImplementorUsageSchemaMismatch(error) {
3445
+ function isSubmitImplementorAdditiveSchemaMismatch(error) {
3446
3446
  return error instanceof AppSyncGraphQLError ? error.message.includes("field that is not defined for input object type 'SubmitImplementorOutputInput'") : !1;
3447
3447
  }
3448
3448
  var RECONNECT_CONFIG = {
@@ -4759,15 +4759,23 @@ var AppSyncClient = class _AppSyncClient {
4759
4759
  sessionId: input.sessionId,
4760
4760
  roundNumber: input.roundNumber,
4761
4761
  encProposal,
4762
- ...input.usage !== void 0 ? { usage: input.usage } : {}
4762
+ ...input.usage !== void 0 ? { usage: input.usage } : {},
4763
+ // Presence is the P24 capability handshake. Updated clients always
4764
+ // send false or true; legacy clients omit the field entirely.
4765
+ reviewScopeReset: input.reviewScopeReset ?? !1
4763
4766
  }
4764
4767
  }, response;
4765
4768
  try {
4766
4769
  response = await this.graphqlRequest(mutations.submitImplementorOutput, variables);
4767
4770
  } catch (err) {
4768
- if (input.usage === void 0 || !isSubmitImplementorUsageSchemaMismatch(err))
4771
+ if (!isSubmitImplementorAdditiveSchemaMismatch(err))
4769
4772
  throw err;
4770
- logger.warn("[AppSyncClient] submitImplementorOutput usage field rejected by backend schema; retrying without usage", {
4773
+ if (input.reviewScopeReset === !0)
4774
+ throw logger.warn("[AppSyncClient] explicit review-scope reset rejected by old backend schema; failing closed", {
4775
+ gateId: input.gateId,
4776
+ taskId: input.taskId
4777
+ }), err;
4778
+ logger.warn("[AppSyncClient] submitImplementorOutput additive fields rejected by backend schema; retrying legacy input", {
4771
4779
  gateId: input.gateId,
4772
4780
  taskId: input.taskId,
4773
4781
  reason: err instanceof Error ? err.message : String(err)
@@ -4849,7 +4857,8 @@ var AppSyncClient = class _AppSyncClient {
4849
4857
  gateId: parsed.gateId,
4850
4858
  seatId: parsed.seatId,
4851
4859
  role: parsed.role,
4852
- prompt
4860
+ prompt,
4861
+ reviewScope: parsed.reviewScope
4853
4862
  };
4854
4863
  }
4855
4864
  /**
@@ -9524,6 +9533,12 @@ var CATALOG = [
9524
9533
  );
9525
9534
  }
9526
9535
  },
9536
+ {
9537
+ name: "/review-reset",
9538
+ blurb: "Start a fresh comprehensive baseline on the next review round (Pro/Max).",
9539
+ minTier: "PRO",
9540
+ handler: () => staticOutput("/review-reset", "PENDING \u2014 entrypoint arms comprehensive review")
9541
+ },
9527
9542
  {
9528
9543
  name: "/task",
9529
9544
  blurb: "Run an implementation request directly, skipping planning (Pro/Max).",
@@ -46803,6 +46818,130 @@ async function runReviewer(opts) {
46803
46818
  });
46804
46819
  }
46805
46820
 
46821
+ // src/reviewer/round-scope.ts
46822
+ var REVIEW_SCOPE_ATTESTATION_INVALID = "review_scope_attestation_invalid", SCOPE_TAG_PREFIX = /^\[(?:baseline|revision|regression|late-preexisting|prior:[^\]\s]+)\](?:\s|$)/i;
46823
+ function parseOrigin(change) {
46824
+ let trimmed = change.trim(), match = /^\[(baseline|revision|regression|late-preexisting|prior:([^\]\s]+))\]\s+([\s\S]+)$/i.exec(
46825
+ trimmed
46826
+ );
46827
+ if (!match)
46828
+ return trimmed === "" || trimmed.startsWith("[") ? { kind: "invalid" } : { kind: "untagged", text: trimmed };
46829
+ if (match[3].trim() === "") return { kind: "invalid" };
46830
+ let tag = match[1].toLowerCase(), text2 = match[3].trim();
46831
+ return SCOPE_TAG_PREFIX.test(text2) ? { kind: "invalid" } : tag === "late-preexisting" ? { kind: "late", text: text2 } : tag.startsWith("prior:") ? {
46832
+ kind: "blocking",
46833
+ origin: "prior",
46834
+ priorFindingId: match[2] ?? null,
46835
+ text: text2
46836
+ } : {
46837
+ kind: "blocking",
46838
+ origin: tag,
46839
+ priorFindingId: null,
46840
+ text: text2
46841
+ };
46842
+ }
46843
+ function originAllowed(parsed, context) {
46844
+ return context.mode === "comprehensive" ? parsed.origin === "baseline" : parsed.origin === "baseline" ? !1 : parsed.origin !== "prior" ? !0 : context.prior_findings.some((finding) => finding.finding_id === parsed.priorFindingId);
46845
+ }
46846
+ function scopeIdentity(context) {
46847
+ let { prior_findings: _priorFindings, ...identity } = context;
46848
+ return identity;
46849
+ }
46850
+ function enforceReviewRoundScope(verdict, context) {
46851
+ if (context.mode === "delta" && verdict.verdict === "REJECT")
46852
+ return {
46853
+ ...verdict,
46854
+ verdict: "ESCALATE",
46855
+ reasoning: REVIEW_SCOPE_ATTESTATION_INVALID,
46856
+ suggested_changes: [],
46857
+ residual_observations: [...verdict.residual_observations, verdict.reasoning],
46858
+ review_scope: {
46859
+ context: scopeIdentity(context),
46860
+ blocking_findings: [],
46861
+ late_preexisting_findings: []
46862
+ }
46863
+ };
46864
+ if (verdict.verdict !== "REVISE")
46865
+ return {
46866
+ ...verdict,
46867
+ review_scope: {
46868
+ context: scopeIdentity(context),
46869
+ blocking_findings: [],
46870
+ late_preexisting_findings: []
46871
+ }
46872
+ };
46873
+ let blockingChanges = [], blockingFindings = [], lateFindings = [], invalid = !1;
46874
+ for (let change of verdict.suggested_changes) {
46875
+ let parsed = parseOrigin(change);
46876
+ if (parsed.kind === "invalid") {
46877
+ invalid = !0;
46878
+ continue;
46879
+ }
46880
+ if (parsed.kind === "late") {
46881
+ if (context.mode === "comprehensive") {
46882
+ invalid = !0;
46883
+ continue;
46884
+ }
46885
+ lateFindings.push(parsed.text);
46886
+ continue;
46887
+ }
46888
+ if (parsed.kind === "untagged") {
46889
+ if (context.mode === "comprehensive") {
46890
+ let suggestionIndex2 = blockingChanges.length;
46891
+ blockingChanges.push(parsed.text), blockingFindings.push({
46892
+ suggestion_index: suggestionIndex2,
46893
+ origin: "baseline",
46894
+ prior_finding_id: null
46895
+ });
46896
+ } else
46897
+ lateFindings.push(parsed.text);
46898
+ continue;
46899
+ }
46900
+ if (!originAllowed(parsed, context)) {
46901
+ if (context.mode === "delta" && parsed.origin === "prior") {
46902
+ lateFindings.push(parsed.text);
46903
+ continue;
46904
+ }
46905
+ invalid = !0;
46906
+ continue;
46907
+ }
46908
+ let suggestionIndex = blockingChanges.length;
46909
+ blockingChanges.push(parsed.text), blockingFindings.push({
46910
+ suggestion_index: suggestionIndex,
46911
+ origin: parsed.origin,
46912
+ prior_finding_id: parsed.priorFindingId
46913
+ });
46914
+ }
46915
+ let residual = [...verdict.residual_observations, ...lateFindings], reviewScope = {
46916
+ context: scopeIdentity(context),
46917
+ blocking_findings: blockingFindings,
46918
+ late_preexisting_findings: lateFindings
46919
+ };
46920
+ return invalid ? {
46921
+ ...verdict,
46922
+ verdict: "ESCALATE",
46923
+ reasoning: REVIEW_SCOPE_ATTESTATION_INVALID,
46924
+ suggested_changes: [],
46925
+ residual_observations: [...residual, ...verdict.suggested_changes],
46926
+ review_scope: {
46927
+ context: scopeIdentity(context),
46928
+ blocking_findings: [],
46929
+ late_preexisting_findings: lateFindings
46930
+ }
46931
+ } : blockingChanges.length === 0 ? {
46932
+ ...verdict,
46933
+ verdict: "APPROVE",
46934
+ suggested_changes: [],
46935
+ residual_observations: residual,
46936
+ review_scope: reviewScope
46937
+ } : {
46938
+ ...verdict,
46939
+ suggested_changes: blockingChanges,
46940
+ residual_observations: residual,
46941
+ review_scope: reviewScope
46942
+ };
46943
+ }
46944
+
46806
46945
  // src/reviewer/provider.ts
46807
46946
  var ReviewerErrorClass = class _ReviewerErrorClass extends Error {
46808
46947
  constructor(detail) {
@@ -47638,6 +47777,10 @@ var QuorumLoop = class _QuorumLoop {
47638
47777
  constructor(deps) {
47639
47778
  /** The active task this desktop is driving (set at start_task). */
47640
47779
  this.activeTaskId = null;
47780
+ /** Tasks whose next automatic revise round must start a fresh review baseline. */
47781
+ this.reviewScopeResetTasks = /* @__PURE__ */ new Set();
47782
+ /** Non-terminal tasks for which `/review-reset` may arm the next revise round. */
47783
+ this.reviewScopeResetEligibleTasks = /* @__PURE__ */ new Set();
47641
47784
  // [live task status timer — TUI-LIVE-TASK-STATUS-DESIGN.md §3] Per-TASK monotonic
47642
47785
  // epoch: bumped once per `startTask` and recorded per taskId. Every progress emit
47643
47786
  // stamps `epoch: originEpoch` resolved from its OWN task's id (never `taskEpoch`
@@ -48574,6 +48717,10 @@ var QuorumLoop = class _QuorumLoop {
48574
48717
  get activeTask() {
48575
48718
  return this.activeTaskId;
48576
48719
  }
48720
+ /** Arm an explicit, one-shot comprehensive baseline for the active task. */
48721
+ requestReviewScopeReset() {
48722
+ return !this.activeTaskId || !this.reviewScopeResetEligibleTasks.has(this.activeTaskId) ? !1 : (this.reviewScopeResetTasks.add(this.activeTaskId), !0);
48723
+ }
48577
48724
  /**
48578
48725
  * Audited-path fix (Fix 5) — the host's detected implementor agents
48579
48726
  * (UPPERCASE `AgentKind`), as threaded into the loop at construction. Used by
@@ -48773,7 +48920,7 @@ var QuorumLoop = class _QuorumLoop {
48773
48920
  err: err.message
48774
48921
  }), null;
48775
48922
  }
48776
- this.startTaskInFlight = !1, logger.info("[QuorumLoop] startTask succeeded \u2014 draining buffered GATE_DISPATCH (if any) then awaiting more", {
48923
+ this.startTaskInFlight = !1, this.reviewScopeResetEligibleTasks.add(taskId), logger.info("[QuorumLoop] startTask succeeded \u2014 draining buffered GATE_DISPATCH (if any) then awaiting more", {
48777
48924
  taskId,
48778
48925
  workflowState,
48779
48926
  buffered: this.pendingGateDispatches.length
@@ -48839,7 +48986,7 @@ var QuorumLoop = class _QuorumLoop {
48839
48986
  return;
48840
48987
  }
48841
48988
  let taskId = this.activeTaskId, brief = this.activeBrief;
48842
- await this.replaceShadowForRound0(taskId), await this.runImplementorRound({
48989
+ await this.replaceShadowForRound0(taskId), this.reviewScopeResetEligibleTasks.add(taskId), await this.runImplementorRound({
48843
48990
  taskId,
48844
48991
  gateId: payload.gateRunId,
48845
48992
  roundNumber: 0,
@@ -49224,7 +49371,7 @@ var QuorumLoop = class _QuorumLoop {
49224
49371
  let stale = this.shadowsByTask.get(taskId);
49225
49372
  stale && (await stale.assertCanDiscard(), logger.info("[QuorumLoop] replacing a stale shadow for a fresh round-0 dispatch (restart)", {
49226
49373
  taskId
49227
- }), await stale.discard(), this.shadowsByTask.get(taskId) === stale && this.shadowsByTask.delete(taskId), this.userNotesByTask.delete(taskId), this.roundHistoryByTask.delete(taskId), this.rationaleByTask.delete(taskId), this.reviewedSnapshotByTask.delete(taskId));
49374
+ }), await stale.discard(), this.shadowsByTask.get(taskId) === stale && this.shadowsByTask.delete(taskId), this.userNotesByTask.delete(taskId), this.reviewScopeResetTasks.delete(taskId), this.roundHistoryByTask.delete(taskId), this.rationaleByTask.delete(taskId), this.reviewedSnapshotByTask.delete(taskId));
49228
49375
  }
49229
49376
  /**
49230
49377
  * CP-14 P1 — the team execution model this session SIGNALS on
@@ -49306,7 +49453,7 @@ var QuorumLoop = class _QuorumLoop {
49306
49453
  return;
49307
49454
  }
49308
49455
  this.inFlightImplementorGates.add(gateId), this.taskByGateId.set(gateId, args.taskId), args.teamAuthority && this.teamAuthorityByGateId.set(gateId, args.teamAuthority), this.roundByGateId.set(gateId, args.roundNumber);
49309
- let originEpoch = this.originEpochFor(args.taskId), tickTimer = null, roundActive = !0, clearTick = () => {
49456
+ let originEpoch = this.originEpochFor(args.taskId), tickTimer = null, roundActive = !0, submissionStarted = !1, clearTick = () => {
49310
49457
  roundActive = !1, tickTimer && (clearInterval(tickTimer), tickTimer = null);
49311
49458
  }, roundBriefForSpawn = args.brief, copiedForSpawn = [];
49312
49459
  try {
@@ -49752,16 +49899,17 @@ var QuorumLoop = class _QuorumLoop {
49752
49899
  this.emitProgressForTask(args.taskId, originEpoch, {
49753
49900
  phase: "submitting_diff",
49754
49901
  round: args.roundNumber
49755
- });
49902
+ }), submissionStarted = !0;
49756
49903
  let result = await this.submitImplementorOutputWithRetry({
49757
49904
  taskId: args.taskId,
49758
49905
  gateId,
49759
49906
  roundNumber: args.roundNumber,
49760
49907
  rawOutput,
49761
49908
  usage: implementorUsage,
49762
- sessionKey: args.sessionKey
49909
+ sessionKey: args.sessionKey,
49910
+ reviewScopeReset: args.reviewScopeReset
49763
49911
  });
49764
- if (this.teamRunIsHalted(args.teamAuthority)) return;
49912
+ if (args.reviewScopeReset && this.reviewScopeResetTasks.delete(args.taskId), this.teamRunIsHalted(args.teamAuthority)) return;
49765
49913
  if (this.submittedOutputGates.add(gateId), result && (result.workflowState === "halted" || result.workflowState === "idle")) {
49766
49914
  let crashedEmpty = files.length === 0 && exit.exitCode !== 0, reason = files.length === 0 ? crashedEmpty ? "The coding agent exited with an error before making any changes \u2014 nothing to review. Re-run the task; if this repeats, check the desktop log." : "The implementor produced no changes \u2014 nothing to review." : "The task was halted before review (no reviewer quorum ran).";
49767
49915
  (crashedEmpty ? logger.warn.bind(logger) : logger.info.bind(logger))("[QuorumLoop] execute-first born-halt surfaced synchronously (#EF-1)", {
@@ -49793,7 +49941,11 @@ var QuorumLoop = class _QuorumLoop {
49793
49941
  this.surfaceHalt(`The task could not proceed \u2014 ${reason}.`), this.emitProgress(
49794
49942
  { phase: "round_failed", round: args.roundNumber, reason, taskId: args.taskId },
49795
49943
  originEpoch
49796
- ), await this.discardShadow(args.taskId);
49944
+ );
49945
+ let reviseFeedbackId = args.reviseFeedbackId, preserveResetForRetry = submissionStarted && args.reviewScopeReset === !0 && reviseFeedbackId !== void 0 && this.reviewScopeResetTasks.has(args.taskId);
49946
+ preserveResetForRetry && this.seenReviseFeedbackIds.delete(reviseFeedbackId), await this.discardShadow(args.taskId, void 0, {
49947
+ preserveReviseContext: preserveResetForRetry
49948
+ });
49797
49949
  } finally {
49798
49950
  clearTick(), this.inFlightImplementorGates.delete(gateId);
49799
49951
  let current = this.activeImplementorByTask.get(args.taskId);
@@ -50231,6 +50383,7 @@ var QuorumLoop = class _QuorumLoop {
50231
50383
  }
50232
50384
  return;
50233
50385
  }
50386
+ terminalDiscard && this.reviewScopeResetEligibleTasks.delete(taskId);
50234
50387
  let shadow = expected ?? mapped;
50235
50388
  if (shadow)
50236
50389
  try {
@@ -50242,7 +50395,7 @@ var QuorumLoop = class _QuorumLoop {
50242
50395
  });
50243
50396
  return;
50244
50397
  }
50245
- if (opts?.preserveReviseContext || (this.userNotesByTask.delete(taskId), this.roundHistoryByTask.delete(taskId), this.rationaleByTask.delete(taskId), appendContextItem(this.deps.session.sessionId, {
50398
+ if (opts?.preserveReviseContext || (this.userNotesByTask.delete(taskId), this.reviewScopeResetTasks.delete(taskId), this.reviewScopeResetEligibleTasks.delete(taskId), this.roundHistoryByTask.delete(taskId), this.rationaleByTask.delete(taskId), appendContextItem(this.deps.session.sessionId, {
50246
50399
  kind: "verdict",
50247
50400
  author: { role: "engine" },
50248
50401
  sensitivity: "user",
@@ -50309,7 +50462,7 @@ var QuorumLoop = class _QuorumLoop {
50309
50462
  async promoteShadowLockedInner(taskId, originEpoch) {
50310
50463
  let teamAuthority = this.teamAuthorityForTask(taskId);
50311
50464
  if (this.teamRunIsHalted(teamAuthority)) return;
50312
- this.userNotesByTask.delete(taskId), this.roundHistoryByTask.delete(taskId), this.rationaleByTask.delete(taskId);
50465
+ this.userNotesByTask.delete(taskId), this.reviewScopeResetTasks.delete(taskId), this.reviewScopeResetEligibleTasks.delete(taskId), this.roundHistoryByTask.delete(taskId), this.rationaleByTask.delete(taskId);
50313
50466
  let shadow = this.shadowsByTask.get(taskId);
50314
50467
  if (!shadow) {
50315
50468
  logger.info("[QuorumLoop] promote no-op \u2014 no in-memory shadow for task", { taskId });
@@ -50725,7 +50878,8 @@ var QuorumLoop = class _QuorumLoop {
50725
50878
  sessionId: this.deps.session.sessionId,
50726
50879
  roundNumber: args.roundNumber,
50727
50880
  rawOutput: args.rawOutput,
50728
- usage: args.usage
50881
+ usage: args.usage,
50882
+ reviewScopeReset: args.reviewScopeReset
50729
50883
  },
50730
50884
  args.sessionKey
50731
50885
  );
@@ -50808,14 +50962,15 @@ var QuorumLoop = class _QuorumLoop {
50808
50962
  }), await this.submitVerdict(args, this.synthesizeEscalate(args, DESKTOP_DISPATCH_ESCALATE_REASONING), null);
50809
50963
  return;
50810
50964
  }
50811
- let promptText = null, lastErr = null;
50965
+ let promptText = null, reviewScope = null, lastErr = null;
50812
50966
  for (let attempt = 0; attempt < PROMPT_FETCH_MAX_ATTEMPTS; attempt++)
50813
50967
  try {
50814
- promptText = (await this.deps.appsyncClient.getReviewerPrompt(
50968
+ let res = await this.deps.appsyncClient.getReviewerPrompt(
50815
50969
  args.gateId,
50816
50970
  args.seatId,
50817
50971
  sessionKey
50818
- )).prompt;
50972
+ );
50973
+ promptText = res.prompt, reviewScope = res.reviewScope ?? null;
50819
50974
  break;
50820
50975
  } catch (err) {
50821
50976
  if (lastErr = err, !isNotFoundError(lastErr) || attempt === PROMPT_FETCH_MAX_ATTEMPTS - 1)
@@ -50916,7 +51071,7 @@ var QuorumLoop = class _QuorumLoop {
50916
51071
  await seatSubstrate.teardown().catch(() => {
50917
51072
  });
50918
51073
  }
50919
- await this.submitVerdict(args, verdict, sessionKey);
51074
+ reviewScope !== null && (verdict = enforceReviewRoundScope(verdict, reviewScope)), await this.submitVerdict(args, verdict, sessionKey);
50920
51075
  } catch (err) {
50921
51076
  logger.warn("[QuorumLoop] spawnOneSeat failed \u2014 dropped", {
50922
51077
  key,
@@ -52508,7 +52663,9 @@ var QuorumLoop = class _QuorumLoop {
52508
52663
  phase: "revise_round",
52509
52664
  round: payload.nextRound,
52510
52665
  feedbackSummary: `addressing ${findingCount} ${findingCount === 1 ? "finding" : "findings"}`
52511
- }), await this.runImplementorRound({
52666
+ });
52667
+ let reviewScopeReset = this.reviewScopeResetTasks.has(taskId);
52668
+ await this.runImplementorRound({
52512
52669
  taskId,
52513
52670
  gateId: payload.nextGateId,
52514
52671
  roundNumber: payload.nextRound,
@@ -52517,6 +52674,8 @@ var QuorumLoop = class _QuorumLoop {
52517
52674
  sessionKey,
52518
52675
  ...teamAuthority ? { teamAuthority } : {},
52519
52676
  ...priorRationale ? { priorRationale } : {},
52677
+ ...reviewScopeReset ? { reviewScopeReset: !0 } : {},
52678
+ reviseFeedbackId: payload.reviseFeedbackId,
52520
52679
  ...trackEntry ? {
52521
52680
  validateDiffScope: {
52522
52681
  writePaths: trackEntry.writePaths,
@@ -53111,7 +53270,7 @@ var QuorumLoop = class _QuorumLoop {
53111
53270
  }
53112
53271
  /** @internal — test seam to set the active task without start_task. */
53113
53272
  _setActiveTaskForTests(taskId, brief) {
53114
- this.activeTaskId = taskId, brief !== void 0 && (this.activeBrief = brief);
53273
+ this.activeTaskId = taskId, this.reviewScopeResetEligibleTasks.add(taskId), brief !== void 0 && (this.activeBrief = brief);
53115
53274
  }
53116
53275
  /** @internal — read a task's accumulated binding user notes. */
53117
53276
  _userNotesForTests(taskId) {
@@ -57495,6 +57654,8 @@ async function handleShellUserInput(deps) {
57495
57654
  }
57496
57655
  }), output.output = "") : output.output = loaded.message;
57497
57656
  }
57657
+ else if (output.command === "/review-reset" && !output.sideEffect && output.output === "PENDING \u2014 entrypoint arms comprehensive review")
57658
+ args.quorumLoop ? args.quorumLoop.requestReviewScopeReset() ? output.output = "The next revise round will start a fresh comprehensive review baseline." : output.output = "/review-reset requires an active task. Start a task before requesting a reset." : output.output = "/review-reset requires an orchestration session with a running loop (Pro/Max). It is unavailable in this session.";
57498
57659
  else if (output.command === "/task" && !output.sideEffect && output.output === "PENDING \u2014 entrypoint drives startTask")
57499
57660
  if (!args.quorumLoop)
57500
57661
  output.output = "/task requires an orchestration session with a running loop (Pro/Max). It is unavailable in this session.";
@@ -554,6 +554,10 @@ export declare class QuorumLoop {
554
554
  private readonly sleep;
555
555
  /** The active task this desktop is driving (set at start_task). */
556
556
  private activeTaskId;
557
+ /** Tasks whose next automatic revise round must start a fresh review baseline. */
558
+ private readonly reviewScopeResetTasks;
559
+ /** Non-terminal tasks for which `/review-reset` may arm the next revise round. */
560
+ private readonly reviewScopeResetEligibleTasks;
557
561
  private taskEpoch;
558
562
  private readonly epochByTaskId;
559
563
  private readonly tokensByTaskId;
@@ -1144,6 +1148,8 @@ export declare class QuorumLoop {
1144
1148
  constructor(deps: QuorumLoopDeps);
1145
1149
  /** The current active task id (for the recovery poll). */
1146
1150
  get activeTask(): string | null;
1151
+ /** Arm an explicit, one-shot comprehensive baseline for the active task. */
1152
+ requestReviewScopeReset(): boolean;
1147
1153
  /**
1148
1154
  * Audited-path fix (Fix 5) — the host's detected implementor agents
1149
1155
  * (UPPERCASE `AgentKind`), as threaded into the loop at construction. Used by
@@ -1443,6 +1449,10 @@ export declare class QuorumLoop {
1443
1449
  round: number;
1444
1450
  text: string;
1445
1451
  };
1452
+ /** Explicitly start a new comprehensive reviewer baseline. */
1453
+ reviewScopeReset?: boolean;
1454
+ /** Deterministic producer identity retained only so a failed revise can be retried. */
1455
+ reviseFeedbackId?: string;
1446
1456
  }): Promise<void>;
1447
1457
  private runImplementorRoundOwned;
1448
1458
  /**
@@ -1,4 +1,5 @@
1
1
  export type { AgentKind, ReviewerRole, ReviewerVerdict, VerdictId, VerdictKind, } from './types.js';
2
+ export { enforceReviewRoundScope, REVIEW_SCOPE_ATTESTATION_INVALID } from './round-scope.js';
2
3
  export type { ReviewerError, ReviewerProvider, ReviewerSpec, } from './provider.js';
3
4
  export { ReviewerErrorClass } from './provider.js';
4
5
  export type { ParseResult, ParsedVerdict, VerdictParseError, } from './output-parser.js';
@@ -0,0 +1,9 @@
1
+ import type { ReviewerVerdict, ReviewScopeContext } from './types';
2
+ export declare const REVIEW_SCOPE_ATTESTATION_INVALID = "review_scope_attestation_invalid";
3
+ /**
4
+ * Bind a reviewer verdict to the server-authored round context. Invalid or
5
+ * unscoped blocking findings become a fail-closed ESCALATE. Properly declared
6
+ * late pre-existing findings remain visible as residual observations and can
7
+ * never trigger another revision.
8
+ */
9
+ export declare function enforceReviewRoundScope(verdict: ReviewerVerdict, context: ReviewScopeContext): ReviewerVerdict;
@@ -50,6 +50,34 @@ export type VerdictKind = 'APPROVE' | 'REJECT' | 'REVISE' | 'ESCALATE';
50
50
  * UUID string, no envelope.
51
51
  */
52
52
  export type VerdictId = string;
53
+ export type ReviewScopeMode = 'comprehensive' | 'delta';
54
+ export type ReviewScopeResetReason = 'initial' | 'explicit' | 'seat_baseline';
55
+ export type ReviewFindingOrigin = 'baseline' | 'prior' | 'revision' | 'regression';
56
+ export interface ReviewScopePriorFinding {
57
+ finding_id: string;
58
+ text: string;
59
+ }
60
+ export interface ReviewScopeContext {
61
+ schema_version: number;
62
+ mode: ReviewScopeMode;
63
+ reset_reason: ReviewScopeResetReason | null;
64
+ candidate_sha256: string;
65
+ prior_candidate_sha256: string | null;
66
+ prior_gate_id: string | null;
67
+ prior_verdict_id: string | null;
68
+ prior_findings: ReviewScopePriorFinding[];
69
+ }
70
+ export type ReviewScopeIdentity = Omit<ReviewScopeContext, 'prior_findings'>;
71
+ export interface ReviewBlockingFinding {
72
+ suggestion_index: number;
73
+ origin: ReviewFindingOrigin;
74
+ prior_finding_id: string | null;
75
+ }
76
+ export interface ReviewScopeAttestation {
77
+ context: ReviewScopeIdentity;
78
+ blocking_findings: ReviewBlockingFinding[];
79
+ late_preexisting_findings: string[];
80
+ }
53
81
  /**
54
82
  * One reviewer's verdict at one gate. Records reasoning plus perf/cost
55
83
  * metadata for telemetry (review latency, token cost per task, etc.).
@@ -104,4 +132,6 @@ export interface ReviewerVerdict {
104
132
  submitted_at: string;
105
133
  /** Canonical input/output/cache/elapsed usage snapshot. Additive post-launch. */
106
134
  usage?: InvocationUsageSnapshot;
135
+ /** P24 server-bound review scope and finding provenance. Append-only. */
136
+ review_scope?: ReviewScopeAttestation | null;
107
137
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-core",
3
- "version": "2.0.12",
3
+ "version": "2.0.14",
4
4
  "description": "Core library for CodeVibe plugins - shared keychain, crypto, AppSync, and auth functionality",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -332,7 +332,7 @@ endif
332
332
 
333
333
  quiet_cmd_regen_makefile = ACTION Regenerating $@
334
334
  cmd_regen_makefile = cd $(srcdir); /opt/homebrew/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/hendryyeh/Library/Caches/node-gyp/24.4.0" "-Dnode_gyp_dir=/opt/homebrew/lib/node_modules/npm/node_modules/node-gyp" "-Dnode_lib_file=/Users/hendryyeh/Library/Caches/node-gyp/24.4.0/<(target_arch)/node.lib" "-Dmodule_root_dir=/Users/hendryyeh/Workspace/CodeVibe/codevibe-claude-plugin/node_modules/fs-ext" "-Dnode_engine=v8" "--depth=." "-Goutput_dir=." "--generator-output=build" -I/Users/hendryyeh/Workspace/CodeVibe/codevibe-claude-plugin/node_modules/fs-ext/build/config.gypi -I/opt/homebrew/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/Users/hendryyeh/Library/Caches/node-gyp/24.4.0/include/node/common.gypi "--toplevel-dir=." binding.gyp
335
- Makefile: $(srcdir)/../../../../../../../opt/homebrew/lib/node_modules/npm/node_modules/node-gyp/addon.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp $(srcdir)/../../../../../Library/Caches/node-gyp/24.4.0/include/node/common.gypi
335
+ Makefile: $(srcdir)/../../../../../../../opt/homebrew/lib/node_modules/npm/node_modules/node-gyp/addon.gypi $(srcdir)/build/config.gypi $(srcdir)/../../../../../Library/Caches/node-gyp/24.4.0/include/node/common.gypi $(srcdir)/binding.gyp
336
336
  $(call do_cmd,regen_makefile)
337
337
 
338
338
  # "all" is a concatenation of the "all" targets from all the included
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-claude-plugin",
3
- "version": "2.0.14",
3
+ "version": "2.0.16",
4
4
  "description": "Control Claude Code from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
5
5
  "main": "dist/server.js",
6
6
  "codevibe": {
@@ -47,7 +47,7 @@
47
47
  "node": ">=22.0.0"
48
48
  },
49
49
  "dependencies": {
50
- "@quantiya/codevibe-core": "2.0.12",
50
+ "@quantiya/codevibe-core": "2.0.14",
51
51
  "@quantiya/quorum-core": "^1.0.1",
52
52
  "dotenv": "^16.6.1",
53
53
  "express": "^5.1.0",