@quolu/lattice 0.64.2 → 0.64.3
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/todo-store-worktree-eol.mjs +46 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quolu/lattice",
|
|
3
|
-
"version": "0.64.
|
|
3
|
+
"version": "0.64.3",
|
|
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",
|
|
@@ -106,13 +106,30 @@ async function prepareProtection(repoRoot) {
|
|
|
106
106
|
return { absolute, create: false };
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
function stagedArtifactRefs(repoRoot, refs) {
|
|
110
|
+
const staged = new Set();
|
|
111
|
+
for (let offset = 0; offset < refs.length; offset += INDEX_REFRESH_BATCH_SIZE) {
|
|
112
|
+
const batch = refs.slice(offset, offset + INDEX_REFRESH_BATCH_SIZE);
|
|
113
|
+
const result = gitSpawnSync(['diff-index', '--cached', '--name-only', '-z', 'HEAD', '--', ...batch], {
|
|
114
|
+
cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
|
|
115
|
+
});
|
|
116
|
+
if (result.error || result.signal || result.status !== 0) {
|
|
117
|
+
fail('git_index_artifacts_unreadable', { refs: batch, status: result.status ?? null });
|
|
118
|
+
}
|
|
119
|
+
for (const ref of (result.stdout ?? '').split('\0')) {
|
|
120
|
+
if (ref !== '') staged.add(ref.replaceAll('\\', '/'));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return staged;
|
|
124
|
+
}
|
|
125
|
+
|
|
109
126
|
function refreshGitIndex(repoRoot, refs) {
|
|
110
127
|
for (let offset = 0; offset < refs.length; offset += INDEX_REFRESH_BATCH_SIZE) {
|
|
111
128
|
const batch = refs.slice(offset, offset + INDEX_REFRESH_BATCH_SIZE);
|
|
112
|
-
const refresh = gitSpawnSync(['
|
|
129
|
+
const refresh = gitSpawnSync(['update-index', '--refresh', '--', ...batch], {
|
|
113
130
|
cwd: repoRoot, stdio: 'ignore',
|
|
114
131
|
});
|
|
115
|
-
//
|
|
132
|
+
// 対象は呼出前にindex blob=HEADを実証済み。update-index --refreshで内容をstageせずstatだけを揃える。
|
|
116
133
|
if (refresh.error || refresh.signal || ![0, 1].includes(refresh.status)) {
|
|
117
134
|
fail('git_index_refresh_failed', { refs: batch, status: refresh.status ?? null });
|
|
118
135
|
}
|
|
@@ -132,52 +149,64 @@ function refreshGitIndex(repoRoot, refs) {
|
|
|
132
149
|
export async function repairTodoStoreWorktreeEol({ repoRoot }) {
|
|
133
150
|
const protection = await prepareProtection(repoRoot);
|
|
134
151
|
const refs = await collectArtifactRefs(repoRoot);
|
|
135
|
-
const
|
|
152
|
+
const stagedRefs = stagedArtifactRefs(repoRoot, refs);
|
|
153
|
+
const artifacts = [];
|
|
136
154
|
for (const ref of refs) {
|
|
137
155
|
const absolute = path.join(repoRoot, ref);
|
|
138
156
|
const stats = await safeRegularFileState(repoRoot, ref);
|
|
139
157
|
const bytes = await readFile(absolute);
|
|
140
158
|
const normalized = normalizePureCrlf(bytes, ref);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
159
|
+
artifacts.push({
|
|
160
|
+
ref, absolute, sourceBytes: bytes, bytes: normalized ?? bytes, mode: stats.mode,
|
|
161
|
+
converted: normalized !== null,
|
|
144
162
|
});
|
|
145
163
|
}
|
|
146
164
|
|
|
147
165
|
let headArtifacts = [];
|
|
148
166
|
let indexArtifacts = [];
|
|
149
|
-
if (
|
|
150
|
-
const maxBodyBytes = Math.max(...
|
|
167
|
+
if (artifacts.length > 0) {
|
|
168
|
+
const maxBodyBytes = Math.max(...artifacts.map(({ bytes }) => bytes.length));
|
|
151
169
|
try {
|
|
152
|
-
headArtifacts = gitCatFileBatch(
|
|
170
|
+
headArtifacts = gitCatFileBatch(artifacts.map(({ ref }) => `HEAD:${ref}`), {
|
|
153
171
|
cwd: repoRoot, maxBodyBytes,
|
|
154
172
|
});
|
|
155
173
|
} catch (error) {
|
|
156
174
|
fail('git_head_artifacts_unreadable', { message: error.message });
|
|
157
175
|
}
|
|
158
176
|
try {
|
|
159
|
-
indexArtifacts = gitCatFileBatch(
|
|
177
|
+
indexArtifacts = gitCatFileBatch(artifacts.map(({ ref }) => `:${ref}`), {
|
|
160
178
|
cwd: repoRoot, maxBodyBytes,
|
|
161
179
|
});
|
|
162
180
|
} catch (error) {
|
|
163
181
|
fail('git_index_artifacts_unreadable', { message: error.message });
|
|
164
182
|
}
|
|
165
183
|
}
|
|
166
|
-
|
|
184
|
+
const converted = [];
|
|
185
|
+
const refreshRefs = [];
|
|
186
|
+
for (const [index, artifact] of artifacts.entries()) {
|
|
167
187
|
const head = headArtifacts[index];
|
|
168
|
-
if (head?.type !== 'blob' || !head.bytes.equals(artifact.bytes)) {
|
|
169
|
-
fail('artifact_not_pure_checkout_conversion', { ref: artifact.ref });
|
|
170
|
-
}
|
|
171
188
|
const staged = indexArtifacts[index];
|
|
172
|
-
if (
|
|
173
|
-
|
|
189
|
+
if (artifact.converted) {
|
|
190
|
+
if (head?.type !== 'blob' || !head.bytes.equals(artifact.bytes)) {
|
|
191
|
+
fail('artifact_not_pure_checkout_conversion', { ref: artifact.ref });
|
|
192
|
+
}
|
|
193
|
+
if (stagedRefs.has(artifact.ref)
|
|
194
|
+
|| staged?.type !== 'blob' || !staged.bytes.equals(head.bytes)) {
|
|
195
|
+
fail('artifact_staged_change_present', { ref: artifact.ref });
|
|
196
|
+
}
|
|
197
|
+
converted.push(artifact);
|
|
198
|
+
}
|
|
199
|
+
if (!stagedRefs.has(artifact.ref) && head?.type === 'blob' && staged?.type === 'blob'
|
|
200
|
+
&& staged.bytes.equals(head.bytes) && artifact.bytes.equals(head.bytes)) {
|
|
201
|
+
refreshRefs.push(artifact.ref);
|
|
174
202
|
}
|
|
175
203
|
}
|
|
176
204
|
|
|
177
205
|
for (const artifact of converted) {
|
|
178
206
|
await atomicReplace({ repoRoot, ...artifact });
|
|
179
207
|
}
|
|
180
|
-
|
|
208
|
+
// 0.64.2がLF書換後のstat refreshで止まった部分状態も、再実行だけで回復させる。
|
|
209
|
+
refreshGitIndex(repoRoot, refreshRefs);
|
|
181
210
|
if (protection.create) {
|
|
182
211
|
await assertSafeDirectoryChain(repoRoot, path.posix.dirname(ATTRIBUTES_REF));
|
|
183
212
|
await writeFile(protection.absolute, EOL_PROTECTION, { flag: 'wx' });
|