@quolu/lattice 0.69.3 → 0.69.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quolu/lattice",
3
- "version": "0.69.3",
3
+ "version": "0.69.5",
4
4
  "description": "Schedulability compiler for multi-agent development: observe real code boundaries, refactor the conflicting seam, recompile the plan for parallel execution",
5
5
  "author": {
6
6
  "name": "Quo / クオ at kitepon.dev",
@@ -219,7 +219,7 @@ function snapshot(options = {}) {
219
219
  version: options.version ?? 'unknown',
220
220
  state_schema_version: STATE_VERSION,
221
221
  cursor: { high_watermark: store.next_sequence - 1, acknowledged_through: store.acknowledged_through, next: rows.at(-1)?.sequence ?? afterCursor },
222
- runtime_errors: rows.filter((record) => record.status === 'open').map(({ error_code, component, status, severity, fingerprint, message_template, count, first_seen, last_seen, state_schema_version }) => ({ error_code, component, status, severity, fingerprint, message_template, occurrence_count: count, first_seen, last_seen, state_schema_version })),
222
+ runtime_errors: rows.filter((record) => record.status === 'open').map(({ product_version, error_code, component, status, severity, fingerprint, message_template, count, first_seen, last_seen, state_schema_version }) => ({ product_version, error_code, component, status, severity, fingerprint, message_template, occurrence_count: count, first_seen, last_seen, state_schema_version })),
223
223
  resolutions: rows.filter((record) => record.status === 'resolved').map(({ fingerprint, resolved_at, reason_code }) => ({ fingerprint, resolved_at, reason_code })),
224
224
  diagnostics: {
225
225
  collection: enabled ? 'enabled' : 'disabled',
@@ -62,7 +62,6 @@ const CROSS_PLAN_RECOVERY_CLAIMS_REF = `${STORE_ROOT_REF}/.cross-plan-recovery`;
62
62
  const CROSS_PLAN_IMPORT_TRANSACTION_SCHEMA = 'lattice.todo_cross_plan_import_transaction.v1';
63
63
  const SOURCE_CUTOVER_BARRIER_REF = `${STORE_ROOT_REF}/source-cutover-recovery.json`;
64
64
  const SOURCE_CUTOVER_RECOVERY_CAPABILITY = Symbol('lattice.todo.source-cutover-recovery');
65
- const EVIDENCE_REPAIR_CAPABILITY = Symbol('lattice.todo.evidence-repair');
66
65
  const MAX_FUTURE_SKEW_MS = 5 * 60 * 1_000;
67
66
  const WRITER_CALLERS = new Set(['g4-migration', 'g5-authoring']);
68
67
  const TODO_REVISION_SCHEMAS = Object.freeze([
@@ -621,8 +620,10 @@ function replay(plan, events, { now = new Date(), verifyEvidence, verifyImportSo
621
620
  const decisionEvidence = event.payload.decision_evidence;
622
621
  const slotEvidence = event.payload.evidence_slots.map((slot) => slot.evidence);
623
622
  pendingVerifications.set(`phase:${event.phase_id}`, () => {
624
- verifyEvidence(decisionEvidence);
625
- for (const evidence of slotEvidence) verifyEvidence(evidence);
623
+ const context = { plan_key: plan.plan_key, phase_id: event.phase_id,
624
+ event_digest: event.event_digest };
625
+ verifyEvidence(decisionEvidence, context);
626
+ for (const evidence of slotEvidence) verifyEvidence(evidence, context);
626
627
  });
627
628
  }
628
629
  state.status = 'accepted'; state.decision_event_digest = event.event_digest;
@@ -633,7 +634,9 @@ function replay(plan, events, { now = new Date(), verifyEvidence, verifyImportSo
633
634
  }
634
635
  if (verifyEvidence) {
635
636
  const decisionEvidence = event.payload.decision_evidence;
636
- pendingVerifications.set(`phase:${event.phase_id}`, () => verifyEvidence(decisionEvidence));
637
+ pendingVerifications.set(`phase:${event.phase_id}`, () => verifyEvidence(decisionEvidence, {
638
+ plan_key: plan.plan_key, phase_id: event.phase_id, event_digest: event.event_digest,
639
+ }));
637
640
  }
638
641
  state.status = 'rejected'; state.decision_event_digest = event.event_digest;
639
642
  state.decision_evidence = event.payload.decision_evidence;
@@ -1222,7 +1225,7 @@ function readEvidenceBlob(absoluteRepo, oid) {
1222
1225
  return entry.bytes;
1223
1226
  }
1224
1227
 
1225
- function evidenceVerifier(manifest, repoRoot, hard, repair = null) {
1228
+ function evidenceVerifier(manifest, repoRoot, hard, newEventDigest = null) {
1226
1229
  const repositories = new Map(manifest.repositories.map((repo) => [repo.repo_id, repo.path]));
1227
1230
  return (descriptor, context = {}) => {
1228
1231
  if (!validateEvidenceDescriptor(descriptor)) fail('STORE_INCONSISTENT', 'evidence_descriptor_invalid');
@@ -1245,15 +1248,8 @@ function evidenceVerifier(manifest, repoRoot, hard, repair = null) {
1245
1248
  if (sha256Bytes(bytes) !== descriptor.content_digest) throw new Error('digest mismatch');
1246
1249
  return true;
1247
1250
  } catch {
1248
- const repairTarget = repair !== null
1249
- && typeof repair.taskId === 'string'
1250
- && context.plan_key === repair.planKey
1251
- && typeof context.task_id === 'string'
1252
- && context.task_id === repair.taskId;
1253
- // evidence promotionだけは、同じtaskの過去eventを新しいhard-verified eventで
1254
- // supersedeするためにここを通る。prospective event自身と他taskは免除しない。
1255
- if (repairTarget
1256
- && (repair.eventDigest === null || context.event_digest !== repair.eventDigest)) return false;
1251
+ // 既存の不達は未検証として残す。新eventの証拠だけは書込み前にhard検証する。
1252
+ if (newEventDigest !== null && context.event_digest !== newEventDigest) return false;
1257
1253
  if (hard) fail('STORE_INCONSISTENT', 'evidence_unverified', {
1258
1254
  ...context,
1259
1255
  next_action: context.plan_key === undefined
@@ -1416,20 +1412,14 @@ function liveReplacementPreservesListStructure(lineBytes, replacement) {
1416
1412
  return source !== null && target !== null && source[1] === target[1] && source[2] === target[2];
1417
1413
  }
1418
1414
 
1419
- function importSourceVerifier(repoRoot, hard, cache = null, repair = null) {
1415
+ function importSourceVerifier(repoRoot, hard, cache = null, newEventDigest = null) {
1420
1416
  return (descriptor, context = {}) => {
1421
1417
  if (!validateTodoImportSource(descriptor)) fail('STORE_INCONSISTENT', 'import_source_descriptor_invalid');
1422
1418
  try {
1423
1419
  pinnedSourceLine(repoRoot, descriptor, cache);
1424
1420
  return true;
1425
1421
  } catch {
1426
- const repairTarget = repair !== null
1427
- && typeof repair.taskId === 'string'
1428
- && context.plan_key === repair.planKey
1429
- && typeof context.task_id === 'string'
1430
- && context.task_id === repair.taskId;
1431
- if (repairTarget
1432
- && (repair.eventDigest === null || context.event_digest !== repair.eventDigest)) return false;
1422
+ if (newEventDigest !== null && context.event_digest !== newEventDigest) return false;
1433
1423
  if (hard) fail('STORE_INCONSISTENT', 'import_source_unverified', {
1434
1424
  ...context,
1435
1425
  next_action: 'verify_source_commit_origin_path_and_line_then_retry',
@@ -1605,14 +1595,8 @@ export async function readTodoStore(options = {}) {
1605
1595
  fail('STORE_INCONSISTENT', 'manifest_revision_binding_mismatch');
1606
1596
  }
1607
1597
  prefetchVerificationObjects(manifest, repoRoot, journal.events, pinnedSourceCache);
1608
- const evidenceRepair = options.evidenceRepairCapability === EVIDENCE_REPAIR_CAPABILITY
1609
- ? options.evidenceRepair ?? null : null;
1610
- const verifyEvidence = evidenceVerifier(
1611
- manifest, repoRoot, options.forWrite === true, evidenceRepair,
1612
- );
1613
- const verifyImportSource = importSourceVerifier(
1614
- repoRoot, options.forWrite === true, pinnedSourceCache, evidenceRepair,
1615
- );
1598
+ const verifyEvidence = evidenceVerifier(manifest, repoRoot, false);
1599
+ const verifyImportSource = importSourceVerifier(repoRoot, false, pinnedSourceCache);
1616
1600
  const tasks = replay(plan, journal.events, {
1617
1601
  now: options.now ? new Date(options.now) : new Date(),
1618
1602
  verifyEvidence: options.forWrite === true ? verifyEvidence : undefined,
@@ -2056,13 +2040,8 @@ export async function appendTodoEvent(options = {}) {
2056
2040
  requireWriter(options.writer, 'g5-authoring');
2057
2041
  const repoRoot = path.resolve(options.repoRoot ?? process.cwd());
2058
2042
  return withLock(repoRoot, async () => {
2059
- const evidenceRepair = options.event.kind === 'done'
2060
- && options.event.payload?.done_mode === 'evidence_promotion'
2061
- ? { planKey: options.planKey, taskId: options.event.task_id, eventDigest: null }
2062
- : null;
2063
2043
  const store = await readTodoStore({
2064
- repoRoot, forWrite: true, now: options.now, evidenceRepair,
2065
- evidenceRepairCapability: EVIDENCE_REPAIR_CAPABILITY,
2044
+ repoRoot, forWrite: true, now: options.now,
2066
2045
  // 修理扉は除去event(tombstone)の書込みだけに開く。それ以外の書込みは従来どおり
2067
2046
  // binding_stale で fail closed する(validateMergedGraph の注記を参照)
2068
2047
  tolerateStaleBindings: options.tolerateStaleBindings === true
@@ -2113,16 +2092,8 @@ export async function appendTodoEvent(options = {}) {
2113
2092
  [...member.journal.events, event], appendSourceCache);
2114
2093
  replay(member.plan, [...member.journal.events, event], {
2115
2094
  now: options.now ? new Date(options.now) : new Date(),
2116
- verifyEvidence: evidenceVerifier(store.manifest, repoRoot, true, {
2117
- planKey: member.plan.plan_key,
2118
- taskId: event.task_id,
2119
- eventDigest: event.event_digest,
2120
- }),
2121
- verifyImportSource: importSourceVerifier(repoRoot, true, appendSourceCache, {
2122
- planKey: member.plan.plan_key,
2123
- taskId: event.task_id,
2124
- eventDigest: event.event_digest,
2125
- }),
2095
+ verifyEvidence: evidenceVerifier(store.manifest, repoRoot, true, event.event_digest),
2096
+ verifyImportSource: importSourceVerifier(repoRoot, true, appendSourceCache, event.event_digest),
2126
2097
  });
2127
2098
  if (options.materializedEvidence !== null && options.materializedEvidence !== undefined) {
2128
2099
  const asset = options.materializedEvidence;
@@ -4270,8 +4241,8 @@ async function applyPhaseTodoRevisionV3(options, revision, repoRoot) {
4270
4241
  await protocolStage(options, 'phase_v3_marker_durable');
4271
4242
  const tasks = replay(revision.desired_plan, [genesis], {
4272
4243
  now: options.now ? new Date(options.now) : new Date(),
4273
- verifyEvidence: evidenceVerifier(store.manifest, repoRoot, true),
4274
- verifyImportSource: importSourceVerifier(repoRoot, true),
4244
+ verifyEvidence: evidenceVerifier(store.manifest, repoRoot, false),
4245
+ verifyImportSource: importSourceVerifier(repoRoot, false),
4275
4246
  });
4276
4247
  const snapshot = snapshotFor(revision.desired_plan, [genesis], tasks);
4277
4248
  for (const [stage, ref, bytes] of [
@@ -4437,8 +4408,8 @@ export async function applyPhaseTodoRevision(options = {}) {
4437
4408
  const journalRef = `${base}/journal/active.jsonl`; const snapshotRef = `${base}/snapshot.json`;
4438
4409
  const tasks = replay(revision.desired_plan, [genesis], {
4439
4410
  now: options.now ? new Date(options.now) : new Date(),
4440
- verifyEvidence: evidenceVerifier(store.manifest, repoRoot, true),
4441
- verifyImportSource: importSourceVerifier(repoRoot, true),
4411
+ verifyEvidence: evidenceVerifier(store.manifest, repoRoot, false),
4412
+ verifyImportSource: importSourceVerifier(repoRoot, false),
4442
4413
  });
4443
4414
  const snapshot = snapshotFor(revision.desired_plan, [genesis], tasks);
4444
4415
  const artifacts = [
@@ -4586,8 +4557,8 @@ export async function applyTodoRevision(options = {}) {
4586
4557
  }
4587
4558
  await protocolStage(options, 'revision_marker_durable');
4588
4559
 
4589
- const verifyEvidence = evidenceVerifier(store.manifest, repoRoot, true);
4590
- const verifyImportSource = importSourceVerifier(repoRoot, true);
4560
+ const verifyEvidence = evidenceVerifier(store.manifest, repoRoot, false);
4561
+ const verifyImportSource = importSourceVerifier(repoRoot, false);
4591
4562
  const tasks = replay(revision.desired_plan, [genesis], {
4592
4563
  now: options.now ? new Date(options.now) : new Date(), verifyEvidence, verifyImportSource,
4593
4564
  });
@@ -4862,8 +4833,8 @@ export async function applyTodoRevisionSet(options = {}) {
4862
4833
  }
4863
4834
  await protocolStage(options, 'revision_set_marker_durable');
4864
4835
 
4865
- const verifyEvidence = evidenceVerifier(store.manifest, repoRoot, true);
4866
- const verifyImportSource = importSourceVerifier(repoRoot, true);
4836
+ const verifyEvidence = evidenceVerifier(store.manifest, repoRoot, false);
4837
+ const verifyImportSource = importSourceVerifier(repoRoot, false);
4867
4838
  for (const entry of prepared) {
4868
4839
  const { revision, genesis } = entry;
4869
4840
  const tasks = replay(revision.desired_plan, [genesis], {