@quolu/lattice 0.58.0 → 0.58.2
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
|
+
"version": "0.58.2",
|
|
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
|
@@ -77,8 +77,7 @@ import {
|
|
|
77
77
|
explainTodoStructureRealization,
|
|
78
78
|
explainTodoStructureSet,
|
|
79
79
|
} from './todo-structure-contracts.mjs';
|
|
80
|
-
import {
|
|
81
|
-
import { collectTodoStructureGitProvenance } from './todo-structure-git-adapter.mjs';
|
|
80
|
+
import { collectTodoStructureAuthoritativeObservation } from './todo-structure-authoritative-observation.mjs';
|
|
82
81
|
import { compileTodoStructureOverlay } from './todo-structure-overlay.mjs';
|
|
83
82
|
import {
|
|
84
83
|
buildTodoStructureCompileArtifact,
|
|
@@ -2024,8 +2023,9 @@ async function structureCompile({ repoRoot, env, planKey, inputRef }) {
|
|
|
2024
2023
|
});
|
|
2025
2024
|
}
|
|
2026
2025
|
const actor = mutationActor(env);
|
|
2027
|
-
const
|
|
2028
|
-
const
|
|
2026
|
+
const observation = await collectTodoStructureAuthoritativeObservation({ repoRoot, structureSet });
|
|
2027
|
+
const sourceEvidence = observation.source_evidence;
|
|
2028
|
+
const gitProvenance = observation.git_provenance;
|
|
2029
2029
|
const realizations = [];
|
|
2030
2030
|
for (const task of structureSet.tasks.filter(({ applicability }) => applicability === 'graph')) {
|
|
2031
2031
|
realizations.push(...await readTodoStructureRealizationChain({
|
|
@@ -2035,10 +2035,7 @@ async function structureCompile({ repoRoot, env, planKey, inputRef }) {
|
|
|
2035
2035
|
const overlay = compileTodoStructureOverlay({
|
|
2036
2036
|
structureSet,
|
|
2037
2037
|
topology: mergedTopology(store),
|
|
2038
|
-
taskStates:
|
|
2039
|
-
task_id: taskId,
|
|
2040
|
-
status: member.tasks.find(({ task_id: id }) => id === taskId)?.status ?? 'pending',
|
|
2041
|
-
})),
|
|
2038
|
+
taskStates: member.tasks.map(({ task_id: taskId, status }) => ({ task_id: taskId, status })),
|
|
2042
2039
|
sourceProjection: sourceEvidence.projection,
|
|
2043
2040
|
gitProvenance,
|
|
2044
2041
|
realizations,
|
|
@@ -2188,14 +2185,15 @@ async function structureFinalize({ repoRoot, env, planKey }) {
|
|
|
2188
2185
|
effectiveTransforms.set(task.task_id, latest.realized);
|
|
2189
2186
|
}
|
|
2190
2187
|
const actor = mutationActor(env);
|
|
2191
|
-
const
|
|
2192
|
-
|
|
2188
|
+
const observation = await collectTodoStructureAuthoritativeObservation({
|
|
2189
|
+
repoRoot, structureSet: source, effectiveTransforms,
|
|
2193
2190
|
});
|
|
2194
|
-
const
|
|
2191
|
+
const sourceEvidence = observation.source_evidence;
|
|
2192
|
+
const gitProvenance = observation.git_provenance;
|
|
2195
2193
|
const overlay = compileTodoStructureOverlay({
|
|
2196
2194
|
structureSet: source,
|
|
2197
2195
|
topology: mergedTopology(store),
|
|
2198
|
-
taskStates:
|
|
2196
|
+
taskStates: member.tasks.map(({ task_id: taskId, status }) => ({ task_id: taskId, status })),
|
|
2199
2197
|
sourceProjection: sourceEvidence.projection,
|
|
2200
2198
|
gitProvenance,
|
|
2201
2199
|
realizations,
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { gitSpawnSync } from './git-process.mjs';
|
|
6
|
+
import { runSensorCli } from './sensor-cli.mjs';
|
|
7
|
+
import { collectTodoStructureGitProvenance } from './todo-structure-git-adapter.mjs';
|
|
8
|
+
import { collectTodoStructureSourceEvidence } from './todo-structure-source-adapter.mjs';
|
|
9
|
+
|
|
10
|
+
const SHA = /^[0-9a-f]{40}$/u;
|
|
11
|
+
|
|
12
|
+
export class TodoStructureObservationError extends Error {
|
|
13
|
+
constructor(code, reason, detail = {}) {
|
|
14
|
+
super(reason);
|
|
15
|
+
this.name = 'TodoStructureObservationError';
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.detail = { reason, ...detail };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function fail(code, reason, detail = {}) {
|
|
22
|
+
throw new TodoStructureObservationError(code, reason, detail);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function git({ cwd, args, operation }) {
|
|
26
|
+
const result = gitSpawnSync(args, {
|
|
27
|
+
cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
|
|
28
|
+
});
|
|
29
|
+
if (result.error !== undefined || result.status !== 0 || result.signal !== null) {
|
|
30
|
+
fail('STRUCTURE_OBSERVATION_GIT_FAILED', 'observation_git_command_failed', {
|
|
31
|
+
operation, status: result.status ?? null, signal: result.signal ?? null,
|
|
32
|
+
cause: result.error?.message ?? null,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return result.stdout.trim();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function memoryStream() {
|
|
39
|
+
let value = '';
|
|
40
|
+
return {
|
|
41
|
+
stream: { write: (chunk) => { value += String(chunk); } },
|
|
42
|
+
read: () => value,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function initializeObservationSensor(observationRoot) {
|
|
47
|
+
const stdout = memoryStream();
|
|
48
|
+
const stderr = memoryStream();
|
|
49
|
+
const status = await runSensorCli({
|
|
50
|
+
argv: ['init', observationRoot, '--json'],
|
|
51
|
+
stdout: stdout.stream,
|
|
52
|
+
stderr: stderr.stream,
|
|
53
|
+
});
|
|
54
|
+
if (status !== 0) {
|
|
55
|
+
let diagnostic = null;
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(stderr.read().trim());
|
|
58
|
+
diagnostic = {
|
|
59
|
+
code: typeof parsed?.code === 'string' ? parsed.code : null,
|
|
60
|
+
detail: parsed?.detail ?? null,
|
|
61
|
+
};
|
|
62
|
+
} catch {
|
|
63
|
+
diagnostic = { code: null, detail: null };
|
|
64
|
+
}
|
|
65
|
+
fail('STRUCTURE_OBSERVATION_SENSOR_INIT_FAILED', 'observation_sensor_init_failed', {
|
|
66
|
+
sensor_error: diagnostic,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function errorSummary(error) {
|
|
72
|
+
return {
|
|
73
|
+
code: typeof error?.code === 'string' ? error.code : null,
|
|
74
|
+
message: error instanceof Error ? error.message : String(error),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 管理repoのdirtyなindex/worktreeを一切観測せず、current HEADだけを一時detached worktreeへ
|
|
80
|
+
* 展開してGit来歴とsource graphを収集する。store/planned source/artifactの読書きは呼出側が
|
|
81
|
+
* 管理repoで行うため、ここへは持ち込まない。
|
|
82
|
+
*/
|
|
83
|
+
export async function collectTodoStructureAuthoritativeObservation({
|
|
84
|
+
repoRoot, structureSet, effectiveTransforms = null,
|
|
85
|
+
initializeSensor = initializeObservationSensor,
|
|
86
|
+
} = {}) {
|
|
87
|
+
if (typeof repoRoot !== 'string' || repoRoot.length === 0
|
|
88
|
+
|| structureSet === null || typeof structureSet !== 'object'
|
|
89
|
+
|| typeof initializeSensor !== 'function') {
|
|
90
|
+
fail('STRUCTURE_OBSERVATION_INPUT_INVALID', 'observation_input_invalid');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const headSha = git({
|
|
94
|
+
cwd: repoRoot, args: ['rev-parse', '--verify', 'HEAD^{commit}'], operation: 'resolve_head',
|
|
95
|
+
});
|
|
96
|
+
if (!SHA.test(headSha)) fail('STRUCTURE_OBSERVATION_HEAD_INVALID', 'observation_head_invalid');
|
|
97
|
+
|
|
98
|
+
const temporaryRoot = await mkdtemp(path.join(tmpdir(), 'lattice-structure-observation-'));
|
|
99
|
+
const observationRoot = path.join(temporaryRoot, 'worktree');
|
|
100
|
+
let registered = false;
|
|
101
|
+
let operationError = null;
|
|
102
|
+
let result;
|
|
103
|
+
try {
|
|
104
|
+
git({
|
|
105
|
+
cwd: repoRoot,
|
|
106
|
+
args: ['worktree', 'add', '--detach', '--quiet', observationRoot, headSha],
|
|
107
|
+
operation: 'worktree_add',
|
|
108
|
+
});
|
|
109
|
+
registered = true;
|
|
110
|
+
const observedHead = git({
|
|
111
|
+
cwd: observationRoot, args: ['rev-parse', '--verify', 'HEAD^{commit}'],
|
|
112
|
+
operation: 'verify_observation_head',
|
|
113
|
+
});
|
|
114
|
+
if (observedHead !== headSha) {
|
|
115
|
+
fail('STRUCTURE_OBSERVATION_HEAD_MISMATCH', 'observation_head_mismatch', {
|
|
116
|
+
expected_head_sha: headSha, actual_head_sha: observedHead,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// sensor DBを作る前にclean treeをGit provenanceへ束縛する。sensorの一時fileは証拠対象外で、
|
|
121
|
+
// source projection収集後にworktreeごと破棄する。
|
|
122
|
+
const gitProvenance = collectTodoStructureGitProvenance({
|
|
123
|
+
repoRoot: observationRoot, structureSet,
|
|
124
|
+
});
|
|
125
|
+
await initializeSensor(observationRoot);
|
|
126
|
+
const sourceEvidence = await collectTodoStructureSourceEvidence({
|
|
127
|
+
cwd: observationRoot, structureSet, effectiveTransforms,
|
|
128
|
+
observationStage: effectiveTransforms === null ? 'planned' : 'final',
|
|
129
|
+
});
|
|
130
|
+
result = {
|
|
131
|
+
head_sha: headSha,
|
|
132
|
+
git_provenance: gitProvenance,
|
|
133
|
+
source_evidence: sourceEvidence,
|
|
134
|
+
};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
operationError = error;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let cleanupError = null;
|
|
140
|
+
try {
|
|
141
|
+
if (registered) {
|
|
142
|
+
git({
|
|
143
|
+
cwd: repoRoot,
|
|
144
|
+
args: ['worktree', 'remove', '--force', observationRoot],
|
|
145
|
+
operation: 'worktree_remove',
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
await rm(temporaryRoot, { recursive: true, force: true });
|
|
149
|
+
} catch (error) {
|
|
150
|
+
cleanupError = error;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (cleanupError !== null) {
|
|
154
|
+
fail('STRUCTURE_OBSERVATION_CLEANUP_FAILED', 'observation_cleanup_failed', {
|
|
155
|
+
operation_error: operationError === null ? null : errorSummary(operationError),
|
|
156
|
+
cleanup_error: errorSummary(cleanupError),
|
|
157
|
+
observation_root: observationRoot,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
if (operationError !== null) throw operationError;
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
@@ -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
|
-
||
|
|
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 (
|
|
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,
|
|
@@ -466,12 +469,6 @@ function realizationFindings(structureSet, states, realizations, gitProvenance,
|
|
|
466
469
|
observed: null, expected: 'fresh task realization',
|
|
467
470
|
nextAction: 'record_the_task_realization_before_done',
|
|
468
471
|
}));
|
|
469
|
-
} else if (state === 'in-progress') {
|
|
470
|
-
findings.push(finding({
|
|
471
|
-
code: 'STRUCTURE_COMMIT_UNBOUND', severity: 'unknown', taskIds: [task.task_id],
|
|
472
|
-
observed: { commits: [] }, expected: 'explicit realization commit OIDs when implementation exists',
|
|
473
|
-
nextAction: 'record_a_realization_when_the_task_has_committed_implementation',
|
|
474
|
-
}));
|
|
475
472
|
}
|
|
476
473
|
continue;
|
|
477
474
|
}
|
|
@@ -563,7 +560,7 @@ export function compileTodoStructureOverlay({
|
|
|
563
560
|
const rawFindings = [
|
|
564
561
|
...anchorFindings(structureSet, sourceProjection),
|
|
565
562
|
...missingEffectiveAnchors,
|
|
566
|
-
...connectionFindings(structureSet, graph.effective, topologyDag),
|
|
563
|
+
...connectionFindings(structureSet, graph.effective, topologyDag, states),
|
|
567
564
|
...realizationFindings(
|
|
568
565
|
structureSet, states, normalizedRealizations, gitProvenance, graph.effective,
|
|
569
566
|
),
|
|
@@ -234,13 +234,26 @@ function edgeObservation(outcome, expectedNode, direction, expectedPath) {
|
|
|
234
234
|
};
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
function effectVerdict(anchor, existence) {
|
|
238
|
-
if (anchor.expected_at
|
|
237
|
+
function effectVerdict(anchor, existence, observationStage) {
|
|
238
|
+
if (anchor.expected_at === 'baseline') {
|
|
239
239
|
return { verdict: 'unknown', reason: 'STRUCTURE_CODE_ANCHOR_TIME_DEFERRED' };
|
|
240
240
|
}
|
|
241
|
+
if (anchor.expected_at === 'after_task' && observationStage === 'planned') {
|
|
242
|
+
return { verdict: 'consistent', reason: null, deferred: true };
|
|
243
|
+
}
|
|
241
244
|
if (existence === 'unknown') {
|
|
242
245
|
return { verdict: 'unknown', reason: 'STRUCTURE_CODE_ANCHOR_UNRESOLVED' };
|
|
243
246
|
}
|
|
247
|
+
if (anchor.expected_at === 'after_task' && observationStage === 'final') {
|
|
248
|
+
const expected = anchor.effect === 'delete' ? 'absent' : 'present';
|
|
249
|
+
return existence === expected
|
|
250
|
+
? { verdict: 'consistent', reason: null }
|
|
251
|
+
: { verdict: 'inconsistent', reason: expected === 'present'
|
|
252
|
+
? 'STRUCTURE_CODE_ANCHOR_ABSENT' : 'STRUCTURE_DELETE_STILL_EXISTS' };
|
|
253
|
+
}
|
|
254
|
+
if (anchor.expected_at !== 'current') {
|
|
255
|
+
return { verdict: 'unknown', reason: 'STRUCTURE_CODE_ANCHOR_TIME_DEFERRED' };
|
|
256
|
+
}
|
|
244
257
|
if (anchor.effect === 'create') {
|
|
245
258
|
return existence === 'absent'
|
|
246
259
|
? { verdict: 'consistent', reason: null }
|
|
@@ -274,7 +287,21 @@ function unknownAnchor(anchor, evidence, reason) {
|
|
|
274
287
|
};
|
|
275
288
|
}
|
|
276
289
|
|
|
277
|
-
function
|
|
290
|
+
function deferredAnchor(anchor, evidence) {
|
|
291
|
+
return {
|
|
292
|
+
...unknownAnchor(anchor, evidence, null),
|
|
293
|
+
verdict: 'consistent',
|
|
294
|
+
reason: null,
|
|
295
|
+
coverage: 'deferred',
|
|
296
|
+
edges: {
|
|
297
|
+
state: 'not_applicable',
|
|
298
|
+
incoming: [], outgoing: [], incoming_omitted: 0, outgoing_omitted: 0,
|
|
299
|
+
incoming_source_limit_reached: false, outgoing_source_limit_reached: false,
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function projectAnchor({ anchor, querySet, outcomes, statusReady, observationStage }) {
|
|
278
305
|
const pathQuery = queryFor('affected', anchor.path);
|
|
279
306
|
const pathOutcome = outcomes.get(pathQuery.id);
|
|
280
307
|
const evidence = {
|
|
@@ -284,11 +311,14 @@ function projectAnchor({ anchor, querySet, outcomes, statusReady }) {
|
|
|
284
311
|
callers: null,
|
|
285
312
|
callees: null,
|
|
286
313
|
};
|
|
314
|
+
if (anchor.expected_at === 'after_task' && observationStage === 'planned') {
|
|
315
|
+
return deferredAnchor(anchor, evidence);
|
|
316
|
+
}
|
|
287
317
|
if (!statusReady) return unknownAnchor(anchor, evidence, 'STRUCTURE_SENSOR_NOT_READY');
|
|
288
318
|
|
|
289
319
|
const pathResult = pathObservation(pathQuery, pathOutcome, anchor.path);
|
|
290
320
|
if (anchor.symbol === null) {
|
|
291
|
-
const decision = effectVerdict(anchor, pathResult.existence);
|
|
321
|
+
const decision = effectVerdict(anchor, pathResult.existence, observationStage);
|
|
292
322
|
return {
|
|
293
323
|
...unknownAnchor(anchor, evidence, decision.reason),
|
|
294
324
|
verdict: decision.verdict,
|
|
@@ -321,7 +351,7 @@ function projectAnchor({ anchor, querySet, outcomes, statusReady }) {
|
|
|
321
351
|
candidates: symbolResult.candidates,
|
|
322
352
|
};
|
|
323
353
|
|
|
324
|
-
const decision = effectVerdict(anchor, symbolResult.existence);
|
|
354
|
+
const decision = effectVerdict(anchor, symbolResult.existence, observationStage);
|
|
325
355
|
if (symbolResult.existence !== 'present') {
|
|
326
356
|
return {
|
|
327
357
|
...unknownAnchor(anchor, evidence, decision.reason),
|
|
@@ -366,15 +396,18 @@ function projectAnchor({ anchor, querySet, outcomes, statusReady }) {
|
|
|
366
396
|
|
|
367
397
|
/** 収集済みsensor outcomeから、anchor到達範囲だけのbounded projectionを作る。 */
|
|
368
398
|
export function projectTodoStructureSourceEvidence({
|
|
369
|
-
structureSet, collected, effectiveTransforms = null,
|
|
399
|
+
structureSet, collected, effectiveTransforms = null, observationStage = 'current',
|
|
370
400
|
} = {}) {
|
|
371
401
|
assertStructureSet(structureSet);
|
|
402
|
+
if (!['current', 'planned', 'final'].includes(observationStage)) {
|
|
403
|
+
throw new TypeError('observationStageが不正');
|
|
404
|
+
}
|
|
372
405
|
const querySet = buildTodoStructureSensorQuerySet(structureSet, { effectiveTransforms });
|
|
373
406
|
const outcomes = indexOutcomes(querySet, collected);
|
|
374
407
|
const statusQuery = querySet.queries[0];
|
|
375
408
|
const statusOutcome = outcomes.get(statusQuery.id);
|
|
376
409
|
const anchors = graphAnchors(structureSet, effectiveTransforms).map((anchor) => projectAnchor({
|
|
377
|
-
anchor, querySet, outcomes, statusReady: statusOutcome.outcome === 'ready',
|
|
410
|
+
anchor, querySet, outcomes, statusReady: statusOutcome.outcome === 'ready', observationStage,
|
|
378
411
|
}));
|
|
379
412
|
const projection = {
|
|
380
413
|
schema: TODO_STRUCTURE_SOURCE_PROJECTION_SCHEMA,
|
|
@@ -400,7 +433,7 @@ export function projectTodoStructureSourceEvidence({
|
|
|
400
433
|
|
|
401
434
|
/** 実sensor収集とpure projectionを同じ既存adapter経由で行う。 */
|
|
402
435
|
export async function collectTodoStructureSourceEvidence({
|
|
403
|
-
cwd, structureSet, effectiveTransforms = null,
|
|
436
|
+
cwd, structureSet, effectiveTransforms = null, observationStage = 'current',
|
|
404
437
|
execute = undefined, inspectAffectedPath = undefined,
|
|
405
438
|
} = {}) {
|
|
406
439
|
const querySet = buildTodoStructureSensorQuerySet(structureSet, { effectiveTransforms });
|
|
@@ -412,6 +445,8 @@ export async function collectTodoStructureSourceEvidence({
|
|
|
412
445
|
});
|
|
413
446
|
return {
|
|
414
447
|
query_set: querySet,
|
|
415
|
-
projection: projectTodoStructureSourceEvidence({
|
|
448
|
+
projection: projectTodoStructureSourceEvidence({
|
|
449
|
+
structureSet, collected, effectiveTransforms, observationStage,
|
|
450
|
+
}),
|
|
416
451
|
};
|
|
417
452
|
}
|