@quolu/lattice 0.12.21 → 0.12.22
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 +1 -1
- package/src/cli-help.mjs +2 -0
- package/src/todo-cli.mjs +12 -1
- package/src/todo-status.mjs +60 -0
package/package.json
CHANGED
package/src/cli-help.mjs
CHANGED
|
@@ -45,6 +45,7 @@ Commands:
|
|
|
45
45
|
|
|
46
46
|
Read commands:
|
|
47
47
|
status [--json]
|
|
48
|
+
bindings [--plan <key>] [--json] # compile_binding付きTaskをTODO identityつきで投影する
|
|
48
49
|
verify [--plan <key>] [--json]
|
|
49
50
|
snapshot --rebuild --plan <key>
|
|
50
51
|
gantt [--out <file>] [--scope live|all] # 既定live: 完走した工程を図から除く(一覧には残る)
|
|
@@ -110,6 +111,7 @@ const SUBCOMMAND_USAGE = Object.freeze({
|
|
|
110
111
|
'run list': 'run list --json',
|
|
111
112
|
'event verify': 'event verify --run .lattice/runs/<id>',
|
|
112
113
|
'todo status': 'todo status [--json]',
|
|
114
|
+
'todo bindings': 'todo bindings [--plan <key>] [--json]',
|
|
113
115
|
'todo verify': 'todo verify [--plan <key>] [--json]',
|
|
114
116
|
'todo snapshot': 'todo snapshot --rebuild --plan <key>',
|
|
115
117
|
'todo gantt': 'todo gantt [--out <file>] [--scope live|all] | status [--out <file>] | serve --port <port> [--scope live|all]',
|
package/src/todo-cli.mjs
CHANGED
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
appendTodoExtraction,
|
|
47
47
|
validateTodoExtraction,
|
|
48
48
|
} from './todo-migration.mjs';
|
|
49
|
-
import { projectTodoStatus } from './todo-status.mjs';
|
|
49
|
+
import { projectTodoBindings, projectTodoStatus } from './todo-status.mjs';
|
|
50
50
|
import {
|
|
51
51
|
parseTodoSourceRef, todoLegacyReconciliationDigest, validatePhaseTodoRevision,
|
|
52
52
|
validateTodoRevision, validateTodoRevisionSet,
|
|
@@ -474,6 +474,10 @@ async function status({ repoRoot }) {
|
|
|
474
474
|
return projectTodoStatus(await readTodoStore({ repoRoot }));
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
+
async function bindings({ repoRoot, requestedPlanKey }) {
|
|
478
|
+
return projectTodoBindings(await readTodoStore({ repoRoot }), { requestedPlanKey });
|
|
479
|
+
}
|
|
480
|
+
|
|
477
481
|
async function readNarrative(repoRoot, ref) {
|
|
478
482
|
const canonicalRoot = await realpath(repoRoot);
|
|
479
483
|
const source = parseTodoSourceRef(ref);
|
|
@@ -926,6 +930,13 @@ export async function runTodoCli({ argv, cwd, stdout, stderr, env = process.env
|
|
|
926
930
|
if ((argv.length === 1 && argv[0] === 'status')
|
|
927
931
|
|| (argv.length === 2 && argv[0] === 'status' && argv[1] === '--json')) {
|
|
928
932
|
action = (repoRoot) => status({ repoRoot });
|
|
933
|
+
} else if ((argv.length === 1 && argv[0] === 'bindings')
|
|
934
|
+
|| (argv.length === 2 && argv[0] === 'bindings' && argv[1] === '--json')) {
|
|
935
|
+
action = (repoRoot) => bindings({ repoRoot, requestedPlanKey: null });
|
|
936
|
+
} else if ((argv.length === 3 || argv.length === 4) && argv[0] === 'bindings'
|
|
937
|
+
&& argv[1] === '--plan' && isTodoIdentifier(argv[2])
|
|
938
|
+
&& (argv.length === 3 || argv[3] === '--json')) {
|
|
939
|
+
action = (repoRoot) => bindings({ repoRoot, requestedPlanKey: argv[2] });
|
|
929
940
|
} else if ((argv.length === 1 && argv[0] === 'verify')
|
|
930
941
|
|| (argv.length === 2 && argv[0] === 'verify' && argv[1] === '--json')) {
|
|
931
942
|
action = (repoRoot) => verify({ repoRoot, requestedPlanKey: null });
|
package/src/todo-status.mjs
CHANGED
|
@@ -338,3 +338,63 @@ export function projectTodoStatus(readModel) {
|
|
|
338
338
|
}
|
|
339
339
|
return result;
|
|
340
340
|
}
|
|
341
|
+
|
|
342
|
+
export const TODO_BINDING_PROJECTION_SCHEMA = 'lattice.todo_binding_projection.v1';
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* `compile_binding`が設定されたTaskだけを、TODO正本のidentityつきで投影する(ADR 0124)。
|
|
346
|
+
*
|
|
347
|
+
* これはTODO工程storeとruntime実行を結ぶ唯一の公開読み取り面である。host は
|
|
348
|
+
* `compiled_plan_digest`で`runtime_plan.v1`を、`base_sha`でrun requestのbaseを照合し、
|
|
349
|
+
* plan→`executor_packet.v1`→`executor_receipt.v1`(`packet_digest`帰属)まで辿れる。
|
|
350
|
+
*
|
|
351
|
+
* `todo_status_result.v4`は変更しない。binding投影は加算の別面とし、v4を受理する
|
|
352
|
+
* 既存hostを壊さない。
|
|
353
|
+
*/
|
|
354
|
+
export function projectTodoBindings(readModel, { requestedPlanKey = null } = {}) {
|
|
355
|
+
if (!plain(readModel) || readModel.schema !== 'lattice.todo_store_read.v1'
|
|
356
|
+
|| !isTodoIdentifier(readModel.project_id) || !Array.isArray(readModel.members)) {
|
|
357
|
+
fail('TODO_STATUS_INVALID_INPUT', 'todo_status_read_model_invalid');
|
|
358
|
+
}
|
|
359
|
+
if (requestedPlanKey !== null && !isTodoIdentifier(requestedPlanKey)) {
|
|
360
|
+
fail('TODO_STATUS_INVALID_INPUT', 'todo_binding_plan_key_invalid');
|
|
361
|
+
}
|
|
362
|
+
const members = [...readModel.members].sort((left, right) => {
|
|
363
|
+
const leftKey = left?.plan?.plan_key ?? '';
|
|
364
|
+
const rightKey = right?.plan?.plan_key ?? '';
|
|
365
|
+
return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
|
|
366
|
+
});
|
|
367
|
+
const bindings = [];
|
|
368
|
+
let matchedPlan = requestedPlanKey === null;
|
|
369
|
+
for (const member of members) {
|
|
370
|
+
const plan = member?.plan;
|
|
371
|
+
if (!plain(plan) || !Array.isArray(plan.tasks)) continue;
|
|
372
|
+
if (requestedPlanKey !== null && plan.plan_key !== requestedPlanKey) continue;
|
|
373
|
+
if (requestedPlanKey !== null) matchedPlan = true;
|
|
374
|
+
for (const task of plan.tasks) {
|
|
375
|
+
if (!plain(task) || task.compile_binding === null || task.compile_binding === undefined) continue;
|
|
376
|
+
bindings.push({
|
|
377
|
+
project_id: plan.project_id,
|
|
378
|
+
plan_key: plan.plan_key,
|
|
379
|
+
plan_version: plan.plan_version,
|
|
380
|
+
task_id: task.task_id,
|
|
381
|
+
compile_binding: task.compile_binding,
|
|
382
|
+
});
|
|
383
|
+
if (bindings.length > TODO_STATUS_LIST_LIMIT) {
|
|
384
|
+
fail('TODO_SCALE_EXCEEDED', 'todo_binding_projection_limit_exceeded', {
|
|
385
|
+
binding_limit: TODO_STATUS_LIST_LIMIT,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (!matchedPlan) fail('TODO_STATUS_INVALID_INPUT', 'todo_binding_plan_not_found');
|
|
391
|
+
const result = {
|
|
392
|
+
schema: TODO_BINDING_PROJECTION_SCHEMA,
|
|
393
|
+
project_id: readModel.project_id,
|
|
394
|
+
plan_key: requestedPlanKey,
|
|
395
|
+
bindings,
|
|
396
|
+
result_digest: '',
|
|
397
|
+
};
|
|
398
|
+
result.result_digest = todoSelfDigest(result, 'result_digest');
|
|
399
|
+
return result;
|
|
400
|
+
}
|