import test from "node:test"; import assert from "node:assert/strict"; import { buildTaskContext, contextSizeReport, CONTEXT_LIMITS, readIndexedTask, selectInputPaths } from "../../../tools/web/task-context-lib.mjs"; test("current task context is bounded and follows the parent pointer", () => { const bundle = buildTaskContext(); const report = contextSizeReport(bundle); assert.match(bundle.context.task, /^M\d+-GAP-\d{5}$/); assert.match(bundle.context.parentTask, /^M\d+-GAP-\d{5}$/); assert.equal(bundle.context.parent.manifest.nextTask, bundle.context.task); assert.ok(bundle.context.commands.length >= 1); assert.equal(bundle.context.sourceDocuments.taskCard, `docs/tasks/${bundle.context.task}.md`); assert.equal(bundle.context.sourceDocuments.taskIndex, "tests/golden/M15-03A/task-index.json"); assert.ok(!JSON.stringify(bundle.context).includes("next-task-plan.json")); assert.equal(bundle.context.scope.ownerFamily, readIndexedTask(bundle.context.task).ownerFamily); assert.equal(report.withinBudget, true); assert.equal(report.totalTokens, report.sourceTokens + report.evidenceTokens); assert.equal(report.evidenceTokens, bundle.context.inputSelection.totals.tokens); assert.ok(report.totalTokens < CONTEXT_LIMITS.contextTokens); }); test("the indexed task record remains available beside the compact card", () => { const bundle = buildTaskContext("M16-GAP-00011"); assert.equal(bundle.context.sourceDocuments.taskCard, "docs/tasks/M16-GAP-00011.md"); assert.equal(bundle.context.parent.manifest.status, "done"); assert.equal(bundle.context.nextTask, "M16-GAP-00012"); }); test("input selection excludes evidence before it can enter the read list", () => { const fixture = "tests/files/web/generated/M16-GAP-00022-datablock-Sound.blend"; const generator = "tools/web/generated/M16-GAP-00022.py"; const result = selectInputPaths([fixture, generator], { sourceBytes: 100, sourceTokens: 25, limits: { ...CONTEXT_LIMITS, evidenceBytes: 1024 }, }); assert.deepEqual(result.inputPaths, [generator]); assert.equal(result.inputSelection.excluded[0].reason, "EVIDENCE_BYTE_BUDGET"); assert.equal(result.inputSelection.totals.bytes, result.inputSelection.selected[0].bytes); }); test("input selection audits remaining context and file-count exclusions", () => { const paths = [ "tools/web/generated/M16-GAP-00022.py", "tools/web/generated/M16-GAP-00021.py", ]; const remaining = selectInputPaths(paths, { sourceBytes: 0, sourceTokens: CONTEXT_LIMITS.contextTokens - 1, limits: { ...CONTEXT_LIMITS, evidenceBytes: 8192 }, }); assert.deepEqual(remaining.inputPaths, []); assert.equal(remaining.inputSelection.excluded[0].reason, "CONTEXT_REMAINING_SPACE"); const count = selectInputPaths(paths, { limits: { ...CONTEXT_LIMITS, evidenceFiles: 1, evidenceBytes: 8192 }, }); assert.equal(count.inputPaths.length, 1); assert.equal(count.inputSelection.excluded[0].reason, "EVIDENCE_FILE_COUNT"); }); test("generated task outputs are excluded before their size can affect selection", () => { const result = selectInputPaths(["tests/golden/M16-GAP-00022/"], { generatedPaths: ["tests/golden/M16-GAP-00022/"], }); assert.deepEqual(result.inputPaths, []); assert.deepEqual(result.inputSelection.excluded, [{ path: "tests/golden/M16-GAP-00022/", bytes: null, reason: "GENERATED_EVIDENCE_OUTPUT", }]); });