Retire legacy OCR/media/evidence fallbacks, add local-folder event bus and Page Aggregate guards, and archive completed design checklists. Validation: cargo test -p mnote-web -- --test-threads=1; cargo test --workspace -- --test-threads=1; git diff --check; codegraph sync .; codegraph_status.
95 lines
4.2 KiB
JavaScript
95 lines
4.2 KiB
JavaScript
#!/usr/bin/env node
|
|
"use strict";
|
|
|
|
const assert = require("node:assert");
|
|
const fs = require("node:fs");
|
|
const path = require("node:path");
|
|
const { execFileSync } = require("node:child_process");
|
|
|
|
const ROOT = process.env.MNOTE_REPO_ROOT || "/mnt/Data1T/mnote";
|
|
const TASK535 = path.join(ROOT, "scripts", "task535-page-ai-local-agent-clean-edit-smoke.js");
|
|
const OUTPUT_DIR = path.join(ROOT, "tmp", "task539-local-agent-audit-scope-contract");
|
|
const RESULT_PATH = path.join(OUTPUT_DIR, "result.json");
|
|
|
|
function run(command, args, options = {}) {
|
|
return execFileSync(command, args, {
|
|
cwd: ROOT,
|
|
encoding: "utf8",
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
...options,
|
|
});
|
|
}
|
|
|
|
function parseLastJsonObject(output) {
|
|
const trimmed = String(output || "").trim();
|
|
const start = trimmed.lastIndexOf("\n{");
|
|
const jsonText = start >= 0 ? trimmed.slice(start + 1) : trimmed;
|
|
return JSON.parse(jsonText);
|
|
}
|
|
|
|
function parseRunBody(task535Result) {
|
|
const runRecord = (task535Result.captured || []).find((item) => item.kind === "run");
|
|
assert(runRecord, "task535 result 缺少 run payload");
|
|
return JSON.parse(runRecord.body || "{}");
|
|
}
|
|
|
|
function main() {
|
|
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
const task535Output = run("node", [TASK535]);
|
|
const task535Result = parseLastJsonObject(task535Output);
|
|
assert.equal(task535Result.ok, true, "task535 clean edit smoke 必须通过");
|
|
const runBody = parseRunBody(task535Result);
|
|
const allowedFiles = runBody.targetPackage?.allowedFiles || [];
|
|
assert.deepEqual(allowedFiles, [task535Result.relativePath], "targetPackage.allowedFiles 应只包含当前目标文件");
|
|
assert.equal(runBody.targetPackage?.currentFile?.relativePath, task535Result.relativePath, "currentFile 应冻结为当前目标文件");
|
|
assert.equal(runBody.runTargetSnapshot?.schema, "mnote.page_ai_run_target_snapshot.v1", "run payload 应携带 frozen target snapshot");
|
|
assert(runBody.runTargetSnapshot?.frozenAt, "frozen target snapshot 应有 frozenAt");
|
|
if (runBody.agentTargetPackage) {
|
|
assert.deepEqual(runBody.agentTargetPackage.allowedFiles || [], [task535Result.relativePath], "agentTargetPackage.allowedFiles 应只包含当前目标文件");
|
|
}
|
|
assert.equal(task535Result.usedDocumentsSave, false, "clean edit 不应调用 /api/documents/save");
|
|
assert.equal(task535Result.usedMarkdownEdit, false, "clean edit 不应调用 mnote.doc.markdown_edit");
|
|
assert.equal(task535Result.usedPageSave, false, "clean edit 不应调用 mnote.page.save");
|
|
|
|
const cargoOutput = run("cargo", ["test", "-p", "mnote-web", "local_agent_audit", "--", "--test-threads=1"], {
|
|
cwd: path.join(ROOT, "rust"),
|
|
});
|
|
assert(cargoOutput.includes("local_agent_audit_allowed_files_override_folder_context"), "cargo test 应覆盖 allowedFiles 优先于 folder context");
|
|
assert(cargoOutput.includes("local_agent_audit_reads_allowed_files_from_agent_run_envelope"), "cargo test 应覆盖 agentRunEnvelope.allowedFiles");
|
|
assert(cargoOutput.includes("local_agent_audit_event_carries_agent_run_receipt"), "cargo test 应覆盖 receipt auditScope");
|
|
|
|
const result = {
|
|
ok: true,
|
|
task: "task539-local-agent-audit-scope-contract",
|
|
task535: {
|
|
resultPath: path.join(ROOT, "tmp", "task535-page-ai-local-agent-clean-edit-smoke", "result.json"),
|
|
relativePath: task535Result.relativePath,
|
|
targetAllowedFiles: allowedFiles,
|
|
frozenAt: runBody.runTargetSnapshot.frozenAt,
|
|
usedDocumentsSave: task535Result.usedDocumentsSave,
|
|
usedMarkdownEdit: task535Result.usedMarkdownEdit,
|
|
usedPageSave: task535Result.usedPageSave,
|
|
},
|
|
backendAudit: {
|
|
command: "cargo test -p mnote-web local_agent_audit -- --test-threads=1",
|
|
expectedScope: "allowed_files",
|
|
expectedFileCount: 1,
|
|
},
|
|
};
|
|
fs.writeFileSync(RESULT_PATH, `${JSON.stringify(result, null, 2)}\n`, "utf8");
|
|
console.log(JSON.stringify(result, null, 2));
|
|
}
|
|
|
|
try {
|
|
main();
|
|
} catch (error) {
|
|
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
fs.writeFileSync(
|
|
path.join(OUTPUT_DIR, "failure.json"),
|
|
`${JSON.stringify({ ok: false, error: error.stack || error.message || String(error) }, null, 2)}\n`,
|
|
"utf8",
|
|
);
|
|
console.error(error.stack || error.message || String(error));
|
|
process.exit(1);
|
|
}
|