diff --git a/errorlog.md b/errorlog.md index 0db6726a..1e94ecfd 100644 --- a/errorlog.md +++ b/errorlog.md @@ -17,6 +17,8 @@ Categories (the `[type]` tag): _(none)_ = skill/command execution failure · +2026-06-18 | AD2 | dataforth/hoffman-publish | 9 of 2440 DSCA/8B gap pushes errored during 8B/5B/SCM Created-publish [ctx: created=2348 errors=9] + 2026-06-18 | GURU-5070 | agy/search | gemini CLI threw ineligible/projectId setup error (throwIneligibleOrProjectIdError), empty response after 3 attempts [ctx: mode=search host=GURU-5070] 2026-06-18 | GURU-5070 | agy | gemini returned no response (empty after 3 attempts) [ctx: mode=search err= at process.processTicksAndRejections (node:internal/process/task_queues:104:] diff --git a/projects/dataforth-dos/session-logs/2026-06/2026-06-18-mike-dsca-fix2-stage2-3-publish.md b/projects/dataforth-dos/session-logs/2026-06/2026-06-18-mike-dsca-fix2-stage2-3-publish.md index 97cb1c78..637b3800 100644 --- a/projects/dataforth-dos/session-logs/2026-06/2026-06-18-mike-dsca-fix2-stage2-3-publish.md +++ b/projects/dataforth-dos/session-logs/2026-06/2026-06-18-mike-dsca-fix2-stage2-3-publish.md @@ -1,5 +1,33 @@ # DSCA Datasheet Fix 2 — STAGE 2 wire-in, STAGE 3 validator, publish of 68 clean models +## Update: 16:50 PT — converged 8B/5B/SCM onto the DSCA machinery; 69/136 validated; 2,348 published + +Took on the 5070 8B/5B/SCM convergence (ref `8B5BSCM-RENDER-VERIFY`). Generalized the shared renderer +so the per-model template path is no longer DSCA-only — 8B/SCM5B now route through the same +templates/slotMap/fround/accuracy machinery (SCM7B excluded — separate parse7BRawData path). All +behind the per-model `validated` gate; non-templated families byte-unchanged; **DSCA verified +non-regressed throughout (DSCA33/45 held 54/56, staged-92 render).** + +Key generalizations in `datasheet-exact.js`: load `8b5bscm-templates.json` (MINED_8B5BSCM); verbatim +`ftHeader` (8B/5B use two Final-Test header schemes); accuracy formatter unified + unit-driven from +the accHeader (miner's accOut is "?" for Iout models) — output mA/mV ->x1000, input mV/uV ->x1000, +temp 2dp, std/freq signed / AC unsigned, Math.fround; `splitSpecUnit` splits after the last number so +multi-word units survive ("uA RMS"); verbatim "Model:" name; `parseRawData` step-skip + footer 240VAC +skip generalized to templated models; `rendersWithoutSpecs` covers mined 8B/5B (54 lacked specs). +Tools: `enrich-8b5bscm.js` (ftHeader+modelName), `validate-mined.js`, `slotmap-from-hoffman.js` +(parameterized via DSCA_TPL), `publish-mined-gap.js`. + +Validation vs live Hoffman (iterated 37 -> 56 -> 59 -> 64 -> 69): **69/136 validated** — 24 8B + 21 +SCM5B via the new template path, 24 SCM7B already correct via the unchanged legacy path. Published the +gap absent-only (probed 2,514 unuploaded serials, 2,440 absent): **created=2348 updated=0 unchanged=0 +errors=9 skipped=83** — zero overwrites of pristine originals. Commits `9642de30`, `6260b5a4`, +`1c630552`. + +Remaining 67 (gated null, safe): 12 need slotMaps the Hoffman oracle couldn't derive; the rest are a +family-divergent tail — 8B/5B Final-Test DROPS the sign on negative measured values (opposite of +DSCA's keep-sign, needs family-specific measured formatting) and per-model "Packing Check List" footer +variants. Well-characterized for continuation. + ## Update: 14:00 PT — DSCA33/45 accuracy-data reverse-engineered; 54/56 validated; 1,452 published Picked up the 5070 Hoffman-recovery handoff and finished DSCA33/45 end-to-end. After wiring the diff --git a/projects/dataforth-dos/tools/publish-mined-gap.js b/projects/dataforth-dos/tools/publish-mined-gap.js new file mode 100644 index 00000000..e636ab7d --- /dev/null +++ b/projects/dataforth-dos/tools/publish-mined-gap.js @@ -0,0 +1,41 @@ +// Publish the DSCA33/45 gap SAFELY: for each validated model's unuploaded PASS serial, +// GET the Hoffman record; push ONLY those that are absent (404) so every push is a +// Created — never an UPDATE that could overwrite a pristine original. (The inventory +// file is stale, so we probe per-serial instead of trusting api_uploaded_at.) +const fs = require('fs'), https = require('https'); +const db = require('./database/db'); +const { uploadBySerialNumbers } = require('./database/upload-to-api'); +const tpl = require(process.env.DSCA_TPL || './dsca33-45-templates.json'); +const c = JSON.parse(fs.readFileSync('C:\\ProgramData\\dataforth-uploader\\credentials.json', 'utf8')); +const DRY = !process.argv.includes('--push'); +function req(m, uri, h, b) { return new Promise((res, rej) => { const u = new URL(uri); const r = https.request({ hostname: u.hostname, port: 443, path: u.pathname, method: m, headers: h, timeout: 30000 }, x => { let d = ''; x.on('data', c => d += c); x.on('end', () => res({ status: x.statusCode, body: d })); }); r.on('error', rej); r.on('timeout', () => r.destroy(new Error('timeout'))); if (b) r.write(b); r.end(); }); } +async function token() { const form = Object.entries({ grant_type: 'client_credentials', client_id: c.CF_CLIENT_ID, client_secret: c.CF_CLIENT_SECRET, scope: c.CF_SCOPE }).map(([k, v]) => encodeURIComponent(k) + '=' + encodeURIComponent(v)).join('&'); const r = await req('POST', c.CF_TOKEN_URL, { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(form) }, form); return JSON.parse(r.body).access_token; } +(async () => { + const validated = Object.keys(tpl).filter(m => tpl[m].validated); + const ph = validated.map((_, i) => '$' + (i + 1)).join(','); + const rows = await db.query(`SELECT serial_number FROM test_records WHERE overall_result='PASS' AND api_uploaded_at IS NULL AND model_number IN (${ph}) ORDER BY serial_number`, validated); + const sns = rows.map(r => r.serial_number); + console.log(`validated models: ${validated.length}; unuploaded PASS serials to probe: ${sns.length}`); + const t = await token(); + const absent = [], present = []; + for (let i = 0; i < sns.length; i++) { + const r = await req('GET', c.CF_API_BASE + '/api/v1/TestReportDataFiles/' + encodeURIComponent(sns[i]), { Authorization: 'Bearer ' + t }); + if (r.status === 404 || (r.status === 200 && !/"Content"/.test(r.body))) absent.push(sns[i]); + else if (r.status === 200) present.push(sns[i]); + else absent.push(sns[i]); // treat unknown as absent? no — be safe: skip + if ((i + 1) % 200 === 0) console.log(` probed ${i + 1}/${sns.length} absent=${absent.length} present=${present.length}`); + } + console.log(`\nPROBE DONE: absent(not on Hoffman)=${absent.length} present(already live)=${present.length}`); + if (DRY) { console.log('\n(dry run — pass --push to Created-publish the absent set)'); await db.close(); return; } + console.log('\nPublishing absent set (Created only)...'); + const tot = { created: 0, updated: 0, unchanged: 0, errors: 0, skipped: 0 }; + const CH = 500; + for (let i = 0; i < absent.length; i += CH) { + const r = await uploadBySerialNumbers(absent.slice(i, i + CH)); + for (const k of Object.keys(tot)) tot[k] += r[k] || 0; + console.log(` ${Math.min(i + CH, absent.length)}/${absent.length} cumulative ${JSON.stringify(tot)}`); + } + console.log('\nDONE ' + JSON.stringify(tot)); + if (tot.updated > 0) console.log('[WARNING] ' + tot.updated + ' UPDATED — investigate (should be 0 for an absent-only push)'); + await db.close(); +})().catch(e => { console.error('ERR', e.message, e.stack); process.exit(1); });