@quolu/lattice 0.13.0 → 0.15.0
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/bin/lattice.mjs +5 -0
- package/package.json +2 -2
- package/src/cli-help.mjs +4 -0
- package/src/project-cli.mjs +148 -37
- package/src/seam-proposal-contracts.mjs +427 -0
- package/src/seam-proposal-queries.mjs +484 -0
- package/src/seam-proposal.mjs +1763 -0
- package/src/todo-cli.mjs +285 -9
- package/src/todo-gantt-html.mjs +60 -1
- package/src/todo-gantt-layout.mjs +76 -0
- package/src/todo-independence-contracts.mjs +61 -8
- package/src/todo-independence-guidance.mjs +49 -0
- package/src/todo-independence.mjs +71 -10
- package/src/todo-store.mjs +103 -2
package/bin/lattice.mjs
CHANGED
|
@@ -14,6 +14,11 @@ if (help !== null) {
|
|
|
14
14
|
process.stdout.write(help);
|
|
15
15
|
} else if (args.length === 1 && args[0] === '--version') {
|
|
16
16
|
process.stdout.write(`${packageJson.version}\n`);
|
|
17
|
+
} else if (args.length === 2 && args[0] === 'session-context' && args[1] === '--json') {
|
|
18
|
+
const { runSessionContext } = await import('../src/project-cli.mjs');
|
|
19
|
+
process.exitCode = await runSessionContext({
|
|
20
|
+
cwd: process.cwd(), stdout: process.stdout, cliVersion: packageJson.version,
|
|
21
|
+
});
|
|
17
22
|
} else if (args.length === 2 && args[0] === 'status' && args[1] === '--json') {
|
|
18
23
|
const { projectStatusFailure, runProjectStatus } = await import('../src/project-cli.mjs');
|
|
19
24
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quolu/lattice",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Lattice — phase-aware TODO graph compiler and conflict-aware orchestration runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"test": "node scripts/run-product-tests.mjs",
|
|
61
61
|
"test:sensor": "npm --prefix sensor test",
|
|
62
62
|
"precheck": "node --check src/bridge-launch-agent.mjs",
|
|
63
|
-
"check": "node --check bin/lattice.mjs && node --check bin/lattice-mcp.mjs && node --check bin/lattice-dashboard.mjs && node --check bin/lattice-bridge.mjs && node --check bin/lattice-scripted-adapter.mjs && node --check src/cli-stdio.mjs && node --check src/bridge-address.mjs && node --check src/bridge-registrar.mjs && node --check src/todo-independence-guidance.mjs && node --check src/todo-independence.mjs && node --check src/todo-independence-contracts.mjs && node --check src/todo-gantt-layout.mjs && node --check src/todo-gantt-scope.mjs && node --check src/bridge-config.mjs && node --check src/bridge-server.mjs && node --check src/bridge-daemon.mjs && node --check src/bridge-cli.mjs && node --check src/project-cli.mjs && node --check src/sensor-cli.mjs && node --check src/sensor-runtime.mjs && node --check src/sensor-adapter.mjs && node --check src/factory-diagnostics.mjs && node --check src/runtime-errors.mjs && node --check src/runtime-contracts.mjs && node --check src/runtime-event-store.mjs && node --check src/runtime-adapter-registry.mjs && node --check src/runtime-scripted-adapter-controller.mjs && node --check src/hash-chain.mjs && node --check src/dag-chain.mjs && node --check src/todo-contracts.mjs && node --check src/todo-chain.mjs && node --check src/todo-store.mjs && node --check src/todo-migration.mjs && node --check src/todo-revision.mjs && node --check src/todo-status.mjs && node --check src/todo-cli.mjs && node --check src/todo-dashboard-registry.mjs && node --check src/todo-gantt-presentation.mjs && node --check src/todo-gantt-live.mjs && node --check src/bounded-seam.mjs && node --check src/todo-narrative-anchor.mjs && node --check src/todo-markdown-renderer.mjs && node --check src/todo-gantt-svg.mjs && node --check src/todo-gantt-html.mjs && node --check src/runtime-projection.mjs && node --check src/runtime-decision-verifier.mjs && node --check src/runtime-front-end.mjs && node --check src/runtime-cli.mjs && node --check src/rc3-dogfood-scaffold.mjs && node --check src/runtime-engine.mjs && node --check src/runtime-scripted-executor.mjs && node --check src/runtime-diff-observer.mjs && node --check src/runtime-worktree-executor.mjs && node --check src/runtime-hold-recompile.mjs && node --check src/rc3-scripted-campaign.mjs && node --check src/rc3-actual-dogfood.mjs && node --check src/rc4-stage1-dogfood.mjs && node --check research/fixtures/dispatch-record/src/dispatch-record.mjs && node --check test/research-dispatch-record.test.mjs",
|
|
63
|
+
"check": "node --check bin/lattice.mjs && node --check bin/lattice-mcp.mjs && node --check bin/lattice-dashboard.mjs && node --check bin/lattice-bridge.mjs && node --check bin/lattice-scripted-adapter.mjs && node --check src/cli-stdio.mjs && node --check src/bridge-address.mjs && node --check src/bridge-registrar.mjs && node --check src/todo-independence-guidance.mjs && node --check src/todo-independence.mjs && node --check src/todo-independence-contracts.mjs && node --check src/seam-proposal-contracts.mjs && node --check src/seam-proposal-queries.mjs && node --check src/seam-proposal.mjs && node --check src/todo-gantt-layout.mjs && node --check src/todo-gantt-scope.mjs && node --check src/bridge-config.mjs && node --check src/bridge-server.mjs && node --check src/bridge-daemon.mjs && node --check src/bridge-cli.mjs && node --check src/project-cli.mjs && node --check src/sensor-cli.mjs && node --check src/sensor-runtime.mjs && node --check src/sensor-adapter.mjs && node --check src/factory-diagnostics.mjs && node --check src/runtime-errors.mjs && node --check src/runtime-contracts.mjs && node --check src/runtime-event-store.mjs && node --check src/runtime-adapter-registry.mjs && node --check src/runtime-scripted-adapter-controller.mjs && node --check src/hash-chain.mjs && node --check src/dag-chain.mjs && node --check src/todo-contracts.mjs && node --check src/todo-chain.mjs && node --check src/todo-store.mjs && node --check src/todo-migration.mjs && node --check src/todo-revision.mjs && node --check src/todo-status.mjs && node --check src/todo-cli.mjs && node --check src/todo-dashboard-registry.mjs && node --check src/todo-gantt-presentation.mjs && node --check src/todo-gantt-live.mjs && node --check src/bounded-seam.mjs && node --check src/todo-narrative-anchor.mjs && node --check src/todo-markdown-renderer.mjs && node --check src/todo-gantt-svg.mjs && node --check src/todo-gantt-html.mjs && node --check src/runtime-projection.mjs && node --check src/runtime-decision-verifier.mjs && node --check src/runtime-front-end.mjs && node --check src/runtime-cli.mjs && node --check src/rc3-dogfood-scaffold.mjs && node --check src/runtime-engine.mjs && node --check src/runtime-scripted-executor.mjs && node --check src/runtime-diff-observer.mjs && node --check src/runtime-worktree-executor.mjs && node --check src/runtime-hold-recompile.mjs && node --check src/rc3-scripted-campaign.mjs && node --check src/rc3-actual-dogfood.mjs && node --check src/rc4-stage1-dogfood.mjs && node --check research/fixtures/dispatch-record/src/dispatch-record.mjs && node --check test/research-dispatch-record.test.mjs",
|
|
64
64
|
"check:project-identity": "node --check src/project-identity.mjs",
|
|
65
65
|
"ci": "npm run test && npm run test:sensor && npm run check && npm run check:project-identity"
|
|
66
66
|
}
|
package/src/cli-help.mjs
CHANGED
|
@@ -4,6 +4,7 @@ const ROOT_HELP = `Usage: lattice <command> [options]
|
|
|
4
4
|
|
|
5
5
|
Commands:
|
|
6
6
|
status --json Discover the current project and next action
|
|
7
|
+
session-context --json Session開始時の現在地を1プロセスで返す(工程状態+並列可否)
|
|
7
8
|
plan <command> Create, compile, or verify plans
|
|
8
9
|
run <command> Start or observe compiled runs
|
|
9
10
|
event verify Verify a runtime event log
|
|
@@ -52,6 +53,7 @@ Read commands:
|
|
|
52
53
|
status [--json]
|
|
53
54
|
bindings [--plan <key>] [--json] # compile_binding付きTaskをTODO identityつきで投影する
|
|
54
55
|
independence [--plan <key>] [--json] # readyを検証済み並列・要直列・未検査へ分けて投影する
|
|
56
|
+
seam-proposal [--plan <key>] [--json] # 記録済みseam提案をsensor無しで投影する
|
|
55
57
|
verify [--plan <key>] [--json]
|
|
56
58
|
snapshot --rebuild --plan <key>
|
|
57
59
|
gantt [--out <file>] [--scope live|all] # 既定live: 完走した工程を図から除く(一覧には残る)
|
|
@@ -68,6 +70,7 @@ Write commands:
|
|
|
68
70
|
evidence promote --plan <key> --task <id> --evidence <file>
|
|
69
71
|
independence compile --plan <key> --input <file> # witness setとsensorから並列可否を記録する
|
|
70
72
|
independence witness migrate --plan <key> # revision後の宣言をtask migrationで写す
|
|
73
|
+
seam-proposal compile --plan <key> # 並列可否記録と実sensorからseam提案を記録する
|
|
71
74
|
revise --plan <key> --input <file>
|
|
72
75
|
revise-phase --plan <key> --input <file>
|
|
73
76
|
revise-set --input <file>
|
|
@@ -129,6 +132,7 @@ const SUBCOMMAND_USAGE = Object.freeze({
|
|
|
129
132
|
'todo status': 'todo status [--json]',
|
|
130
133
|
'todo bindings': 'todo bindings [--plan <key>] [--json]',
|
|
131
134
|
'todo independence': 'todo independence [--plan <key>] [--json] | compile --plan <key> --input <file> | witness migrate --plan <key>',
|
|
135
|
+
'todo seam-proposal': 'todo seam-proposal [--plan <key>] [--json] | compile --plan <key>',
|
|
132
136
|
'todo verify': 'todo verify [--plan <key>] [--json]',
|
|
133
137
|
'todo snapshot': 'todo snapshot --rebuild --plan <key>',
|
|
134
138
|
'todo gantt': 'todo gantt [--out <file>] [--scope live|all] | status [--out <file>] | serve --port <port> [--scope live|all]',
|
package/src/project-cli.mjs
CHANGED
|
@@ -13,12 +13,16 @@ import {
|
|
|
13
13
|
todoSelfDigest,
|
|
14
14
|
} from './todo-contracts.mjs';
|
|
15
15
|
import { projectTodoStatus } from './todo-status.mjs';
|
|
16
|
+
import { projectIndependenceFrontier } from './todo-independence.mjs';
|
|
17
|
+
import { isTodoIndependenceLegacyMarker } from './todo-independence-contracts.mjs';
|
|
18
|
+
import { selectIndependenceGuidance } from './todo-independence-guidance.mjs';
|
|
16
19
|
import { ensureTodoDashboardActivity } from './todo-dashboard-registry.mjs';
|
|
17
20
|
import { resolveProjectIdentity } from './project-identity.mjs';
|
|
18
21
|
import {
|
|
19
22
|
buildTodoPlan,
|
|
20
23
|
createTodoStoreWriter,
|
|
21
24
|
initializeAuthoredTodoStore,
|
|
25
|
+
readTodoIndependenceArtifact,
|
|
22
26
|
readTodoStore,
|
|
23
27
|
TodoStoreError,
|
|
24
28
|
} from './todo-store.mjs';
|
|
@@ -27,6 +31,9 @@ const STORE_REF = '.lattice/todo';
|
|
|
27
31
|
const MANIFEST_REF = `${STORE_REF}/manifest.json`;
|
|
28
32
|
const MAX_INPUT_BYTES = 8_388_608;
|
|
29
33
|
const STATUS_SCHEMA = 'lattice.project_status.v1';
|
|
34
|
+
const SESSION_CONTEXT_SCHEMA = 'lattice.session_context.v1';
|
|
35
|
+
/** HEADが読めない環境でも投影を組めるようにする。記録があるときは実HEADで置き換わる。 */
|
|
36
|
+
const PLACEHOLDER_SHA = '0'.repeat(40);
|
|
30
37
|
const CREATE_INPUT_SCHEMA = 'lattice.plan_create_input.v1';
|
|
31
38
|
const PHASE_CREATE_INPUT_SCHEMA = 'lattice.plan_create_input.v2';
|
|
32
39
|
const DECOUPLED_PHASE_CREATE_INPUT_SCHEMA = 'lattice.plan_create_input.v3';
|
|
@@ -106,6 +113,74 @@ export function validateProjectStatus(value) {
|
|
|
106
113
|
} catch { return false; }
|
|
107
114
|
}
|
|
108
115
|
|
|
116
|
+
/**
|
|
117
|
+
* readyのあるplanについてだけ並列可否を要約する(ADR 0131 Decision 5)。
|
|
118
|
+
*
|
|
119
|
+
* store読みは呼び出し側が済ませている。ここが払うのはplanごとの小さな記録ファイルと、
|
|
120
|
+
* 記録があるときだけのHEAD照合である。readyが無いplanは述べる対象が無いので載せない。
|
|
121
|
+
*/
|
|
122
|
+
async function summarizeIndependence({ repoRoot, store, todo }) {
|
|
123
|
+
const readyPlanKeys = [...new Set(todo.next_ready.map(({ plan_key: key }) => key))].sort();
|
|
124
|
+
if (readyPlanKeys.length === 0) return [];
|
|
125
|
+
const activeByPlan = new Map();
|
|
126
|
+
for (const task of todo.active_set) {
|
|
127
|
+
if (!activeByPlan.has(task.plan_key)) activeByPlan.set(task.plan_key, []);
|
|
128
|
+
activeByPlan.get(task.plan_key).push(task.task_id);
|
|
129
|
+
}
|
|
130
|
+
let currentBaseSha = null;
|
|
131
|
+
const summaries = [];
|
|
132
|
+
for (const planKey of readyPlanKeys) {
|
|
133
|
+
const member = store.members.find(({ descriptor }) => descriptor.plan_key === planKey);
|
|
134
|
+
if (member === undefined) continue;
|
|
135
|
+
let artifact = null;
|
|
136
|
+
try {
|
|
137
|
+
artifact = await readTodoIndependenceArtifact({ repoRoot, store, planKey });
|
|
138
|
+
} catch (error) {
|
|
139
|
+
// 読めない記録を「記録なし」へ丸めない。理由を載せて先へ進む。
|
|
140
|
+
summaries.push({
|
|
141
|
+
plan_key: planKey, coverage: null,
|
|
142
|
+
guidance: { code: 'independence_unrecorded', message: null, next_action: 'none' },
|
|
143
|
+
unreadable_reason: error instanceof TodoStoreError
|
|
144
|
+
? `${error.code}:${error.detail?.reason ?? error.message}` : 'independence_unreadable',
|
|
145
|
+
parallel_groups: [], serialize_pair_count: 0,
|
|
146
|
+
conflict_with_active_count: 0, unknown_task_ids: [],
|
|
147
|
+
});
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (currentBaseSha === null && artifact !== null) currentBaseSha = gitHead(repoRoot);
|
|
151
|
+
const projected = projectIndependenceFrontier({
|
|
152
|
+
artifact,
|
|
153
|
+
readyTaskIds: todo.next_ready.filter((task) => task.plan_key === planKey)
|
|
154
|
+
.map(({ task_id: taskId }) => taskId),
|
|
155
|
+
activeTaskIds: activeByPlan.get(planKey) ?? [],
|
|
156
|
+
plan: member.plan,
|
|
157
|
+
currentBaseSha: currentBaseSha ?? PLACEHOLDER_SHA,
|
|
158
|
+
changedPaths: null,
|
|
159
|
+
});
|
|
160
|
+
summaries.push({
|
|
161
|
+
plan_key: planKey,
|
|
162
|
+
coverage: projected.coverage,
|
|
163
|
+
guidance: selectIndependenceGuidance({
|
|
164
|
+
coverage: projected.coverage,
|
|
165
|
+
contractSuperseded: isTodoIndependenceLegacyMarker(artifact),
|
|
166
|
+
readyCount: todo.next_ready.filter((task) => task.plan_key === planKey).length,
|
|
167
|
+
taskDeclared: projected.frontier.unknown
|
|
168
|
+
.every(({ unknowns }) => !unknowns.some(({ kind }) => kind === 'witness_missing')),
|
|
169
|
+
taskStale: projected.frontier.unknown
|
|
170
|
+
.some(({ unknowns }) => unknowns.some(({ kind }) => kind === 'record_stale')),
|
|
171
|
+
conflictWithActive: projected.frontier.conflicts_with_active[0]?.severability ?? null,
|
|
172
|
+
conflictBetweenReady: projected.frontier.serialize_pairs[0]?.severability ?? null,
|
|
173
|
+
}),
|
|
174
|
+
unreadable_reason: null,
|
|
175
|
+
parallel_groups: projected.frontier.parallel_groups.map(({ task_ids: ids }) => [...ids]),
|
|
176
|
+
serialize_pair_count: projected.frontier.serialize_pairs.length,
|
|
177
|
+
conflict_with_active_count: projected.frontier.conflicts_with_active.length,
|
|
178
|
+
unknown_task_ids: projected.frontier.unknown.map(({ task_id: taskId }) => taskId),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return summaries;
|
|
182
|
+
}
|
|
183
|
+
|
|
109
184
|
function statusResult(fields) {
|
|
110
185
|
const result = { schema: STATUS_SCHEMA, ...fields, result_digest: '' };
|
|
111
186
|
result.result_digest = resultDigest(result);
|
|
@@ -124,12 +199,18 @@ function invalidStatus({ cliVersion, repoRoot, reason }) {
|
|
|
124
199
|
});
|
|
125
200
|
}
|
|
126
201
|
|
|
127
|
-
|
|
128
|
-
|
|
202
|
+
/**
|
|
203
|
+
* discoveryとstore読みを1回で済ませ、status結果と(読めたなら)storeを返す。
|
|
204
|
+
*
|
|
205
|
+
* `runProjectStatus`と`runSessionContext`が同じ判定を二度書かないための共有点。
|
|
206
|
+
* store読みはここでしか行わない——session開始経路が同じstoreを二度払っていたのが
|
|
207
|
+
* ADR 0131で直した欠陥である。
|
|
208
|
+
*/
|
|
209
|
+
async function resolveProjectState({ cwd, cliVersion }) {
|
|
129
210
|
const repoRoot = resolveRepoRoot(cwd);
|
|
130
211
|
if (repoRoot === null) {
|
|
131
|
-
|
|
132
|
-
|
|
212
|
+
return { exitCode: 1, repoRoot: null, store: null, todo: null,
|
|
213
|
+
result: invalidStatus({ cliVersion, repoRoot: null, reason: 'git_repository_unresolved' }) };
|
|
133
214
|
}
|
|
134
215
|
const storeAbsolute = path.join(repoRoot, STORE_REF);
|
|
135
216
|
const manifestAbsolute = path.join(repoRoot, MANIFEST_REF);
|
|
@@ -137,30 +218,24 @@ export async function runProjectStatus({ cwd, stdout, cliVersion, env = process.
|
|
|
137
218
|
let latticeState;
|
|
138
219
|
let storeState;
|
|
139
220
|
let manifestState;
|
|
221
|
+
const invalid = (reason) => ({
|
|
222
|
+
exitCode: 1, repoRoot, store: null, todo: null,
|
|
223
|
+
result: invalidStatus({ cliVersion, repoRoot, reason }),
|
|
224
|
+
});
|
|
140
225
|
try { latticeState = await lstat(latticeAbsolute); } catch (error) {
|
|
141
|
-
if (error?.code !== 'ENOENT')
|
|
142
|
-
stdout.write(`${JSON.stringify(invalidStatus({ cliVersion, repoRoot, reason: 'lattice_root_unreadable' }))}\n`);
|
|
143
|
-
return 1;
|
|
144
|
-
}
|
|
226
|
+
if (error?.code !== 'ENOENT') return invalid('lattice_root_unreadable');
|
|
145
227
|
}
|
|
146
228
|
if (latticeState !== undefined && (latticeState.isSymbolicLink() || !latticeState.isDirectory())) {
|
|
147
|
-
|
|
148
|
-
return 1;
|
|
229
|
+
return invalid('lattice_root_invalid');
|
|
149
230
|
}
|
|
150
231
|
try { storeState = await lstat(storeAbsolute); } catch (error) {
|
|
151
|
-
if (error?.code !== 'ENOENT')
|
|
152
|
-
stdout.write(`${JSON.stringify(invalidStatus({ cliVersion, repoRoot, reason: 'store_unreadable' }))}\n`);
|
|
153
|
-
return 1;
|
|
154
|
-
}
|
|
232
|
+
if (error?.code !== 'ENOENT') return invalid('store_unreadable');
|
|
155
233
|
}
|
|
156
234
|
try { manifestState = await lstat(manifestAbsolute); } catch (error) {
|
|
157
|
-
if (error?.code !== 'ENOENT')
|
|
158
|
-
stdout.write(`${JSON.stringify(invalidStatus({ cliVersion, repoRoot, reason: 'manifest_unreadable' }))}\n`);
|
|
159
|
-
return 1;
|
|
160
|
-
}
|
|
235
|
+
if (error?.code !== 'ENOENT') return invalid('manifest_unreadable');
|
|
161
236
|
}
|
|
162
237
|
if (storeState === undefined && manifestState === undefined) {
|
|
163
|
-
|
|
238
|
+
return { exitCode: 0, repoRoot, store: null, todo: null, result: statusResult({
|
|
164
239
|
cli: { available: true, version: cliVersion },
|
|
165
240
|
project: { root: repoRoot, git_head: gitHead(repoRoot), project_id: null },
|
|
166
241
|
state: 'uninitialized',
|
|
@@ -171,14 +246,11 @@ export async function runProjectStatus({ cwd, stdout, cliVersion, env = process.
|
|
|
171
246
|
input_schema: CURRENT_CREATE_INPUT_SCHEMA,
|
|
172
247
|
schema_command: CURRENT_CREATE_SCHEMA_COMMAND,
|
|
173
248
|
},
|
|
174
|
-
});
|
|
175
|
-
stdout.write(`${JSON.stringify(result)}\n`);
|
|
176
|
-
return 0;
|
|
249
|
+
}) };
|
|
177
250
|
}
|
|
178
251
|
if (storeState?.isSymbolicLink() || !storeState?.isDirectory()
|
|
179
252
|
|| manifestState?.isSymbolicLink() || !manifestState?.isFile()) {
|
|
180
|
-
|
|
181
|
-
return 1;
|
|
253
|
+
return invalid('store_layout_invalid');
|
|
182
254
|
}
|
|
183
255
|
try {
|
|
184
256
|
const store = await readTodoStore({ repoRoot });
|
|
@@ -205,21 +277,60 @@ export async function runProjectStatus({ cwd, stdout, cliVersion, env = process.
|
|
|
205
277
|
can_create_plan: false,
|
|
206
278
|
next_action: next,
|
|
207
279
|
});
|
|
208
|
-
|
|
209
|
-
const identity = await resolveProjectIdentity({ repoRoot, projectId: store.project_id, env });
|
|
210
|
-
const actorSession = env.LATTICE_TODO_ACTOR_SESSION;
|
|
211
|
-
await ensureDashboardActivity({
|
|
212
|
-
repoRoot, projectId: store.project_id, displayName: identity.displayName,
|
|
213
|
-
sessionId: isTodoIdentifier(actorSession) ? actorSession : `status-${process.pid}`, env,
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
stdout.write(`${JSON.stringify(result)}\n`);
|
|
217
|
-
return 0;
|
|
280
|
+
return { exitCode: 0, repoRoot, store, todo, result };
|
|
218
281
|
} catch (error) {
|
|
219
|
-
const reason = error instanceof TodoStoreError
|
|
220
|
-
|
|
221
|
-
return
|
|
282
|
+
const reason = error instanceof TodoStoreError
|
|
283
|
+
? `${error.code}:${error.detail?.reason ?? error.message}` : 'store_validation_failed';
|
|
284
|
+
return invalid(reason);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export async function runProjectStatus({ cwd, stdout, cliVersion, env = process.env,
|
|
289
|
+
ensureDashboardActivity = ensureTodoDashboardActivity }) {
|
|
290
|
+
const state = await resolveProjectState({ cwd, cliVersion });
|
|
291
|
+
// dashboard活動の登録はdiscovery面の副作用として維持する(ADR 0131 Decision 4で
|
|
292
|
+
// session-context側だけが持たない、と決めた面である)。
|
|
293
|
+
if (state.store !== null && env.LATTICE_DASHBOARD_AUTOSTART !== '0') {
|
|
294
|
+
const identity = await resolveProjectIdentity({
|
|
295
|
+
repoRoot: state.repoRoot, projectId: state.store.project_id, env,
|
|
296
|
+
});
|
|
297
|
+
const actorSession = env.LATTICE_TODO_ACTOR_SESSION;
|
|
298
|
+
await ensureDashboardActivity({
|
|
299
|
+
repoRoot: state.repoRoot, projectId: state.store.project_id,
|
|
300
|
+
displayName: identity.displayName,
|
|
301
|
+
sessionId: isTodoIdentifier(actorSession) ? actorSession : `status-${process.pid}`, env,
|
|
302
|
+
});
|
|
222
303
|
}
|
|
304
|
+
stdout.write(`${JSON.stringify(state.result)}\n`);
|
|
305
|
+
return state.exitCode;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* session開始時の現在地を1プロセス・1 store読みで返す(ADR 0131)。
|
|
310
|
+
*
|
|
311
|
+
* `lattice status`と`lattice todo status`は同じ`readTodoStore`を別プロセスで二重に払う。
|
|
312
|
+
* hostのSessionStartはその両方を呼ぶため、storeが育ったprojectでは実行枠を超えて
|
|
313
|
+
* 案内ごと捨てられていた。ここは合成であって置き換えではない——既存2面は不変で、
|
|
314
|
+
* それぞれの消費者を持ち続ける。
|
|
315
|
+
*
|
|
316
|
+
* dashboard活動の登録は行わない。現在地を知るために呼ぶ面であり、常駐面を起こす面ではない。
|
|
317
|
+
*/
|
|
318
|
+
export async function runSessionContext({ cwd, stdout, cliVersion }) {
|
|
319
|
+
const state = await resolveProjectState({ cwd, cliVersion });
|
|
320
|
+
const independence = state.store === null || state.todo === null
|
|
321
|
+
? [] : await summarizeIndependence({ repoRoot: state.repoRoot, store: state.store, todo: state.todo });
|
|
322
|
+
const result = {
|
|
323
|
+
schema: SESSION_CONTEXT_SCHEMA,
|
|
324
|
+
// project discoveryの答えをそのまま埋める。hostは既存の検証器を再利用できる。
|
|
325
|
+
status: state.result,
|
|
326
|
+
// todoは`todo_status_result.v4`そのもの。新しい意味論を発明しない。
|
|
327
|
+
todo: state.todo,
|
|
328
|
+
independence,
|
|
329
|
+
result_digest: '',
|
|
330
|
+
};
|
|
331
|
+
result.result_digest = todoSelfDigest(result, 'result_digest');
|
|
332
|
+
stdout.write(`${JSON.stringify(result)}\n`);
|
|
333
|
+
return state.exitCode;
|
|
223
334
|
}
|
|
224
335
|
|
|
225
336
|
async function readCanonicalInput(repoRoot, inputRef) {
|