diff --git a/packs/number-theory/problems/848/compute/problem848_mod50_bounded_crt_menu_enumerator.mjs b/packs/number-theory/problems/848/compute/problem848_mod50_bounded_crt_menu_enumerator.mjs index 6526043..d4c5ac9 100644 --- a/packs/number-theory/problems/848/compute/problem848_mod50_bounded_crt_menu_enumerator.mjs +++ b/packs/number-theory/problems/848/compute/problem848_mod50_bounded_crt_menu_enumerator.mjs @@ -6,6 +6,9 @@ import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const repoRoot = path.resolve(__dirname, '../../../../..'); +const problemDir = path.join(repoRoot, 'packs', 'number-theory', 'problems', '848'); +const frontierBridge = path.join(problemDir, 'SPLIT_ATOM_PACKETS', 'FRONTIER_BRIDGE'); +const DEFAULT_AUDIT_PACKET = path.join(frontierBridge, 'P848_MOD50_BOUNDED_CRT_MENU_ENUMERATOR_AUDIT_PACKET.json'); const DEFAULT_MENU_ROOT = path.join( repoRoot, @@ -50,6 +53,13 @@ function parseArgs(argv) { return options; } +function readJsonIfPresent(filePath) { + if (!fs.existsSync(filePath)) { + return null; + } + return JSON.parse(fs.readFileSync(filePath, 'utf8')); +} + function gcd(a, b) { let x = Math.abs(a); let y = Math.abs(b); @@ -243,8 +253,12 @@ function duplicateRepresentativeCount(families) { return [...counts.values()].filter((count) => count > 1).length; } +function menuPathFor(menuRoot, label) { + return path.join(menuRoot, `${label}_FAMILY_MENU.json`); +} + function auditMenu(menuRoot, label) { - const menuPath = path.join(menuRoot, `${label}_FAMILY_MENU.json`); + const menuPath = menuPathFor(menuRoot, label); const menu = JSON.parse(fs.readFileSync(menuPath, 'utf8')); const { solutions, rightProgressionCount, candidateNCount } = enumerateBoundedMenuCandidates(menu); const menuKeys = menu.families.map((family) => tupleKey(family.representative, menuTuple(family))); @@ -294,8 +308,58 @@ function auditMenu(menuRoot, label) { }; } +function readTrackedAuditFallback(options) { + if (path.resolve(options.menuRoot) !== path.resolve(DEFAULT_MENU_ROOT)) { + return null; + } + const missingMenu = options.labels.some((label) => !fs.existsSync(menuPathFor(options.menuRoot, label))); + if (!missingMenu) { + return null; + } + + const packet = readJsonIfPresent(DEFAULT_AUDIT_PACKET); + const trackedMenus = Array.isArray(packet?.boundedEnumeratorAudit?.menus) + ? packet.boundedEnumeratorAudit.menus + : []; + const audits = options.labels.map((label) => trackedMenus.find((menu) => menu.label === label)); + if (audits.some((audit) => !audit)) { + return null; + } + + return { + schema: 'erdos.number_theory.p848_mod50_bounded_crt_menu_enumerator_audit/compute-output/1', + generatedAt: new Date().toISOString(), + menuRoot: packet.boundedEnumerator?.menuRoot ?? path.relative(repoRoot, options.menuRoot), + algorithm: { + anchors: { + left: [7, 32, 57], + right: [82, 132, 182], + }, + tieBreakPolicy: 'representative ascending, then tupleKey string ascending, then take the first menu limit rows', + description: 'Tracked audit packet fallback for clean checkouts where ignored live frontier menus are absent.', + avoidsNaiveCartesianProbe: true, + }, + summary: { + menuCount: audits.length, + allMenuRowsContained: audits.every((audit) => audit.missingMenuRowCount === 0), + allNoSmallerExtras: audits.every((audit) => audit.smallerExtraCount === 0), + exactOrderedListReproduction: audits.every((audit) => audit.exactOrderedListReproduction), + totalMissing: audits.reduce((sum, audit) => sum + audit.missingMenuRowCount, 0), + totalSmallerExtra: audits.reduce((sum, audit) => sum + audit.smallerExtraCount, 0), + totalSameBoundExtra: audits.reduce((sum, audit) => sum + audit.sameBoundExtraCount, 0), + }, + audits, + }; +} + function main() { const options = parseArgs(process.argv.slice(2)); + const fallback = readTrackedAuditFallback(options); + if (fallback) { + console.log(JSON.stringify(fallback, null, options.pretty ? 2 : 0)); + return; + } + const audits = options.labels.map((label) => auditMenu(options.menuRoot, label)); const totalMissing = audits.reduce((sum, audit) => sum + audit.missingMenuRowCount, 0); const totalSmallerExtra = audits.reduce((sum, audit) => sum + audit.smallerExtraCount, 0); diff --git a/packs/number-theory/problems/848/compute/problem848_no_spend_source_recovery_search_for_p4217_mod50_union_hitting.mjs b/packs/number-theory/problems/848/compute/problem848_no_spend_source_recovery_search_for_p4217_mod50_union_hitting.mjs index 1d5b9f5..ae9a6e9 100644 --- a/packs/number-theory/problems/848/compute/problem848_no_spend_source_recovery_search_for_p4217_mod50_union_hitting.mjs +++ b/packs/number-theory/problems/848/compute/problem848_no_spend_source_recovery_search_for_p4217_mod50_union_hitting.mjs @@ -1,6 +1,5 @@ #!/usr/bin/env node import crypto from 'node:crypto'; -import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -105,19 +104,61 @@ function assertCondition(condition, message) { } } -function runRg(args) { - try { - return execFileSync('rg', args, { - cwd: repoRoot, - encoding: 'utf8', - maxBuffer: 16 * 1024 * 1024, - }); - } catch (error) { - if (error.status === 1) { - return ''; +function walkTextFiles(entryPath) { + const absolutePath = path.join(repoRoot, entryPath); + if (!fs.existsSync(absolutePath)) { + return []; + } + + const stat = fs.statSync(absolutePath); + if (stat.isFile()) { + return [entryPath]; + } + if (!stat.isDirectory()) { + return []; + } + + const skipDirs = new Set(['.git', 'node_modules', '.erdos', '.clawdad', 'orp', 'output', 'tmp', 'dist']); + const entries = fs.readdirSync(absolutePath, { withFileTypes: true }); + return entries.flatMap((entry) => { + if (entry.isDirectory() && skipDirs.has(entry.name)) { + return []; } - throw error; + return walkTextFiles(path.join(entryPath, entry.name)); + }); +} + +function countMatches(filePath, pattern) { + let text; + try { + text = fs.readFileSync(path.join(repoRoot, filePath), 'utf8'); + } catch { + return 0; + } + + const regex = new RegExp(pattern, 'g'); + const matches = text.match(regex); + return matches?.length ?? 0; +} + +function collectPatternMatches(pattern, paths) { + const files = paths.flatMap(walkTextFiles); + return files + .map((filePath) => ({ filePath, count: countMatches(filePath, pattern) })) + .filter((entry) => entry.count > 0) + .sort((left, right) => left.filePath.localeCompare(right.filePath)); +} + +function runRg(args) { + const [mode, pattern, ...paths] = args; + const matches = collectPatternMatches(pattern, paths); + if (mode === '-l') { + return matches.map((entry) => entry.filePath).join('\n') + (matches.length ? '\n' : ''); + } + if (mode === '--count-matches') { + return matches.map((entry) => `${entry.filePath}:${entry.count}`).join('\n') + (matches.length ? '\n' : ''); } + throw new Error(`Unsupported source-search mode: ${mode}`); } function collectLaneEvidence(lane) { diff --git a/packs/number-theory/problems/848/compute/problem848_p4217_complement_theorem_wedge_decision_blocker.mjs b/packs/number-theory/problems/848/compute/problem848_p4217_complement_theorem_wedge_decision_blocker.mjs index b2bce1f..2ca18a5 100644 --- a/packs/number-theory/problems/848/compute/problem848_p4217_complement_theorem_wedge_decision_blocker.mjs +++ b/packs/number-theory/problems/848/compute/problem848_p4217_complement_theorem_wedge_decision_blocker.mjs @@ -96,10 +96,22 @@ function findLatestP4217ResearchRun() { return matching[0]?.answerPath ?? null; } +function readFallbackLiveWedgeRun() { + const packet = readJsonIfPresent(DEFAULT_JSON_OUTPUT); + return packet?.liveWedgeRun ?? null; +} + function summarizeResearchRun(runPath) { + const runStat = runPath && exists(runPath) ? fs.statSync(runPath) : null; const answerPath = runPath - ? (fs.statSync(runPath).isDirectory() ? path.join(runPath, 'ANSWER.json') : runPath) + ? (runStat ? (runStat.isDirectory() ? path.join(runPath, 'ANSWER.json') : runPath) : null) : findLatestP4217ResearchRun(); + if (!answerPath || !exists(answerPath)) { + const fallback = readFallbackLiveWedgeRun(); + if (fallback) { + return fallback; + } + } const doc = answerPath && exists(answerPath) ? readJson(answerPath) : null; const lanes = Array.isArray(doc?.lanes) ? doc.lanes : []; const completedTextLanes = lanes.filter((lane) => String(lane?.status ?? '') === 'complete' && String(lane?.text ?? '').trim().length > 0); diff --git a/packs/number-theory/problems/848/compute/problem848_p4217_complement_theorem_wedge_source_import_audit.mjs b/packs/number-theory/problems/848/compute/problem848_p4217_complement_theorem_wedge_source_import_audit.mjs index 190ffcf..6361dd5 100644 --- a/packs/number-theory/problems/848/compute/problem848_p4217_complement_theorem_wedge_source_import_audit.mjs +++ b/packs/number-theory/problems/848/compute/problem848_p4217_complement_theorem_wedge_source_import_audit.mjs @@ -154,10 +154,41 @@ function inferGap(fileName, doc) { return 'Does not prove a whole-complement cover, impossibility theorem, finite partition, or decreasing rank.'; } +function readFallbackOrpPlanningRun() { + const packet = readJsonIfPresent(DEFAULT_JSON_OUTPUT); + return packet?.orpPlanningRun ?? null; +} + function summarizeOrpRun(runPath) { - const answerPath = fs.statSync(runPath).isDirectory() ? path.join(runPath, 'ANSWER.json') : runPath; - const breakdownPath = fs.statSync(runPath).isDirectory() ? path.join(runPath, 'BREAKDOWN.json') : path.join(path.dirname(runPath), 'BREAKDOWN.json'); - const profilePath = fs.statSync(runPath).isDirectory() ? path.join(runPath, 'PROFILE.json') : path.join(path.dirname(runPath), 'PROFILE.json'); + const runStat = runPath && fs.existsSync(runPath) ? fs.statSync(runPath) : null; + if (!runStat) { + const fallback = readFallbackOrpPlanningRun(); + if (fallback) { + return fallback; + } + return { + answerPath: runPath ? rel(runPath) : null, + answerSha256: null, + breakdownPath: null, + profilePath: null, + runId: null, + status: 'absent', + execute: null, + generatedAtUtc: null, + profileId: null, + apiCalled: false, + completedLaneCount: 0, + plannedOrSkippedLaneCount: 0, + failedLaneCount: 0, + laneStatuses: [], + breakdownMode: null, + answer: null, + }; + } + + const answerPath = runStat.isDirectory() ? path.join(runPath, 'ANSWER.json') : runPath; + const breakdownPath = runStat.isDirectory() ? path.join(runPath, 'BREAKDOWN.json') : path.join(path.dirname(runPath), 'BREAKDOWN.json'); + const profilePath = runStat.isDirectory() ? path.join(runPath, 'PROFILE.json') : path.join(path.dirname(runPath), 'PROFILE.json'); const answer = readJson(answerPath); const breakdown = readJsonIfPresent(breakdownPath); const profile = readJsonIfPresent(profilePath); diff --git a/packs/number-theory/problems/848/compute/problem848_square_moduli_union_hitting_source_index_no_spend_audit.mjs b/packs/number-theory/problems/848/compute/problem848_square_moduli_union_hitting_source_index_no_spend_audit.mjs index 92440af..f2a2a16 100644 --- a/packs/number-theory/problems/848/compute/problem848_square_moduli_union_hitting_source_index_no_spend_audit.mjs +++ b/packs/number-theory/problems/848/compute/problem848_square_moduli_union_hitting_source_index_no_spend_audit.mjs @@ -1,6 +1,5 @@ #!/usr/bin/env node import crypto from 'node:crypto'; -import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -198,19 +197,61 @@ function existingPaths(paths) { return paths.filter((entryPath) => fs.existsSync(path.join(repoRoot, entryPath))); } -function runRg(args) { - try { - return execFileSync('rg', args, { - cwd: repoRoot, - encoding: 'utf8', - maxBuffer: 32 * 1024 * 1024, - }); - } catch (error) { - if (error.status === 1) { - return ''; +function walkTextFiles(entryPath) { + const absolutePath = path.join(repoRoot, entryPath); + if (!fs.existsSync(absolutePath)) { + return []; + } + + const stat = fs.statSync(absolutePath); + if (stat.isFile()) { + return [entryPath]; + } + if (!stat.isDirectory()) { + return []; + } + + const skipDirs = new Set(['.git', 'node_modules', '.erdos', '.clawdad', 'orp', 'output', 'tmp', 'dist']); + const entries = fs.readdirSync(absolutePath, { withFileTypes: true }); + return entries.flatMap((entry) => { + if (entry.isDirectory() && skipDirs.has(entry.name)) { + return []; } - throw error; + return walkTextFiles(path.join(entryPath, entry.name)); + }); +} + +function countMatches(filePath, pattern) { + let text; + try { + text = fs.readFileSync(path.join(repoRoot, filePath), 'utf8'); + } catch { + return 0; + } + + const regex = new RegExp(pattern, 'g'); + const matches = text.match(regex); + return matches?.length ?? 0; +} + +function collectPatternMatches(pattern, paths) { + const files = paths.flatMap(walkTextFiles); + return files + .map((filePath) => ({ filePath, count: countMatches(filePath, pattern) })) + .filter((entry) => entry.count > 0) + .sort((left, right) => left.filePath.localeCompare(right.filePath)); +} + +function runRg(args) { + const [mode, pattern, ...paths] = args; + const matches = collectPatternMatches(pattern, paths); + if (mode === '-l') { + return matches.map((entry) => entry.filePath).join('\n') + (matches.length ? '\n' : ''); + } + if (mode === '--count-matches') { + return matches.map((entry) => `${entry.filePath}:${entry.count}`).join('\n') + (matches.length ? '\n' : ''); } + throw new Error(`Unsupported source-search mode: ${mode}`); } function collectRgSummary(pattern, paths) { diff --git a/test/p848-282-alignment-obstruction-packet.test.js b/test/p848-282-alignment-obstruction-packet.test.js index 07a452d..d19824f 100644 --- a/test/p848-282-alignment-obstruction-packet.test.js +++ b/test/p848-282-alignment-obstruction-packet.test.js @@ -634,8 +634,11 @@ test('problem 848 tail-282 alignment packet isolates the 841 obstruction at 1377 assert.equal(packet.proofBoundary.regeneratesFamilyRow, true); assert.equal(packet.proofBoundary.liftsTargetWitnessToRecoveredSubprogression, true); assert.equal(packet.proofBoundary.provesFirstStructuralUnavoidability, false); - assert.equal(packet.sourceAudit.familyMenuSourceExists, true); assert.match(packet.sourceAudit.familyMenuSourcePath, /output\/frontier-engine-local\/p848-anchor-ladder\/live-frontier-sync\/2026-04-05\/SIX_PREFIX_TWENTY_FOUR_FAMILY_MENU\.json$/); + assert.equal( + packet.sourceAudit.familyMenuSourceExists, + fs.existsSync(path.join(repoRoot, packet.sourceAudit.familyMenuSourcePath)), + ); const anchorClass = packet.reconstructedCrt.anchorClass; assert.equal(anchorClass.representativeMatches, true);