@quolu/lattice 0.58.1 → 0.58.3

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.58.1",
3
+ "version": "0.58.3",
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",
package/src/todo-cli.mjs CHANGED
@@ -646,7 +646,7 @@ function terminalAuditDoneAdvisory(plan, phases) {
646
646
 
647
647
  async function mutate({
648
648
  repoRoot, env, planKey, taskId, kind, payload, evidenceRef, advisory = null,
649
- noteContext = null,
649
+ noteContext = null, structureContext = null,
650
650
  }) {
651
651
  const actor = mutationActor(env);
652
652
  const evidence = evidenceRef === null ? null : await readEvidenceInput(repoRoot, evidenceRef);
@@ -674,7 +674,7 @@ async function mutate({
674
674
  throw new TypeError('start mutation requires note context');
675
675
  }
676
676
  const result = {
677
- schema: includesNoteContext ? 'lattice.todo_mutation_result.v4' : 'lattice.todo_mutation_result.v2',
677
+ schema: includesNoteContext ? 'lattice.todo_mutation_result.v5' : 'lattice.todo_mutation_result.v2',
678
678
  project_id: event.project_id,
679
679
  plan_key: event.plan_key,
680
680
  plan_version: event.plan_version,
@@ -689,6 +689,7 @@ async function mutate({
689
689
  ...(includesNoteContext ? {
690
690
  design_memo: designMemoProjection(authoredTask),
691
691
  note_context: noteContext,
692
+ structure_context: structureContext,
692
693
  } : {}),
693
694
  result_digest: '',
694
695
  };
@@ -696,6 +697,38 @@ async function mutate({
696
697
  return result;
697
698
  }
698
699
 
700
+ async function startStructureContext({ repoRoot, store, planKey, taskId }) {
701
+ const member = store.members.find(({ descriptor }) => descriptor.plan_key === planKey);
702
+ if (member === undefined) throw new TodoStoreError('STORE_INCONSISTENT', 'plan_not_active');
703
+ const source = await readTodoStructureSource({ repoRoot, planKey });
704
+ const binding = await readTodoStructureBinding({
705
+ repoRoot, planKey, planVersion: member.plan.plan_version,
706
+ });
707
+ if (source === null || binding === null) return {
708
+ status: 'not_enabled', enabled: false, freshness: null, stale_reasons: [],
709
+ structure_set_digest: null, task: null, next_actions: [],
710
+ };
711
+ if (source.structure_set_digest !== binding.structure_set_digest) {
712
+ throw new TodoStoreError('STRUCTURE_LIFECYCLE_GATE_FAILED',
713
+ 'enabled_structure_source_unreadable', undefined, { plan_key: planKey });
714
+ }
715
+ const task = source.tasks.find(({ task_id: id }) => id === taskId);
716
+ if (task === undefined) {
717
+ throw new TodoStoreError('STRUCTURE_LIFECYCLE_GATE_FAILED',
718
+ 'enabled_structure_task_missing', undefined, { plan_key: planKey, task_id: taskId });
719
+ }
720
+ const state = await readTodoStructureState({ repoRoot, store, planKey });
721
+ return {
722
+ status: 'available', enabled: true, freshness: state.status,
723
+ stale_reasons: state.stale_reasons,
724
+ structure_set_digest: source.structure_set_digest,
725
+ task: structuredClone(task),
726
+ next_actions: task.applicability === 'graph'
727
+ ? [`lattice todo structure realize --plan ${planKey} --task ${taskId} --input <realization.json>`]
728
+ : [],
729
+ };
730
+ }
731
+
699
732
  function designMemoProjection(task) {
700
733
  if (typeof task.design_memo === 'string') {
701
734
  return { status: 'available', markdown: task.design_memo, prompt: TODO_DESIGN_MEMO_PROMPT };
@@ -898,8 +931,12 @@ async function startTask({
898
931
  const advisory = await startAdvisory({
899
932
  repoRoot, store, projection, planKey, taskId: resolvedTaskId,
900
933
  });
934
+ const structureContext = await startStructureContext({
935
+ repoRoot, store, planKey, taskId: resolvedTaskId,
936
+ });
901
937
  return mutate({ repoRoot, env, planKey, taskId: resolvedTaskId, kind: 'start',
902
- payload: { override_reason: overrideReason }, evidenceRef: null, advisory, noteContext });
938
+ payload: { override_reason: overrideReason }, evidenceRef: null, advisory, noteContext,
939
+ structureContext });
903
940
  }
904
941
 
905
942
  async function retractStart({ repoRoot, env, planKey, taskId, reason }) {
@@ -2035,10 +2072,7 @@ async function structureCompile({ repoRoot, env, planKey, inputRef }) {
2035
2072
  const overlay = compileTodoStructureOverlay({
2036
2073
  structureSet,
2037
2074
  topology: mergedTopology(store),
2038
- taskStates: structureSet.tasks.map(({ task_id: taskId }) => ({
2039
- task_id: taskId,
2040
- status: member.tasks.find(({ task_id: id }) => id === taskId)?.status ?? 'pending',
2041
- })),
2075
+ taskStates: member.tasks.map(({ task_id: taskId, status }) => ({ task_id: taskId, status })),
2042
2076
  sourceProjection: sourceEvidence.projection,
2043
2077
  gitProvenance,
2044
2078
  realizations,
@@ -2196,7 +2230,7 @@ async function structureFinalize({ repoRoot, env, planKey }) {
2196
2230
  const overlay = compileTodoStructureOverlay({
2197
2231
  structureSet: source,
2198
2232
  topology: mergedTopology(store),
2199
- taskStates: source.tasks.map(({ task_id: taskId }) => ({ task_id: taskId, status: 'done' })),
2233
+ taskStates: member.tasks.map(({ task_id: taskId, status }) => ({ task_id: taskId, status })),
2200
2234
  sourceProjection: sourceEvidence.projection,
2201
2235
  gitProvenance,
2202
2236
  realizations,
@@ -62,12 +62,14 @@ function normalizeTaskStates(structureSet, taskStates) {
62
62
  if (!isPlain(entry) || Object.keys(entry).length !== 2
63
63
  || typeof entry.task_id !== 'string'
64
64
  || !['pending', 'in-progress', 'blocked', 'done'].includes(entry.status)
65
- || !expected.has(entry.task_id) || states.has(entry.task_id)) {
65
+ || states.has(entry.task_id)) {
66
66
  fail('STRUCTURE_OVERLAY_INPUT_INVALID', 'task_state_entry_invalid');
67
67
  }
68
68
  states.set(entry.task_id, entry.status);
69
69
  }
70
- if (states.size !== expected.size) fail('STRUCTURE_OVERLAY_INPUT_INVALID', 'task_state_coverage_invalid');
70
+ if ([...expected].some((taskId) => !states.has(taskId))) {
71
+ fail('STRUCTURE_OVERLAY_INPUT_INVALID', 'task_state_coverage_invalid');
72
+ }
71
73
  return states;
72
74
  }
73
75
 
@@ -302,7 +304,7 @@ function buildOverlayGraph(structureSet, states, latestRealizations, sourceProje
302
304
  return { nodes: uniqueNodes, edges, effective };
303
305
  }
304
306
 
305
- function connectionFindings(structureSet, effective, topologyDag) {
307
+ function connectionFindings(structureSet, effective, topologyDag, states) {
306
308
  const findings = [];
307
309
  const externalById = new Map(structureSet.external_contracts
308
310
  .map((external) => [external.contract_id, external]));
@@ -322,7 +324,8 @@ function connectionFindings(structureSet, effective, topologyDag) {
322
324
  const structureTaskIds = new Set(structureSet.tasks.map(({ task_id: id }) => id));
323
325
  const extraTopologyTasks = topologyDag.nodes
324
326
  .filter(({ ref }) => ref.project_id === structureSet.project_id
325
- && ref.plan_key === structureSet.plan_key && !structureTaskIds.has(ref.task_id))
327
+ && ref.plan_key === structureSet.plan_key && !structureTaskIds.has(ref.task_id)
328
+ && states.get(ref.task_id) !== 'done')
326
329
  .map(({ ref }) => ref.task_id).sort(compareText);
327
330
  if (extraTopologyTasks.length > 0) findings.push(finding({
328
331
  code: 'STRUCTURE_COVERAGE_MISSING', severity: 'error', taskIds: extraTopologyTasks,
@@ -557,7 +560,7 @@ export function compileTodoStructureOverlay({
557
560
  const rawFindings = [
558
561
  ...anchorFindings(structureSet, sourceProjection),
559
562
  ...missingEffectiveAnchors,
560
- ...connectionFindings(structureSet, graph.effective, topologyDag),
563
+ ...connectionFindings(structureSet, graph.effective, topologyDag, states),
561
564
  ...realizationFindings(
562
565
  structureSet, states, normalizedRealizations, gitProvenance, graph.effective,
563
566
  ),