@allocator-one/rcl 4.5.19 → 4.5.20

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/dist/index.js CHANGED
@@ -60,7 +60,7 @@ import { writeExclusive, serializeRecoveryDocument } from './evidence/original-r
60
60
  import { readStable, sha256 } from './telemetry/recovery/files.js';
61
61
  import { appendCalls, appendOutcomes, loadModelStats, resolveDataDir, DEFAULT_WINDOW_DAYS, MIN_OUTCOMES_FOR_WEIGHT, } from './models/stats-store.js';
62
62
  import { buildSeedRecords } from './models/seed.js';
63
- import { assignmentLane, buildRoster, configDigest, diffDigest, describeRunTarget, detectRunner, parseSpecSource, resolveConvergeContext, guardedInputSha256, sha256Hex, stableStringify, validateSha, } from './report/run-header.js';
63
+ import { assignmentLane, buildRoster, configDigest, diffDigest, describeRunTarget, detectRunner, parseSpecSource, resolveConvergeContext, guardedInputSha256, sha256Hex, validateSha, } from './report/run-header.js';
64
64
  import { assertExpectedHead, resolveReviewTarget } from './resolver/target.js';
65
65
  import { createTelemetryRuntime, deliverRun, evidenceRequirementConflict, emitConvergeEvents, flushOutbox, flushOutboxAtStart, loadHarnessSettings, resolveTelemetryLevel, } from './telemetry/deliver.js';
66
66
  import { normalizeGeneratedReport, sanitizeForDelivery } from './telemetry/envelope.js';
@@ -1869,13 +1869,14 @@ async function runReview(target, opts) {
1869
1869
  }
1870
1870
  else {
1871
1871
  const prTarget = parseGitHubTarget(target);
1872
- diff = await fetchPRDiff(prTarget, config.githubToken);
1872
+ diff = await fetchPRDiff(prTarget, config.githubToken, undefined, { requireMergeBase: true });
1873
1873
  }
1874
1874
  // Resolve the head BEFORE the empty-diff exit so --expect-head-sha is
1875
1875
  // honored even when there is nothing to review: a moved target must
1876
1876
  // never read as a clean round.
