@quolu/lattice 0.46.2 → 0.48.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.
@@ -113,47 +113,28 @@ export function buildWitnessSet({ draft, observationByPath } = {}) {
113
113
  const entries = draftOwnEntries(task, draft.schema) ?? [];
114
114
  const owns = [...new Map(entries.map((entry) => [entry.target, entry])).values()]
115
115
  .sort((left, right) => compareText(left.target, right.target));
116
- if (owns.length === 0) { reasons.push(`owns_empty:${taskId}`); continue; }
117
- // affected_testsは宣言とfresh観測をbinding単位でexact比較する。複数pathを所有すると
118
- // 観測集合が一致しない限り必ず落ちるので、今の契約では表現できない(2026-07-27の実測)。
119
- if (owns.length > 1) { reasons.push(`multiple_owned_paths_unsupported:${taskId}`); continue; }
120
- const [own] = owns;
121
- const target = own.target;
122
- const observed = observationByPath?.[target];
123
- // 観測できていないことを空配列へ丸めない。丸めるとdriftでcompileが落ちる。
124
- if (observed === undefined) { reasons.push(`affected_tests_unobserved:${target}`); continue; }
125
- if (own.creates) {
126
- // 宣言が実態と合っているかを確かめるのが道具の役目である。front endが要求する形
127
- // (fresh absent・blast radiusが空・changedFilesが対象1件)をここで満たしておかないと、
128
- // 通る宣言を作ったつもりでcompileで落ちる(ADR 0136)。
129
- if (observed.state !== 'absent') { reasons.push(`creates_path_present:${target}`); continue; }
130
- if (observed.affectedTests.length !== 0
131
- || observed.changedFiles.length !== 1
132
- || observed.changedFiles[0] !== target) {
133
- reasons.push(`creates_unverified:${target}`); continue;
134
- }
135
- } else if (observed.state === 'absent') {
136
- // 不存在のpathを黙って通さない。作るつもりならそう宣言する、が次の一手である。
137
- reasons.push(`path_absent_declare_creates:${target}`); continue;
138
- }
139
- const affected = own.creates ? [] : observed.affectedTests;
116
+ const ownedTargets = new Set(owns.map(({ target }) => target));
117
+ const affected = sortedUnique(owns.flatMap(({ target }) => (
118
+ observationByPath?.[target]?.affectedTests ?? []
119
+ )));
140
120
  for (const anchor of task.concern_anchors ?? []) {
141
121
  // `within`は自分が所有している資源に限る。所有していない資源の内側に担当を主張させない。
142
- if (anchor.within !== target) reasons.push(`anchor_outside_owned:${taskId}:${anchor.within}`);
122
+ if (!ownedTargets.has(anchor.within)) reasons.push(`anchor_outside_owned:${taskId}:${anchor.within}`);
143
123
  }
144
124
  manualWitness[taskId] = {
145
- owns: [own.creates ? { kind: 'path', target, creates: true } : { kind: 'path', target }],
125
+ owns: owns.map(({ target, creates }) => (
126
+ creates ? { kind: 'path', target, creates: true } : { kind: 'path', target }
127
+ )),
146
128
  reads: sortedUnique(task.reads ?? []),
147
- writes: [target],
129
+ writes: owns.map(({ target }) => target),
148
130
  resources: [],
149
131
  state_effects: [],
150
132
  sensor_provenance: {
151
- queries: [{
152
- query_id: queryIdByPath.get(target),
153
- expect: { kind: 'affected', path: target },
154
- }],
133
+ queries: owns.map(({ target }) => ({
134
+ query_id: queryIdByPath.get(target), expect: { kind: 'affected', path: target },
135
+ })),
155
136
  },
156
- affected_tests: sortedUnique(affected),
137
+ affected_tests: affected,
157
138
  // 明示unknownは下書きが持つ。観測で埋まる欄ではなく、書き手が「ここは確定していない」と
158
139
  // 述べる欄なので、道具が発明も削除もしない。
159
140
  unknowns: [...(task.unknowns ?? [])]