@quolu/lattice 0.22.0 → 0.23.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/package.json
CHANGED
|
@@ -92,6 +92,27 @@ export async function observeMacosBinaryIdentity(binaryPath) {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/** storeが解決したimmutable bindingからprocess/worktree/checkpointをDirect OSで再観測する。 */
|
|
95
|
+
/**
|
|
96
|
+
* 本repositoryの現在状態を1つのdigestへ畳む。
|
|
97
|
+
*
|
|
98
|
+
* HEAD・作業ツリー状態(untracked/ignoredを含む)・全refを見る。workerがworktreeの外へ
|
|
99
|
+
* 書いてもworktreeのdiffには映らないので、ここだけが本repositoryへの書き込みを捕まえる。
|
|
100
|
+
*/
|
|
101
|
+
export async function canonicalRepositoryFingerprint(repoRoot) {
|
|
102
|
+
const parts = [];
|
|
103
|
+
for (const args of [
|
|
104
|
+
['rev-parse', 'HEAD'],
|
|
105
|
+
['status', '--porcelain=v1', '--untracked-files=all', '--ignored=matching'],
|
|
106
|
+
['for-each-ref', '--format=%(refname) %(objectname)'],
|
|
107
|
+
]) {
|
|
108
|
+
const { stdout } = await execFileAsync('git', args, {
|
|
109
|
+
cwd: repoRoot, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024,
|
|
110
|
+
});
|
|
111
|
+
parts.push(stdout);
|
|
112
|
+
}
|
|
113
|
+
return createHash('sha256').update(parts.join('\u0000'), 'utf8').digest('hex');
|
|
114
|
+
}
|
|
115
|
+
|
|
95
116
|
export function createDirectOsProcessObserver({ resolveObservationBinding }) {
|
|
96
117
|
if (typeof resolveObservationBinding !== 'function') fail('SUPERVISOR_CONFIGURATION_INVALID', 'observation binding resolverなし');
|
|
97
118
|
return async ({ kind, binding, ack }) => {
|
|
@@ -104,6 +125,15 @@ export function createDirectOsProcessObserver({ resolveObservationBinding }) {
|
|
|
104
125
|
|| typeof resolved.base_sha !== 'string' || !/^[0-9a-f]{40}$/.test(resolved.base_sha)
|
|
105
126
|
|| !validateProcessStartIdentity(resolved.process_start_identity)
|
|
106
127
|
|| resolved.process_start_identity.pid !== resolved.process_pid) fail('HOLD_ACKS_INCOMPLETE', 'observation binding不正');
|
|
128
|
+
// 本repositoryを見る材料は対で渡す。片方だけでは照合できず、片方だけを受けると
|
|
129
|
+
// 「検査した」と読める記録が検査なしで作れてしまう。
|
|
130
|
+
const canonicalRoot = resolved.canonical_root ?? null;
|
|
131
|
+
const canonicalBaseline = resolved.canonical_fingerprint_digest ?? null;
|
|
132
|
+
if ((canonicalRoot === null) !== (canonicalBaseline === null)
|
|
133
|
+
|| (canonicalRoot !== null && !path.isAbsolute(canonicalRoot))
|
|
134
|
+
|| (canonicalBaseline !== null && !/^[0-9a-f]{64}$/u.test(canonicalBaseline))) {
|
|
135
|
+
fail('HOLD_ACKS_INCOMPLETE', 'canonical repository観測bindingが不正');
|
|
136
|
+
}
|
|
107
137
|
let processState = 'exited';
|
|
108
138
|
let observedIdentity = resolved.process_start_identity;
|
|
109
139
|
let processGroupId = resolved.process_group_id;
|
|
@@ -145,7 +175,17 @@ export function createDirectOsProcessObserver({ resolveObservationBinding }) {
|
|
|
145
175
|
const checkpoint = await captureWorktreeDiff({ worktreePath: worktreeRealpath, baseSha: resolved.base_sha });
|
|
146
176
|
const processObservation = { schema: 'lattice.direct_process_observation.v1', pid: resolved.process_pid, process_start_identity_digest: observedIdentity.identity_digest, process_group_id: processGroupId, state: processState };
|
|
147
177
|
const processObservationDigest = digestArtifact(processObservation);
|
|
148
|
-
|
|
178
|
+
// worktreeの外——本repository——への書き込みは、worktreeのdiffにはまったく映らない。
|
|
179
|
+
// 見ていないことを「変更が無かった」と読ませないため、検査したかどうかを記録へ残す
|
|
180
|
+
// (ADR 0140)。渡されていなければ`null`=未検査であり、無変更の主張ではない。
|
|
181
|
+
let canonicalDigest = null;
|
|
182
|
+
if (canonicalRoot !== null) {
|
|
183
|
+
canonicalDigest = await canonicalRepositoryFingerprint(canonicalRoot);
|
|
184
|
+
if (canonicalDigest !== canonicalBaseline) {
|
|
185
|
+
fail('HOLD_ACKS_INCOMPLETE', 'canonical repositoryがworker実行中に変化した');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const worktreeFingerprint = { schema: 'lattice.direct_worktree_fingerprint.v2', worktree_id: binding?.worktree_id ?? ack.worktree_id, worktree_realpath: worktreeRealpath, checkpoint_digest: checkpoint.checkpoint_digest, canonical_fingerprint_digest: canonicalDigest };
|
|
149
189
|
const worktreeFingerprintDigest = digestArtifact(worktreeFingerprint);
|
|
150
190
|
return {
|
|
151
191
|
quiesced: true,
|