@quolu/lattice 0.58.3 → 0.58.4

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.3",
3
+ "version": "0.58.4",
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/cli-help.mjs CHANGED
@@ -113,8 +113,10 @@ Write commands:
113
113
  # 検査済みplanned sourceをcanonical refへ保存する。compile成功までは有効化しない
114
114
  structure compile --plan <key> --input <file>
115
115
  # source graph・Git provenance・ToDo DAGを結合する。consistent時だけplanへimmutableに有効化する
116
+ structure realize --plan <key> --task <id> (--planned|--realized <actual-structure.json>) [--commit <HEAD|sha>]...
117
+ # AIはplannedどおりか実体構造だけを判断する。identity・HEAD・履歴鎖・digest・actor・時刻は機械生成する
116
118
  structure realize --plan <key> --task <id> --input <file>
117
- # 実装後の構造をappend-onlyで記録してからtodo doneへ進む
119
+ # 完全なrealization envelopeを移送・再生する互換入口
118
120
  structure finalize --plan <key> --json
119
121
  # 全対象task完了後、最終HEADと全realizationを再結合する。fresh consistentだけterminal受理へ進む
120
122
  seam-proposal compile --plan <key> # 並列可否記録と実sensorからseam提案を記録する
@@ -236,7 +238,7 @@ const SUBCOMMAND_USAGE = Object.freeze({
236
238
  'todo note list': 'todo note list --plan <key> [--task <id>] --json',
237
239
  'todo bindings': 'todo bindings [--plan <key>] [--json]',
238
240
  'todo independence': 'todo independence [--plan <key>] [--json] | compile --plan <key> --input <file> | witness migrate --plan <key>',
239
- 'todo structure': 'todo structure --schema --json | [--plan <key>] --json | input --plan <key> --input <file> [--dry-run --json] | compile --plan <key> --input <file> | realize --plan <key> --task <id> --input <file> | finalize --plan <key> --json',
241
+ 'todo structure': 'todo structure --schema --json | [--plan <key>] --json | input --plan <key> --input <file> [--dry-run --json] | compile --plan <key> --input <file> | realize --plan <key> --task <id> (--planned|--realized <actual-structure.json>) [--commit <HEAD|sha>]... | realize --plan <key> --task <id> --input <full-realization.json> | finalize --plan <key> --json',
240
242
  'todo seam-profile': 'todo seam-profile --plan <key> --file <path> [--json]',
241
243
  'todo seam-proposal': 'todo seam-proposal [--plan <key>] [--json] | compile --plan <key>',
242
244
  'todo verify': 'todo verify [--plan <key>] [--json]',
package/src/todo-cli.mjs CHANGED
@@ -74,6 +74,10 @@ import {
74
74
  } from './todo-store.mjs';
75
75
  import {
76
76
  TODO_STRUCTURE_BINDING_SCHEMA,
77
+ TODO_STRUCTURE_LIMITS,
78
+ TODO_STRUCTURE_REALIZATION_SCHEMA,
79
+ digestTodoStructureTransform,
80
+ explainTodoStructureTransform,
77
81
  explainTodoStructureRealization,
78
82
  explainTodoStructureSet,
79
83
  } from './todo-structure-contracts.mjs';
@@ -724,7 +728,10 @@ async function startStructureContext({ repoRoot, store, planKey, taskId }) {
724
728
  structure_set_digest: source.structure_set_digest,
725
729
  task: structuredClone(task),
726
730
  next_actions: task.applicability === 'graph'
727
- ? [`lattice todo structure realize --plan ${planKey} --task ${taskId} --input <realization.json>`]
731
+ ? [
732
+ `lattice todo structure realize --plan ${planKey} --task ${taskId} --planned`,
733
+ `lattice todo structure realize --plan ${planKey} --task ${taskId} --realized <actual-structure.json>`,
734
+ ]
728
735
  : [],
729
736
  };
730
737
  }
@@ -1998,6 +2005,31 @@ function structurePlanMember(store, requestedPlanKey) {
1998
2005
  return store.members[0];
1999
2006
  }
2000
2007
 
2008
+ function parseAutomatedStructureRealizeArgs(argv) {
2009
+ if (argv[0] !== 'structure' || argv[1] !== 'realize'
2010
+ || argv[2] !== '--plan' || !isTodoIdentifier(argv[3])
2011
+ || argv[4] !== '--task' || !isTodoIdentifier(argv[5])) return null;
2012
+ let cursor;
2013
+ let usePlanned;
2014
+ let realizedRef = null;
2015
+ if (argv[6] === '--planned') {
2016
+ cursor = 7; usePlanned = true;
2017
+ } else if (argv[6] === '--realized' && isTodoRef(argv[7])) {
2018
+ cursor = 8; usePlanned = false; realizedRef = argv[7];
2019
+ } else {
2020
+ return null;
2021
+ }
2022
+ const commitRefs = [];
2023
+ while (cursor < argv.length) {
2024
+ if (argv[cursor] !== '--commit' || typeof argv[cursor + 1] !== 'string') return null;
2025
+ commitRefs.push(argv[cursor + 1]);
2026
+ cursor += 2;
2027
+ }
2028
+ return {
2029
+ planKey: argv[3], taskId: argv[5], usePlanned, realizedRef, commitRefs,
2030
+ };
2031
+ }
2032
+
2001
2033
  function structureNextActions({ coverage, planKey, findings = [], staleReasons = [] }) {
2002
2034
  const actions = [];
2003
2035
  const add = (value) => { if (!actions.includes(value)) actions.push(value); };
@@ -2013,14 +2045,16 @@ function structureNextActions({ coverage, planKey, findings = [], staleReasons =
2013
2045
  if (staleReasons.includes('realization_head_digest')) {
2014
2046
  add(`lattice todo structure --plan ${planKey} --json`);
2015
2047
  } else {
2016
- add(`lattice todo structure realize --plan ${planKey} --task <task-id> --input <realization.json>`);
2048
+ add(`lattice todo structure realize --plan ${planKey} --task <task-id> --planned`);
2049
+ add(`lattice todo structure realize --plan ${planKey} --task <task-id> --realized <actual-structure.json>`);
2017
2050
  }
2018
2051
  add(`lattice todo structure finalize --plan ${planKey} --json`);
2019
2052
  } else if (coverage === 'superseded') {
2020
2053
  add(`lattice todo structure input --plan ${planKey} --input <migrated-structure-set.json> --dry-run --json`);
2021
2054
  } else if (coverage === 'consistent') {
2022
2055
  add(`lattice todo structure --plan ${planKey} --json`);
2023
- add(`lattice todo structure realize --plan ${planKey} --task <task-id> --input <realization.json>`);
2056
+ add(`lattice todo structure realize --plan ${planKey} --task <task-id> --planned`);
2057
+ add(`lattice todo structure realize --plan ${planKey} --task <task-id> --realized <actual-structure.json>`);
2024
2058
  add(`lattice todo structure finalize --plan ${planKey} --json`);
2025
2059
  }
2026
2060
  return actions;
@@ -2134,6 +2168,24 @@ async function readStructureRealizationInput(repoRoot, inputRef) {
2134
2168
  });
2135
2169
  }
2136
2170
 
2171
+ async function readStructureRealizedTransformInput(repoRoot, inputRef) {
2172
+ let explained = null;
2173
+ return readJsonInput(repoRoot, inputRef, {
2174
+ validate: (value) => {
2175
+ explained = explainTodoStructureTransform(value);
2176
+ return explained.valid;
2177
+ },
2178
+ invalidCode: 'INVALID_TODO_STRUCTURE_TRANSFORM',
2179
+ }).catch((error) => {
2180
+ if (error?.code === 'INVALID_TODO_STRUCTURE_TRANSFORM' && explained !== null) {
2181
+ throw new TodoStoreError(error.code, explained.reason, undefined, {
2182
+ input_ref: inputRef, path: explained.path,
2183
+ });
2184
+ }
2185
+ throw error;
2186
+ });
2187
+ }
2188
+
2137
2189
  async function readCurrentStructureEffective(repoRoot, structureSet) {
2138
2190
  const realizations = [];
2139
2191
  for (const task of structureSet.tasks.filter(({ applicability }) => applicability === 'graph')) {
@@ -2144,22 +2196,7 @@ async function readCurrentStructureEffective(repoRoot, structureSet) {
2144
2196
  return projectTodoStructureEffective({ structureSet, realizations });
2145
2197
  }
2146
2198
 
2147
- async function structureRealize({ repoRoot, env, planKey, taskId, inputRef }) {
2148
- const realization = await readStructureRealizationInput(repoRoot, inputRef);
2149
- const actor = mutationActor(env);
2150
- if (realization.plan_key !== planKey || realization.task_id !== taskId) {
2151
- throw new TodoStoreError('STRUCTURE_REALIZATION_BINDING_MISMATCH',
2152
- 'cli_target_mismatch', undefined, {
2153
- expected: { plan_key: planKey, task_id: taskId },
2154
- actual: { plan_key: realization.plan_key, task_id: realization.task_id },
2155
- });
2156
- }
2157
- if (canonicalizeTodoArtifact(realization.actor) !== canonicalizeTodoArtifact(actor)) {
2158
- throw new TodoStoreError('STRUCTURE_REALIZATION_BINDING_MISMATCH',
2159
- 'actor_environment_mismatch', undefined, {
2160
- expected_actor: actor, actual_actor: realization.actor,
2161
- });
2162
- }
2199
+ async function recordStructureRealization({ repoRoot, planKey, taskId, realization }) {
2163
2200
  const appended = await appendTodoStructureRealization({ repoRoot, realization });
2164
2201
  const structureSet = await readTodoStructureSource({ repoRoot, planKey });
2165
2202
  const effective = await readCurrentStructureEffective(repoRoot, structureSet);
@@ -2180,6 +2217,103 @@ async function structureRealize({ repoRoot, env, planKey, taskId, inputRef }) {
2180
2217
  return result;
2181
2218
  }
2182
2219
 
2220
+ async function structureRealize({ repoRoot, env, planKey, taskId, inputRef }) {
2221
+ const realization = await readStructureRealizationInput(repoRoot, inputRef);
2222
+ const actor = mutationActor(env);
2223
+ if (realization.plan_key !== planKey || realization.task_id !== taskId) {
2224
+ throw new TodoStoreError('STRUCTURE_REALIZATION_BINDING_MISMATCH',
2225
+ 'cli_target_mismatch', undefined, {
2226
+ expected: { plan_key: planKey, task_id: taskId },
2227
+ actual: { plan_key: realization.plan_key, task_id: realization.task_id },
2228
+ });
2229
+ }
2230
+ if (canonicalizeTodoArtifact(realization.actor) !== canonicalizeTodoArtifact(actor)) {
2231
+ throw new TodoStoreError('STRUCTURE_REALIZATION_BINDING_MISMATCH',
2232
+ 'actor_environment_mismatch', undefined, {
2233
+ expected_actor: actor, actual_actor: realization.actor,
2234
+ });
2235
+ }
2236
+ return recordStructureRealization({ repoRoot, planKey, taskId, realization });
2237
+ }
2238
+
2239
+ function resolveStructureRealizationCommits(repoRoot, refs) {
2240
+ const requested = refs.length === 0 ? ['HEAD'] : refs;
2241
+ if (requested.length > TODO_STRUCTURE_LIMITS.commitsPerRealization) {
2242
+ throw new TodoStoreError('STRUCTURE_REALIZATION_COMMIT_REF_INVALID',
2243
+ 'commit_ref_count_exceeds_limit', undefined, {
2244
+ actual: requested.length, limit: TODO_STRUCTURE_LIMITS.commitsPerRealization,
2245
+ });
2246
+ }
2247
+ const resolved = [];
2248
+ for (const ref of requested) {
2249
+ if (!(ref === 'HEAD' || /^[0-9a-f]{40}$/u.test(ref))) {
2250
+ throw new TodoStoreError('STRUCTURE_REALIZATION_COMMIT_REF_INVALID',
2251
+ 'commit_ref_must_be_head_or_full_sha', undefined, { commit_ref: ref });
2252
+ }
2253
+ let oid;
2254
+ try {
2255
+ oid = gitSync(['rev-parse', '--verify', `${ref}^{commit}`], {
2256
+ cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
2257
+ }).trim();
2258
+ } catch {
2259
+ throw new TodoStoreError('STRUCTURE_REALIZATION_COMMIT_REF_INVALID',
2260
+ 'commit_ref_unresolved', undefined, { commit_ref: ref });
2261
+ }
2262
+ if (!/^[0-9a-f]{40}$/u.test(oid)) {
2263
+ throw new TodoStoreError('STRUCTURE_REALIZATION_COMMIT_REF_INVALID',
2264
+ 'commit_ref_resolved_invalid_oid', undefined, { commit_ref: ref });
2265
+ }
2266
+ if (resolved.includes(oid)) {
2267
+ throw new TodoStoreError('STRUCTURE_REALIZATION_COMMIT_REF_INVALID',
2268
+ 'duplicate_commit_ref', undefined, { commit_ref: ref, commit_oid: oid });
2269
+ }
2270
+ resolved.push(oid);
2271
+ }
2272
+ return resolved.sort();
2273
+ }
2274
+
2275
+ async function structureRealizeActual({
2276
+ repoRoot, env, planKey, taskId, realizedRef, usePlanned, commitRefs,
2277
+ }) {
2278
+ const structureSet = await readTodoStructureSource({ repoRoot, planKey });
2279
+ if (structureSet === null) {
2280
+ throw new TodoStoreError('STRUCTURE_REALIZATION_BINDING_MISMATCH',
2281
+ 'planned_structure_source_missing', undefined, { plan_key: planKey });
2282
+ }
2283
+ const task = structureSet.tasks.find(({ task_id: id }) => id === taskId);
2284
+ if (task?.applicability !== 'graph') {
2285
+ throw new TodoStoreError('STRUCTURE_REALIZATION_BINDING_MISMATCH',
2286
+ 'task_not_graph_applicable', undefined, { plan_key: planKey, task_id: taskId });
2287
+ }
2288
+ const realized = usePlanned
2289
+ ? structuredClone(task.planned)
2290
+ : await readStructureRealizedTransformInput(repoRoot, realizedRef);
2291
+ const chain = await readTodoStructureRealizationChain({
2292
+ repoRoot, structureSet, taskId,
2293
+ });
2294
+ const previous = chain.at(-1) ?? null;
2295
+ const realization = {
2296
+ schema: TODO_STRUCTURE_REALIZATION_SCHEMA,
2297
+ project_id: structureSet.project_id,
2298
+ plan_key: structureSet.plan_key,
2299
+ plan_version: structureSet.plan_version,
2300
+ task_id: taskId,
2301
+ sequence: (previous?.sequence ?? 0) + 1,
2302
+ previous_digest: previous?.realization_digest ?? null,
2303
+ structure_set_digest: structureSet.structure_set_digest,
2304
+ planned_digest: digestTodoStructureTransform(task.planned),
2305
+ head_sha: currentHeadSha(repoRoot),
2306
+ commit_oids: resolveStructureRealizationCommits(repoRoot, commitRefs),
2307
+ realized,
2308
+ supersedes: previous?.realization_digest ?? null,
2309
+ actor: mutationActor(env),
2310
+ recorded_at: new Date().toISOString(),
2311
+ realization_digest: '',
2312
+ };
2313
+ realization.realization_digest = todoSelfDigest(realization, 'realization_digest');
2314
+ return recordStructureRealization({ repoRoot, planKey, taskId, realization });
2315
+ }
2316
+
2183
2317
  async function structureFinalize({ repoRoot, env, planKey }) {
2184
2318
  const store = await readTodoStore({ repoRoot });
2185
2319
  const member = structurePlanMember(store, planKey);
@@ -2215,7 +2349,7 @@ async function structureFinalize({ repoRoot, env, planKey }) {
2215
2349
  throw new TodoStoreError('STRUCTURE_FINALIZATION_UNAVAILABLE',
2216
2350
  'realization_missing', undefined, {
2217
2351
  plan_key: planKey, task_id: task.task_id,
2218
- next_action: `lattice todo structure realize --plan ${planKey} --task ${task.task_id} --input <realization.json>`,
2352
+ next_action: `lattice todo structure realize --plan ${planKey} --task ${task.task_id} --realized <actual-structure.json>`,
2219
2353
  });
2220
2354
  }
2221
2355
  realizations.push(...chain);
@@ -3479,6 +3613,14 @@ export async function runTodoCli({ argv, cwd, stdout, stderr, env = process.env
3479
3613
  });
3480
3614
  }
3481
3615
 
3616
+ if (argv[0] === 'structure' && argv[1] === 'realize'
3617
+ && argv[6] === '--realized' && typeof argv[7] === 'string' && path.isAbsolute(argv[7])) {
3618
+ return typedArgumentFailure(stderr, 'INPUT_OUTSIDE_REPOSITORY', 'absolute_input_path_rejected', {
3619
+ argument: '--realized', expected: 'repo-relative path', actual: 'absolute path',
3620
+ next_action: 'place_the_input_inside_the_repository_and_pass_a_repo_relative_path',
3621
+ });
3622
+ }
3623
+
3482
3624
  // `--schema --json`はstoreを読まない決定的な出力(`plan create --schema`と同じ規律)。
3483
3625
  // 通常dispatchより前に処理し、repoRoot解決やdashboard daemon起動を経由させない。
3484
3626
  if (argv.length === 3 && argv[1] === '--schema' && argv[2] === '--json'
@@ -3513,6 +3655,7 @@ export async function runTodoCli({ argv, cwd, stdout, stderr, env = process.env
3513
3655
  }
3514
3656
  }
3515
3657
 
3658
+ const automatedStructureRealize = parseAutomatedStructureRealizeArgs(argv);
3516
3659
  let action = null;
3517
3660
  if ((argv.length === 1 && argv[0] === 'status')
3518
3661
  || (argv.length === 2 && argv[0] === 'status' && argv[1] === '--json')) {
@@ -3615,6 +3758,10 @@ export async function runTodoCli({ argv, cwd, stdout, stderr, env = process.env
3615
3758
  action = (repoRoot) => structureRealize({
3616
3759
  repoRoot, env, planKey: argv[3], taskId: argv[5], inputRef: argv[7],
3617
3760
  });
3761
+ } else if (automatedStructureRealize !== null) {
3762
+ action = (repoRoot) => structureRealizeActual({
3763
+ repoRoot, env, ...automatedStructureRealize,
3764
+ });
3618
3765
  } else if (argv.length === 5 && argv[0] === 'structure' && argv[1] === 'finalize'
3619
3766
  && argv[2] === '--plan' && isTodoIdentifier(argv[3]) && argv[4] === '--json') {
3620
3767
  action = (repoRoot) => structureFinalize({ repoRoot, env, planKey: argv[3] });
@@ -1640,7 +1640,7 @@ async function enforceTodoStructureLifecycleGate(repoRoot, member, eventInput) {
1640
1640
  if (latest === undefined) {
1641
1641
  fail('STRUCTURE_REALIZATION_REQUIRED', 'fresh_realization_missing', {
1642
1642
  plan_key: member.plan.plan_key, task_id: eventInput.task_id,
1643
- next_action: `lattice todo structure realize --plan ${member.plan.plan_key} --task ${eventInput.task_id} --input <realization.json>`,
1643
+ next_action: `lattice todo structure realize --plan ${member.plan.plan_key} --task ${eventInput.task_id} --realized <actual-structure.json>`,
1644
1644
  });
1645
1645
  }
1646
1646
  let currentHead;
@@ -1655,7 +1655,7 @@ async function enforceTodoStructureLifecycleGate(repoRoot, member, eventInput) {
1655
1655
  fail('STRUCTURE_REALIZATION_REQUIRED', 'realization_head_stale', {
1656
1656
  plan_key: member.plan.plan_key, task_id: eventInput.task_id,
1657
1657
  realization_head_sha: latest.head_sha, current_head_sha: currentHead,
1658
- next_action: `lattice todo structure realize --plan ${member.plan.plan_key} --task ${eventInput.task_id} --input <realization.json>`,
1658
+ next_action: `lattice todo structure realize --plan ${member.plan.plan_key} --task ${eventInput.task_id} --realized <actual-structure.json>`,
1659
1659
  });
1660
1660
  }
1661
1661
  }
@@ -467,6 +467,11 @@ export const digestTodoStructureTransform = (value) => todoSelfDigest(
467
467
  { schema: 'lattice.todo_structure_transform.v1', transform: value, digest: '' }, 'digest',
468
468
  );
469
469
 
470
+ /** AIが判断して渡す実体構造だけを、realization envelopeとは独立に検証する。 */
471
+ export function explainTodoStructureTransform(value) {
472
+ return transform(value, '');
473
+ }
474
+
470
475
  export function explainTodoStructureRealization(value, { structureSet = null, previous = null,
471
476
  priorDigests = null } = {}) {
472
477
  try {