1877
1877
  const runTarget = await resolveReviewTarget(diff, gitMode, {
1878
1878
  ...opts,
1879
+ historicalPendingRecovery: opts.exportPendingPackage !== undefined || opts.finalizePendingOnly === true,
1879
1880
  convergeTarget: opts.convergeTarget ?? process.env['RCL_CONVERGE_TARGET'],
1880
1881
  // The flag is the user's word and is refused off a patch file; a value
1881
1882
  // left in the environment only ever attributes a patch file.
@@ -1958,9 +1959,12 @@ async function executeCouncil(spinner, prepared, diff, opts, extra, preparedWork
1958
1959
  explicit: prepared.explicit, gating: prepared.gatingConfig });
1959
1960
  const guardedInput = {
1960
1961
  head: extra.target.headSha, kind: extra.target.kind, repo: extra.target.repo, pr: extra.target.prNumber,
1962
+ ...(extra.target.baseSha === undefined ? {} : { base: extra.target.baseSha }),
1961
1963
  diff: diffDigest(diff.files), config: configDigest(config), roster, prompts,
1962
1964
  asyncRoles: asyncAssignments.map(assignment => assignment.role), spec: prepared.spec,
1963
1965
  };
1966
+ const { base: _base, ...historicalGuardedInput } = guardedInput;
1967
+ const historicalInputSha256 = guardedInputSha256(historicalGuardedInput);
1964
1968
  const ordinaryAsyncDescriptors = chunks.flatMap(() => asyncAssignments.map(assignment => ({
1965
1969
  model: assignment.model, role: assignment.role.name, provider: assignment.provider,
1966
1970
  }))).slice(0, MAX_ASYNC_CALLS_PER_ROUND);
@@ -1968,17 +1972,26 @@ async function executeCouncil(spinner, prepared, diff, opts, extra, preparedWork
1968
1972
  const common = await resolveGitCommonDir();
1969
1973
  if (opts.exportPendingPackage) {
1970
1974
  const pendingNative = await loadConvergeRunState(common, prepared.converge.target);
1971
- // A launch recorded before shared task context (RCL-191) hashed prompts
1972
- // without it. Offer that exact earlier form too; the export still
1973
- // authenticates only an exact match with the recorded input hash.
1975
+ // Discovery only: export re-reads the native marker and authenticates its
1976
+ // complete retained packet before producing anything. The caller can
1977
+ // confirm this historical base, never nominate an unauthenticated one.
1978
+ const original = pendingNative?.lastLaunch;
1979
+ const retainedBase = original?.headSha === extra.target.headSha ? original?.ordinaryInputs?.baseSha : undefined;
1980
+ const exportBaseSha = retainedBase === opts.expectBaseSha ? retainedBase : extra.target.baseSha;
1981
+ // Only explicit historical export may reconstruct older input forms.
1982
+ // New launches always hash a resolved base; older records may omit it or the
1983
+ // shared task context. Retained receipts still check the base separately.
1974
1984
  let exportInput = guardedInput;
1975
- if (work.taskContext && pendingNative?.lastLaunch?.inputSha256 !== guardedInputSha256(guardedInput)) {
1976
- const earlier = { ...guardedInput, prompts: await Promise.all(chunkAssignments.map(({ assignment, chunk }) => buildPrompt(chunk, assignment.role, { contextDocs, plan: planContext }))) };
1977
- if (pendingNative?.lastLaunch?.inputSha256 === guardedInputSha256(earlier))
1978
- exportInput = earlier;
1985
+ if (pendingNative?.lastLaunch?.inputSha256 !== guardedInputSha256(guardedInput)) {
1986
+ const candidates = [guardedInput, ...(exportBaseSha === undefined ? [] : [{ ...guardedInput, base: exportBaseSha }]), historicalGuardedInput];
1987
+ if (work.taskContext) {
1988
+ const earlierPrompts = await Promise.all(chunkAssignments.map(({ assignment, chunk }) => buildPrompt(chunk, assignment.role, { contextDocs, plan: planContext })));
1989
+ candidates.push(...candidates.map(input => ({ ...input, prompts: earlierPrompts })));
1990
+ }
1991
+ exportInput = candidates.find(input => pendingNative?.lastLaunch?.inputSha256 === guardedInputSha256(input)) ?? guardedInput;
1979
1992
  }
1980
1993
  const receipt = await exportOrdinaryPendingPackage({ gitCommonDir: common,
1981
- target: prepared.converge.target, headSha: extra.target.headSha ?? '', baseSha: extra.target.baseSha ?? '',
1994
+ target: prepared.converge.target, headSha: extra.target.headSha ?? '', baseSha: exportBaseSha ?? '',
1982
1995
  expectedBaseSha: opts.expectBaseSha, expectedRound: prepared.converge.round,
1983
1996
  guardedInput: exportInput, path: opts.exportPendingPackage,
1984
1997
  preview: opts.previewPending === true,
@@ -2028,6 +2041,14 @@ async function executeCouncil(spinner, prepared, diff, opts, extra, preparedWork
2028
2041
  const migrationPackage = opts.ordinaryPendingPackage === undefined ? undefined : JSON.parse((await readStable(opts.ordinaryPendingPackage)).text);
2029
2042
  const migrationGuardedInput = migrationPackage
2030
2043
  ? ordinaryPendingGuardedInput(migrationPackage) : undefined;
2044
+ // Only finalize-only may authenticate the old PR base rather than the
2045
+ // current comparison. Never feed that base into a successor's capture.
2046
+ // Advanced receipt replay remains available via an explicitly bound patch.
2047
+ const retainedBase = native?.lastLaunch?.headSha === extra.target.headSha
2048
+ ? native?.lastLaunch?.ordinaryInputs?.baseSha : undefined;
2049
+ const finalizationBaseSha = opts.finalizePendingOnly && extra.target.kind === 'pr' &&
2050
+ retainedBase !== undefined && retainedBase === migrationPackage?.baseSha
2051
+ ? retainedBase : extra.target.baseSha ?? '';
2031
2052
  if (opts.expectPrHeadSha !== undefined && migrationPackage?.version !== 2) {
2032
2053
  throw new ReviewLaunchRefused('pending_finalize_incompatible', '--expect-pr-head-sha requires a cycle-backed version-2 pending package.');
2033
2054
  }
@@ -2084,7 +2105,7 @@ async function executeCouncil(spinner, prepared, diff, opts, extra, preparedWork
2084
2105
  if (opts.previewPending && migrationPackage) {
2085
2106
  const inputSha256 = guardedInputSha256(migrationGuardedInput);
2086
2107
  const preview = await previewOrdinaryPendingLaunch({ gitCommonDir: common,
2087
- target: prepared.converge.target, headSha: extra.target.headSha ?? '', baseSha: extra.target.baseSha ?? '',
2108
+ target: prepared.converge.target, headSha: extra.target.headSha ?? '', baseSha: finalizationBaseSha,
2088
2109
  pendingInputSha256: inputSha256, recoveryInputSha256: inputSha256,
2089
2110
  retryReason: opts.retryReason ?? 'Finalize-only authentication preview.',
2090
2111
  captured: {}, retainedAsyncSha256: expectedAsyncSha256,
@@ -2104,7 +2125,7 @@ async function executeCouncil(spinner, prepared, diff, opts, extra, preparedWork
2104
2125
  const inputSha256 = guardedInputSha256(migrationGuardedInput);
2105
2126
  const result = await finalizeOrdinaryPendingLaunch({ gitCommonDir: common,
2106
2127
  target: prepared.converge.target, headSha: extra.target.headSha ?? '',
2107
- baseSha: extra.target.baseSha ?? '', pendingInputSha256: inputSha256,
2128
+ baseSha: finalizationBaseSha, pendingInputSha256: inputSha256,
2108
2129
  nativeStateSha256: opts.pendingNativeSha256, attemptStateSha256: opts.pendingAttemptSha256,
2109
2130
  retainedAsyncSha256: expectedAsyncSha256, migrationPackage,
2110
2131
  maxAttempts: Number(opts.maxAttempts), cycleRemote,
@@ -2177,15 +2198,12 @@ async function executeCouncil(spinner, prepared, diff, opts, extra, preparedWork
2177
2198
  } } : {}), });
2178
2199
  const asyncStoreDir = await resolveAsyncStoreDir();
2179
2200
  const asyncKey = asyncTargetKey(extra.asyncTargetLabel ?? prepared.converge.target, extra.target.kind === 'patch' ? prepared.converge.target : undefined, prepared.converge.cycleId);
2180
- const recoveryInputSha256 = sha256Hex(stableStringify({
2181
- head: extra.target.headSha, kind: extra.target.kind, repo: extra.target.repo,
2182
- pr: extra.target.prNumber, diff: diffDigest(diff.files), config: configDigest(config),
2183
- roster, prompts, asyncRoles: asyncAssignments.map(assignment => assignment.role), spec: prepared.spec,
2184
- }));
2201
+ const recoveryInputSha256 = guardedInputSha256(guardedInput);
2185
2202
  const pendingResumeOptions = { gitCommonDir: common,
2186
2203
  target: prepared.converge.target, headSha: extra.target.headSha ?? '',
2187
2204
  baseSha: extra.target.baseSha ?? '',
2188
2205
  pendingInputSha256: claimedInputSha256, recoveryInputSha256,
2206
+ historicalRecoveryInputSha256: historicalInputSha256,
2189
2207
  retryReason: opts.retryReason,
2190
2208
  ...(opts.retryReport ? { legacyRetry: { reportPath: opts.retryReport, config, roster,
2191
2209
  historicalPlan: prepared.historicalRetryPlan === true } } : {}),
@@ -2280,6 +2298,7 @@ async function executeCouncil(spinner, prepared, diff, opts, extra, preparedWork
2280
2298
  recoverySource: prepared.converge.recovery_source,
2281
2299
  headSha: extra.target.headSha ?? '',
2282
2300
  inputSha256: guardedInputSha256(guardedInput),
2301
+ historicalInputSha256,
2283
2302
  round: prepared.converge.round,
2284
2303
  intent: opts.launchIntent,
2285
2304
  retryReason: opts.retryReason,