slop
Container image / image (push) Failing after 56s

This commit is contained in:
legop3
2026-09-28 23:38:19 -04:00
parent ba71106544
commit 18e7fb8d1b
2 changed files with 65 additions and 18 deletions
+19 -3
View File
@@ -9,8 +9,8 @@ network produces scores and numeric values; it never generates JSON text.
Implemented: remove wheel-speed telemetry and previous drive-command history Implemented: remove wheel-speed telemetry and previous drive-command history
from inputs (raw recordings unchanged); preserve exact numeric zeros; reuse from inputs (raw recordings unchanged); preserve exact numeric zeros; reuse
recordings with a fresh resume-v4.pt / schedule-v4.json lineage. Existing format 2 recordings with a fresh resume-v4.pt / schedule-v4.json lineage. Startup deletes recognized older-format snapshots/checkpoints and old training
snapshots remain stored and are visibly incompatible. One ongoing learner, gated lineages; recordings and current-format models are preserved. One ongoing learner, gated
Latest/Previous, and admin-created permanent snapshots remain. Latest/Previous, and admin-created permanent snapshots remain.
Main card shows decoded command parameters, score, applied threshold marker, and Main card shows decoded command parameters, score, applied threshold marker, and
@@ -123,7 +123,7 @@ the default development path. Intended layout:
rover-learning/ rover-learning/
recordings/ Rolling video/events/session metadata (implemented) recordings/ Rolling video/events/session metadata (implemented)
models/ Permanent UUID directories: weights.pt and model.json models/ Permanent UUID directories: weights.pt and model.json
training/ resume-v3.pt, schedule-v3.json, and current job.json training/ resume-v4.pt, schedule-v4.json, and current job.json
``` ```
Training/model directories are created only when service and training are enabled. Training/model directories are created only when service and training are enabled.
@@ -914,3 +914,19 @@ temporal latching or accumulated motion from random reservoir samples. It does
not claim to verify actual physical command execution. After normal deployment, not claim to verify actual physical command execution. After normal deployment,
let the new learner process retained recordings; copied reportVersion 3 diagnostics let the new learner process retained recordings; copied reportVersion 3 diagnostics
provide the next evidence. Do not demand prescribed driving or a manual checklist. provide the next evidence. Do not demand prescribed driving or a manual checklist.
### Obsolete model cleanup after format 3 transition
Latest user IPI explicitly authorizes deleting obsolete snapshots, checkpoints and
training state. This supersedes earlier format 2 preservation notes above.
Enabled-service startup, before trainer/driving/gateway startup, scans both models/
and training/checkpoints/. Identity-validated manifests with integer policy version
>=1 and < current POLICY_VERSION are removed. Current/future/unknown-format models
and recordings are preserved. Candidate/Latest/Previous references to obsolete
checkpoints are atomically removed before directory deletion; leased directories
are retired until release. Cleanup also removes legacy resume/schedule files and
temporary files for the unversioned and v1-v3 lineages. Current v4 state is kept.
Cleanup runs even when training is disabled, but not when the whole service is off.
Production deletion occurs on the next enabled-service startup after deployment.
Verification: focused Node syntax and diff whitespace checks passed. No production
storage was accessed; actual deletion awaits enabled-service startup on the server.
@@ -20,7 +20,7 @@ async function index(root) {
} }
async function metadata(directory) { async function metadata(directory) {
const model = JSON.parse(await fs.readFile(path.join(directory, 'model.json'), 'utf8')); const model = JSON.parse(await fs.readFile(path.join(directory, 'model.json'), 'utf8'));
if (model.id !== path.basename(directory)) throw new Error('Model identity mismatch'); if (model?.id !== path.basename(directory)) throw new Error('Model identity mismatch');
return model; return model;
} }
async function prune(root, state) { async function prune(root, state) {
@@ -100,22 +100,53 @@ function saveSnapshot(root) {
return model; return model;
}); });
} }
// Explicitly retire format 1 models after the wheel-policy replacement. Only // Retire recognized older policies before workers start. Unknown/malformed or
// recognized old-format manifests qualify; malformed or future models are kept. // future manifests are not evidence that a directory is disposable.
function removeObsoleteModels(root) { function removeObsoleteModels(root) {
return serial(async () => { return serial(async () => {
const directory = path.join(root, 'models'); const obsolete = [];
for (const directory of [path.join(root, 'models'), checkpointRoot(root)]) {
let entries; let entries;
try { entries = await fs.readdir(directory, { withFileTypes: true }); } try { entries = await fs.readdir(directory, { withFileTypes: true }); }
catch (error) { if (error.code === 'ENOENT') return; throw error; } catch (error) { if (error.code === 'ENOENT') continue; throw error; }
for (const entry of entries) { for (const entry of entries) {
if (!entry.isDirectory() || !UUID.test(entry.name)) continue; if (!entry.isDirectory() || !UUID.test(entry.name)) continue;
const target = path.join(directory, entry.name); const target = path.join(directory, entry.name);
let model; let model;
try { model = await metadata(target); } try { model = await metadata(target); }
catch { continue; } catch (error) {
if (model.specification?.version === 1 && !leases.has(target)) { if (error.code === 'ENOENT' || error instanceof SyntaxError || error.message === 'Model identity mismatch') continue;
await fs.rm(target, { recursive: true }); throw error;
}
const version = model.specification?.version;
if (Number.isInteger(version) && version >= 1 && version < POLICY_VERSION) {
obsolete.push({ target, checkpoint: directory === checkpointRoot(root), id: entry.name });
}
}
}
const state = await index(root);
const oldCheckpoints = new Set(obsolete.filter((entry) => entry.checkpoint).map((entry) => entry.id));
let changed = false;
for (const alias of ['candidate', 'latest', 'previous']) {
if (oldCheckpoints.has(state[alias])) { delete state[alias]; changed = true; }
}
// Remove aliases atomically first, so interruption cannot leave the catalog
// referring to a checkpoint that cleanup already deleted.
if (changed) {
const filename = path.join(checkpointRoot(root), 'index.json');
await fs.writeFile(`${filename}.tmp`, JSON.stringify(state));
await fs.rename(`${filename}.tmp`, filename);
}
for (const { target } of obsolete) {
if (leases.has(target)) retired.add(target);
else { await fs.rm(target, { recursive: true, force: true }); retired.delete(target); }
}
// These filenames belong to superseded optimizer/scheduler lineages, not
// policy versions. The current command policy uses lineage v4.
for (const suffix of ['', '-v1', '-v2', '-v3']) {
for (const filename of [`resume${suffix}.pt`, `resume${suffix}.tmp`,
`schedule${suffix}.json`, `schedule${suffix}.json.tmp`]) {
await fs.rm(path.join(root, 'training', filename), { force: true });
} }
} }
}); });