v3.14.1: the counter probe demands its PoR

_counter_probe_params validates SPI2, forces b1 (PoR required; a caller's
00 becomes 01) and defaults to the RAM listing transport 21; the no-PoR
error names the effective SPI2, and ramSyncCounter posts
ramListingSpi2('40') for the RAM view (the failed operation's transport).

Live 2026-10-02 (NP BAP): the probe had sent the plain SCP80 form's 00
(no PoR) - the card answered the ENVELOPE 9000 and sent nothing, so the
probe stopped after one packet with "card verdict: no PoR".

Docs: docs/api.md, repo AGENTS.  sw cache simple-v376.
Tests: 670 Python + 798 frontend green.
This commit is contained in:
2026-10-02 14:40:07 +03:00
parent c18b6cd0d4
commit 475c13aeb8
7 changed files with 60 additions and 12 deletions
+7 -2
View File
@@ -1877,7 +1877,7 @@
// ===== Version =====
// Single source of truth for the PWA version: shown in the header and used
// by the server version check in pysimConnect().
const SIMPLE_VERSION = '3.14.0';
const SIMPLE_VERSION = '3.14.1';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching =====
@@ -11323,7 +11323,12 @@ async function ramSyncCounter(presetIdx, view) {
const tar = val('sp-tar').replace(/[^0-9a-fA-F]/g, '').toUpperCase();
if (tar) body.tar = tar;
if (val('sp-spi1')) body.spi1 = val('sp-spi1');
if (val('sp-spi2-hex')) body.spi2 = val('sp-spi2-hex');
// The probe needs a PoR: use the transport the failed operation used - the
// RAM listings pin SPI2 0x21 (ramListingSpi2); a plain send whose SPI2
// requested no PoR cannot raise the low-counter notice, so the form's
// value is already PoR-requesting here.
if (view === 'ram') body.spi2 = ramListingSpi2('40');
else if (val('sp-spi2-hex')) body.spi2 = val('sp-spi2-hex');
const cntr = val('sp-cntr').replace(/[^0-9a-fA-F]/g, '');
if (cntr) body.cntr = cntr;
if (box) box.innerHTML = '<div class="text-sm text-gray-500 dark:text-slate-400">' +
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simple-v375';
const CACHE = 'simple-v376';
const URLS = [
'index.html',
'help.html',
+2
View File
@@ -981,6 +981,8 @@ test('every SCP80/RAM flow checks the low counter and stops', () => {
assert.ok(sync.includes('kvnOf(other)'), sync);
assert.ok(sync.includes("getElementById('sp-cntr')"), sync);
assert.ok(sync.includes('spKeysetSync()'), 'the keyset labels must refresh');
assert.ok(sync.includes("ramListingSpi2('40')"),
'the RAM view must probe with the RAM listing transport (a PoR is required)');
});
test('the RAM form offers the command format choice', () => {