merge: reconcile remote main deployment updates
# Conflicts: # .project-docs/00-brief/project-positioning.md # .project-docs/00-brief/success-criteria.md # .project-docs/05-agent-entry/memory-index.md # .project-docs/10-decisions/decision-index.md # .project-docs/20-architecture/data-flow.md # .project-docs/20-architecture/module-map.md # .project-docs/20-architecture/system-overview.md # .project-docs/30-worklog/current-state.md # .project-docs/30-worklog/task-history.md # .project-docs/40-domain/business-rules.md # .project-docs/40-domain/glossary.md # .project-docs/50-evidence/evidence-index.md # .project-docs/50-evidence/topic-template.md # .project-docs/60-reflection/reflection-index.md # .project-docs/80-commitments/commitments.md # .project-docs/90-maintenance/stale-items.md # archive/project-history/2026-08-28/legacy-planning-with-files-progress-final.md
This commit is contained in:
commit
c4c469f444
40 files changed
+1412
-47
No files matched your search
@@ -45,9 +45,23 @@ const ROOT_ALLOWLIST = new Set([
|
||||
|
||||
const SKIP_WALK = new Set(['.git', '.build', 'node_modules']);
|
||||
const RESIDUE_PATTERN = /(^\.DS_Store$|\.log$|\.tmp$|\.swp$|\.pid$|~$)/i;
|
||||
const TEXT_ARTIFACT_EXTENSIONS = new Set(['.css', '.html', '.js', '.json', '.md', '.mjs', '.py', '.ts', '.txt', '.yaml', '.yml']);
|
||||
|
||||
function hashBytes(value) {
|
||||
return createHash('sha256').update(value).digest('hex');
|
||||
}
|
||||
|
||||
async function sha256(filePath) {
|
||||
return createHash('sha256').update(await readFile(filePath)).digest('hex');
|
||||
return hashBytes(await readFile(filePath));
|
||||
}
|
||||
|
||||
function artifactContentHash(entry, value) {
|
||||
if (!TEXT_ARTIFACT_EXTENSIONS.has(path.extname(entry).toLowerCase())) return hashBytes(value);
|
||||
return hashBytes(Buffer.from(value.toString('utf8').replace(/\r\n/gu, '\n'), 'utf8'));
|
||||
}
|
||||
|
||||
async function normalizedTextSha256(filePath) {
|
||||
return artifactContentHash(filePath, await readFile(filePath));
|
||||
}
|
||||
|
||||
async function walkFiles(directory, options = {}) {
|
||||
@@ -88,10 +102,8 @@ async function assertArchiveMatchesSource({ artifactPath, sourcePath, ignore = n
|
||||
assert.deepEqual(artifactEntries, sourceEntries, `${path.basename(artifactPath)} 文件集合与源码不一致`);
|
||||
|
||||
for (const entry of sourceEntries) {
|
||||
const sourceHash = createHash('sha256')
|
||||
.update(await readFile(path.join(sourcePath, ...entry.split('/'))))
|
||||
.digest('hex');
|
||||
const archiveHash = createHash('sha256').update(archiveEntry(artifactPath, entry)).digest('hex');
|
||||
const sourceHash = artifactContentHash(entry, await readFile(path.join(sourcePath, ...entry.split('/'))));
|
||||
const archiveHash = artifactContentHash(entry, archiveEntry(artifactPath, entry));
|
||||
assert.equal(archiveHash, sourceHash, `${path.basename(artifactPath)}:${entry} 与源码内容不一致`);
|
||||
}
|
||||
}
|
||||
@@ -165,8 +177,8 @@ test('dist is an exact versioned release set with valid hashes', async () => {
|
||||
assert.equal(manifest.artifacts.filter(({ kind }) => kind === 'business_instruction_docx').length, 1);
|
||||
|
||||
const docx = manifest.artifacts.find(({ kind }) => kind === 'business_instruction_docx');
|
||||
assert.equal(await sha256(path.resolve(ROOT, docx.source)), docx.source_sha256);
|
||||
assert.equal(await sha256(path.resolve(ROOT, docx.builder)), docx.builder_sha256);
|
||||
assert.equal(await normalizedTextSha256(path.resolve(ROOT, docx.source)), docx.source_sha256);
|
||||
assert.equal(await normalizedTextSha256(path.resolve(ROOT, docx.builder)), docx.builder_sha256);
|
||||
});
|
||||
|
||||
test('extension and release baselines agree', async () => {
|
||||
|
||||
Reference in new issue
Block a user