Skip to content

Commit c486787

Browse files
Bojan131claude
andcommitted
revert(v10-publish): restore the harness to its 9 July state
Reverts the 10 July changes (node-version preflight logging 709ffc4, the response-loss VM read-back rescue fb56929, and the response-loss assertion/ flag/entity-triples peer read e921c30) so the mainnet publish tests run exactly as they did on 9 July while the team evaluates the #1572 node fix (PR #1590) separately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent e921c30 commit c486787

1 file changed

Lines changed: 20 additions & 111 deletions

File tree

tests/publish/src/v10-publish-lib.js

Lines changed: 20 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,10 @@ import {
4141
const PUBLISH_EPOCHS = Number(process.env.PUBLISH_EPOCHS || 2);
4242
const KA_COUNT = Number(process.env.TEST_KA_BATCHES || 10);
4343
const OP_TIMEOUT_MS = Number(process.env.V10_OP_TIMEOUT_MS || 6 * 60 * 1000);
44-
// 10.0.5 mainnet cores hang the sync publish HTTP response in the post-confirm
45-
// swm-share fanout: the publish CONFIRMS on-chain in ~1 min (node log:
46-
// "On-chain confirmed: UAL=...") but the response never arrives — observed on
47-
// all 4 mainnet cores 2026-07-10, every KA, 11-min client timeouts. So: wait a
48-
// BOUNDED time for the response, then VERIFY the real outcome by reading THIS
49-
// KA's root entity back from the node's Verifiable Memory. Data present ==
50-
// the publish objectively succeeded (count success, flag response_lost); data
51-
// absent after the verify window == real failure with the timeout as evidence.
52-
const PUBLISH_RESPONSE_TIMEOUT_MS = Number(process.env.V10_PUBLISH_RESPONSE_TIMEOUT_MS || 150 * 1000);
53-
const PUBLISH_VERIFY_TIMEOUT_MS = Number(process.env.V10_PUBLISH_VERIFY_TIMEOUT_MS || 240 * 1000);
54-
const PUBLISH_VERIFY_POLL_MS = 10 * 1000;
44+
// publish must outlast the node's ACK collection (~8 min worst case) so the
45+
// 207 with the node's real error (storage_ack_insufficient, ...) arrives
46+
// instead of a client-side timeout that hides it.
47+
const PUBLISH_TIMEOUT_MS = Math.max(OP_TIMEOUT_MS, Number(process.env.V10_PUBLISH_TIMEOUT_MS || 11 * 60 * 1000));
5548

5649
// Context-graph provisioning. Publishing to Verifiable Memory needs the CG to be
5750
// registered on-chain. V10_CG_REGISTER=true registers a fresh CG (~100 TRAC on the
@@ -150,13 +143,13 @@ export function makeNodeClient(baseUrl, token) {
150143
// via alsoShareSwm/alsoPublishVm, and returns kaId/ual/txHash/authorAddress
151144
// with status "vm-confirmed". We map that back to the old response shape so
152145
// the summary/report code stays unchanged.
153-
publish: (contextGraphId, quads, name) =>
146+
publish: (contextGraphId, quads) =>
154147
req(
155148
'POST',
156149
'/api/knowledge-assets',
157150
{
158151
contextGraphId,
159-
name: name || `jenkins-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
152+
name: `jenkins-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
160153
quads,
161154
alsoShareSwm: true,
162155
alsoPublishVm: { publishEpochs: PUBLISH_EPOCHS },
@@ -170,10 +163,6 @@ export function makeNodeClient(baseUrl, token) {
170163
})),
171164
query: (sparql, contextGraphId, view = 'verifiable-memory') =>
172165
req('POST', '/api/query', { sparql, contextGraphId, view }).then((r) => r.data),
173-
// named-KA descriptor (state + UAL once published) — used to recover the
174-
// UAL when the publish response was lost to the swm-share response hang.
175-
asset: (name, contextGraphId) =>
176-
req('GET', `/api/knowledge-assets/${encodeURIComponent(name)}?contextGraphId=${encodeURIComponent(contextGraphId)}`).then((r) => r.data),
177166
// cross-node read: ask a PEER (by peerId) for a KA's triples. lookup is
178167
// { lookupType, ual } for ENTITY_BY_UAL (resolves a UAL → triples; works for
179168
// any node's KA) or { lookupType:'ENTITY_TRIPLES', entityUri } for an entity.
@@ -304,17 +293,7 @@ export function defineChainPublishSuite(config) {
304293
: `✅ present (length ${trimmedToken.length})`;
305294
console.log(`🔑 ${name} bearer token: ${tokenState}`);
306295

307-
// Log the node's version+commit — when publishes hang, the FIRST question
308-
// is "which release is this node actually running?" (operator nodes may
309-
// lag the npm mainnet tag), so put the answer in every build log.
310-
try {
311-
const st = await makeNodeClient(hostname, token).status();
312-
console.log(`📟 ${name} node version: ${st.version}${st.commitShort ? ` (commit ${st.commitShort})` : ''}${st.distTag ? ` [${st.distTag}]` : ''}`);
313-
} catch (error) {
314-
console.log(`📟 ${name} node version: unreadable (${String(error.message).slice(0, 80)})`);
315-
}
316-
317-
let publishSuccess = 0, publishFail = 0, publishRescued = 0;
296+
let publishSuccess = 0, publishFail = 0;
318297
let querySuccess = 0, queryFail = 0;
319298
let vmGetSuccess = 0, vmGetFail = 0;
320299
let queryRemoteSuccess = 0, queryRemoteFail = 0;
@@ -390,16 +369,14 @@ export function defineChainPublishSuite(config) {
390369
for (let i = 0; i < KA_COUNT; i++) {
391370
console.log(`\nPublishing KA #${i + 1} on ${name}`);
392371
const { quads, rootEntity } = buildQuads(name, i + 1);
393-
const kaName = `jenkins-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
394372

395373
let ual = null;
396-
let publishOk = false;
397374
let step = 'publish';
398375

399376
// ── 1. publish (mint to Verifiable Memory) ─────────────────────────
400377
const pubStart = Date.now();
401378
try {
402-
const result = await withTimeout(client.publish(contextGraphId, quads, kaName), 'publish', name, PUBLISH_RESPONSE_TIMEOUT_MS);
379+
const result = await withTimeout(client.publish(contextGraphId, quads), 'publish', name, PUBLISH_TIMEOUT_MS);
403380
assert.ok(result, 'Publish returned no result');
404381
if (result.status !== 'confirmed') {
405382
// Test-side message stays SHORT; the node's own lifecycle error
@@ -432,52 +409,13 @@ export function defineChainPublishSuite(config) {
432409
console.log(` ↳ first ${sample.length} child KA UAL(s):`);
433410
sample.forEach((u) => console.log(` ${u}`));
434411
}
435-
publishOk = true;
436412
publishSuccess++;
437413
publishDurations.push(Date.now() - pubStart);
438414
} catch (error) {
439-
// Response lost (timeout / connection drop)? The node may still
440-
// have confirmed the publish on-chain — 10.0.5 hangs the sync
441-
// response in the post-confirm swm-share fanout. Read THIS KA's
442-
// root entity back from the node's VM to learn the real outcome:
443-
// present == success (flagged response-lost), absent == failure.
444-
const responseLost = /Timeout after/.test(error?.message ?? '') || error?.network === true;
445-
let rescued = false;
446-
if (responseLost) {
447-
console.log(`⏳ Publish response lost on ${name} — verifying the real outcome via VM read-back (up to ${Math.round(PUBLISH_VERIFY_TIMEOUT_MS / 1000)}s)…`);
448-
const deadline = Date.now() + PUBLISH_VERIFY_TIMEOUT_MS;
449-
while (Date.now() < deadline) {
450-
try {
451-
const probe = await client.query(`SELECT ?p ?o WHERE { <${rootEntity}> ?p ?o } LIMIT 1`, contextGraphId, 'verifiable-memory');
452-
if (queryHasData(probe)) { rescued = true; break; }
453-
} catch { /* transient read error — keep polling */ }
454-
await sleep(PUBLISH_VERIFY_POLL_MS);
455-
}
456-
}
457-
if (rescued) {
458-
publishOk = true;
459-
publishSuccess++;
460-
publishRescued++;
461-
// The lifecycle record can flip to `published` a beat after the
462-
// VM data lands — retry the descriptor read briefly.
463-
for (let attempt = 0; attempt < 3 && !ual; attempt++) {
464-
try {
465-
const hist = await client.asset(kaName, contextGraphId);
466-
ual = hist?.publishedUal || hist?.ual || null;
467-
const rescuedAddr = hist?.publisherAddress || hist?.authorAddress;
468-
if (rescuedAddr) publisherAddresses.add(rescuedAddr);
469-
} catch { /* descriptor read failed — remote read falls back to FALLBACK_UAL */ }
470-
if (!ual) await sleep(5000);
471-
}
472-
// NOT pushed into publishDurations: the latency is dominated by
473-
// our own response-wait, so the avg stays clean-response-only.
474-
console.log(`✅ Published KA #${i + 1} — CONFIRMED via VM read-back after response loss (node response-hang bug) in ${formatDuration(Date.now() - pubStart)}${ual ? ` | UAL: ${ual}` : ' | UAL unrecovered'}`);
475-
} else {
476-
await logError(error, name, step, errorStats, i + 1, { baseUrl: client.baseUrl });
477-
console.log(`❌ Publish failed | No UAL`);
478-
failedAssets.push(`KA #${i + 1} (Publish failed)`);
479-
publishFail++;
480-
}
415+
await logError(error, name, step, errorStats, i + 1, { baseUrl: client.baseUrl });
416+
console.log(`❌ Publish failed | No UAL`);
417+
failedAssets.push(`KA #${i + 1} (Publish failed)`);
418+
publishFail++;
481419
}
482420

483421
// Reads are RUN-SPECIFIC when the publish succeeded — they target THIS
@@ -493,7 +431,7 @@ export function defineChainPublishSuite(config) {
493431
const queryStart = Date.now();
494432
try {
495433
let result;
496-
if (publishOk) {
434+
if (ual) {
497435
const sparql = `SELECT ?s ?name WHERE { ?s <http://schema.org/isPartOf> <${rootEntity}> ; <http://schema.org/name> ?name } LIMIT 5`;
498436
result = await readWithRetry('query', name, () => client.query(sparql, contextGraphId, 'verifiable-memory'));
499437
} else if (FALLBACK_UAL) {
@@ -516,7 +454,7 @@ export function defineChainPublishSuite(config) {
516454
const vmGetStart = Date.now();
517455
try {
518456
let result;
519-
if (publishOk) {
457+
if (ual) {
520458
const entitySparql = `SELECT ?p ?o WHERE { <${rootEntity}> ?p ?o } LIMIT 5`;
521459
result = await readWithRetry('VM GET', name, () => client.query(entitySparql, contextGraphId, 'verifiable-memory'));
522460
} else if (FALLBACK_UAL) {
@@ -544,18 +482,12 @@ export function defineChainPublishSuite(config) {
544482
const remoteNode = nodes[otherIndexes[Math.floor(Math.random() * otherIndexes.length)]];
545483
const remoteStart = Date.now();
546484
try {
547-
if (!readUal && !publishOk) throw new Error('Query Remote (sync): publish failed and no DKG_FALLBACK_UAL configured');
485+
if (!readUal) throw new Error('Query Remote (sync): publish failed and no DKG_FALLBACK_UAL configured');
548486
const remotePeerId = await getPeerId(remoteNode);
549487
if (!remotePeerId) throw new Error(`Could not resolve peerId for ${remoteNode.name} (${remoteNode.hostname})`);
550-
// Run-specific even when the UAL could not be recovered (response
551-
// lost, #1572): ask the peer for THIS KA's unique root entity
552-
// directly — a pass proves this run's KA propagated cross-node.
553-
const lookup = publishOk && !ual
554-
? { lookupType: 'ENTITY_TRIPLES', entityUri: rootEntity }
555-
: { lookupType: 'ENTITY_BY_UAL', ual: readUal };
556-
const result = await readWithRetry('Query Remote (sync)', remoteNode.name, () => client.queryRemote(remotePeerId, contextGraphId, lookup));
557-
assert.ok(queryHasData(result), `Query Remote (sync) returned no triples for ${lookup.ual || lookup.entityUri} from ${remoteNode.name}`);
558-
console.log(`✅ Query Remote (sync) succeeded — ${remoteNode.name} has the KA (synced${publishOk && !ual ? ', by entity URI' : ''})`);
488+
const result = await readWithRetry('Query Remote (sync)', remoteNode.name, () => client.queryRemote(remotePeerId, contextGraphId, { lookupType: 'ENTITY_BY_UAL', ual: readUal }));
489+
assert.ok(queryHasData(result), `Query Remote (sync) returned no triples for ${readUal} from ${remoteNode.name}`);
490+
console.log(`✅ Query Remote (sync) succeeded — ${remoteNode.name} has the KA (synced)`);
559491
queryRemoteSuccess++;
560492
queryRemoteDurations.push(Date.now() - remoteStart);
561493
} catch (error) {
@@ -584,7 +516,7 @@ export function defineChainPublishSuite(config) {
584516
}
585517

586518
globalStats[blockchainName][name] = {
587-
publishSuccess, publishFail, publishRescued,
519+
publishSuccess, publishFail,
588520
querySuccess, queryFail,
589521
vmGetSuccess, vmGetFail,
590522
queryRemoteSuccess, queryRemoteFail,
@@ -597,9 +529,6 @@ export function defineChainPublishSuite(config) {
597529
node_wallet: nodeWalletAddrs.join(', '),
598530
node_publisher_wallets: [...publisherAddresses].join(', '),
599531
publish_success_rate: safeRate(publishSuccess, publishFail),
600-
// publishes whose HTTP response was lost to the 10.0.5 response-hang
601-
// but were CONFIRMED via VM read-back (counted in the success rate)
602-
publish_response_lost: publishRescued,
603532
query_success_rate: safeRate(querySuccess, queryFail),
604533
publisher_get_success_rate: safeRate(vmGetSuccess, vmGetFail), // VM GET
605534
non_publisher_get_success_rate: safeRate(queryRemoteSuccess, queryRemoteFail), // Query Remote (sync)
@@ -622,36 +551,16 @@ export function defineChainPublishSuite(config) {
622551
const errorsFileName = `errors_${name.replace(/\s+/g, '_')}.json`;
623552
fs.writeFileSync(errorsFileName, JSON.stringify(errorData, null, 2));
624553
console.log(`✅ Saved errors to ${errorsFileName}`);
625-
626-
// Response-loss flag: consumed by the Jenkins pipeline to mark the
627-
// build UNSTABLE while node bug OriginTrail/dkg#1572 is active.
628-
// Appended (not truncated) — parallel node stages share the workspace.
629-
if (publishRescued > 0) {
630-
fs.appendFileSync('response_lost.flag', `${name}: ${publishRescued}\n`);
631-
}
632554
}
633555
});
634556

635-
// Runs AFTER the main test (summaries already written, Grafana import
636-
// unaffected — the pipeline wraps stages in catchError(buildResult:
637-
// 'SUCCESS')). Fails the STAGE whenever any publish response was lost,
638-
// so node bug OriginTrail/dkg#1572 is impossible to overlook while the
639-
// publish stats above still tell the true success story.
640-
it('publish responses arrived in time (node bug OriginTrail/dkg#1572 inactive)', function () {
641-
const lost = Object.values(globalStats[blockchainName] || {})
642-
.reduce((s, st) => s + (st.publishRescued || 0), 0);
643-
assert.equal(lost, 0,
644-
`${lost} publish response(s) never arrived and were confirmed via VM read-back instead — ` +
645-
`sync publish response hang (OriginTrail/dkg#1572) is active on this chain's cores`);
646-
});
647-
648557
after(() => {
649558
console.log(`\n\nGlobal Publish Summary:`);
650559
Object.entries(globalStats).forEach(([blockchain, nodeStats]) => {
651560
console.log(`\n🔗 Blockchain: ${blockchain}`);
652561
Object.entries(nodeStats).forEach(([nodeName, stats]) => {
653562
console.log(` • ${nodeName}:`);
654-
console.log(` Publish: ✅ ${stats.publishSuccess} / ❌ ${stats.publishFail} -> ${safeRate(stats.publishSuccess, stats.publishFail)}%${stats.publishRescued ? ` (${stats.publishRescued} confirmed via VM read-back after response loss — node response-hang bug)` : ''}`);
563+
console.log(` Publish: ✅ ${stats.publishSuccess} / ❌ ${stats.publishFail} -> ${safeRate(stats.publishSuccess, stats.publishFail)}%`);
655564
console.log(` Query: ✅ ${stats.querySuccess} / ❌ ${stats.queryFail} -> ${safeRate(stats.querySuccess, stats.queryFail)}%`);
656565
console.log(` VM GET: ✅ ${stats.vmGetSuccess} / ❌ ${stats.vmGetFail} -> ${safeRate(stats.vmGetSuccess, stats.vmGetFail)}%`);
657566
console.log(` Query Remote (sync): ✅ ${stats.queryRemoteSuccess} / ❌ ${stats.queryRemoteFail} -> ${safeRate(stats.queryRemoteSuccess, stats.queryRemoteFail)}%`);

0 commit comments

Comments
 (0)