test: add devnet e2e sections 18-24 covering V10 feature gaps#120
test: add devnet e2e sections 18-24 covering V10 feature gaps#120branarakic merged 15 commits intov10-rcfrom
Conversation
New sections cover sync protocol catch-up verification, memory layer view queries (VM/SWM/WM isolation), context graph existence checks, SWM TTL settings CRUD, import-file extraction lifecycle, publisher queue end-to-end flow, authorization/error edge cases, and sub-graph query isolation with gossip verification. Made-with: Cursor
| SYNC_SWM=$(c -X POST "http://127.0.0.1:9205/api/query" -d "{ | ||
| \"sparql\":\"SELECT ?name WHERE { <http://example.org/entity/city1> <http://schema.org/name> ?name }\", | ||
| \"contextGraphId\":\"$CONTEXT_GRAPH\", | ||
| \"view\":\"shared-memory\" |
There was a problem hiding this comment.
🔴 Bug: /api/query does not recognize "shared-memory" as a valid view value; the supported SWM view is "shared-working-memory". As written, this query returns a 400 error that the script then interprets as zero bindings, so the new SWM checks here (and the later view:"shared-memory" queries) cannot actually verify anything.
| check "TTL reads back as 7 days" "$TTL_DAYS_NEW" "7" | ||
|
|
||
| echo "--- 20f: Restore original TTL ---" | ||
| curl -s -X PUT -H "Authorization: Bearer $AUTH" -H "Content-Type: application/json" "http://127.0.0.1:9201/api/settings/shared-memory-ttl" -d "{\"ttlDays\":$TTL_DAYS_ORIG}" > /dev/null 2>&1 |
There was a problem hiding this comment.
🔴 Bug: the restore step uses TTL_DAYS_ORIG, but the GET endpoint rounds ttlDays from ttlMs. If the original TTL was not an exact whole number of days, this permanently changes the setting after the test. Restore from ttlMs with an exact fractional-day value, and verify the PUT response before reporting success.
| c -X POST "http://127.0.0.1:9201/api/assertion/create" -d "{\"contextGraphId\":\"$CONTEXT_GRAPH\",\"name\":\"$WM_NAME\"}" > /dev/null | ||
| c -X POST "http://127.0.0.1:9201/api/assertion/$WM_NAME/write" -d "{ | ||
| \"contextGraphId\":\"$CONTEXT_GRAPH\", | ||
| \"quads\":[$(ql 'urn:wm-view:only' 'http://schema.org/name' 'WM Only Data')] |
There was a problem hiding this comment.
🟡 Issue: this subject IRI is fixed, but the follow-up checks query outside the assertion graph (19d/19e). On a reused devnet, leftover data from an earlier run can make those negative assertions fail even when WM isolation is correct for this run. Use a unique subject derived from WM_NAME or a timestamp.
| \"view\":\"shared-memory\" | ||
| }") | ||
| ROOT_ALPHA_CT=$(echo "$ROOT_ALPHA" | python3 -c 'import sys,json;print(len(json.load(sys.stdin).get("result",{}).get("bindings",[])))' 2>/dev/null || echo "0") | ||
| [[ "$ROOT_ALPHA_CT" -eq 0 ]] && ok "Sub-graph alpha data absent from root CG SWM" || warn "Sub-graph data visible in root CG ($ROOT_ALPHA_CT) — may be expected if SWM shares a graph" |
There was a problem hiding this comment.
🔴 Bug: a root CG query seeing sub-graph-only SWM data is an isolation regression, but this only warns and lets the suite pass. Because root and sub-graph shared-memory live in different graph URIs, this case should fail the test rather than be treated as "may be expected".
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use correct view value "shared-working-memory" (not "shared-memory") for all SWM view queries (sections 18c, 19b, 24e) - Restore SWM TTL from ttlMs for precision and verify PUT response - Make WM subject IRI unique per run to avoid false negatives on reused devnets - Promote root CG sub-graph isolation check from warn to fail since root and sub-graph SWM use different graph URIs Made-with: Cursor
| $SYNC_OK && ok "Sync catch-up completed on Node5 (status=$SYNC_ST)" || warn "Sync catch-up not completed after 40s (status=$SYNC_ST)" | ||
|
|
||
| echo "--- 18b: Verify synced VM data on Node5 ---" | ||
| SYNC_VM=$(c -X POST "http://127.0.0.1:9205/api/query" -d "{ |
There was a problem hiding this comment.
🔴 Bug: This validates catch-up with city1, but section 3 already proves that city1 is present on Node5's verified memory before this subscribe call runs. A broken catch-up path can still pass here from preexisting state. Use data created after the subscribe (or a fresh/cleared node) so this actually exercises sync.
| [[ "$WM_IN_VM_CT" -eq 0 ]] && ok "WM data correctly absent from verified memory" || fail "WM data leaked into verified memory ($WM_IN_VM_CT)" | ||
|
|
||
| echo "--- 19e: WM data NOT visible on Node2 ---" | ||
| WM_REMOTE=$(c -X POST "http://127.0.0.1:9202/api/query" -d "{ |
There was a problem hiding this comment.
🔴 Bug: This query does not set includeSharedMemory or graphSuffix, so it only checks Node2's root data graph. If WM data regresses into shared memory on another node, this still returns 0 and the test passes. Query the SWM graph as well, or use includeSharedMemory: true plus a separate root-graph assertion.
| check "TTL reads back as 7 days" "$TTL_DAYS_NEW" "7" | ||
|
|
||
| echo "--- 20f: Restore original TTL ---" | ||
| TTL_RESTORE=$(curl -s -X PUT -H "Authorization: Bearer $AUTH" -H "Content-Type: application/json" "http://127.0.0.1:9201/api/settings/shared-memory-ttl" -d "{\"ttlMs\":$TTL_MS_ORIG}") |
There was a problem hiding this comment.
🔴 Bug: PUT /api/settings/shared-memory-ttl only accepts ttlDays today. Sending {"ttlMs":...} here will fail, which leaves the node stuck at 7 days for the rest of the script instead of restoring the original setting. Convert TTL_MS_ORIG back to days before restoring, or update the endpoint to accept ttlMs.
| [[ "$PQ_FINAL_ST" == "finalized" || "$PQ_FINAL_ST" == "included" || "$PQ_FINAL_ST" == "failed" ]] && break | ||
| sleep 3 | ||
| done | ||
| [[ "$PQ_FINAL_ST" == "finalized" || "$PQ_FINAL_ST" == "included" ]] && ok "Publisher job reached $PQ_FINAL_ST" || warn "Publisher job status: $PQ_FINAL_ST" |
There was a problem hiding this comment.
🔴 Bug: A failed/timed-out publisher job is only a warning here, so the suite can still exit successfully with a broken end-to-end publisher queue. Since this section is the only real validation of /api/publisher/enqueue + job processing, anything other than included/finalized should be a hard failure.
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
….1/§10.2)
The markdown-extraction pipeline in packages/cli was emitting structural
content triples but not the source-file linkage triples required by
19_MARKDOWN_CONTENT_TYPE.md §10.1 and §10.2. Without those triples, the
assertion graph had no way to rediscover the original file blob after a
daemon restart — the in-memory extractionStatus map was the only surviving
linkage, and it vanished on restart.
This commit implements the full 20-row spec-mandated source-file linkage
contract from §10.1 (data-graph entity linkage) and §10.2 (_meta graph
metadata), split between the extractor and the import-file route handler:
**Extractor (packages/cli/src/extraction/markdown-extractor.ts)**
- Removes the non-spec dkg:derivedFrom / prov:wasGeneratedBy provenance
block entirely — daemon owns all provenance emission now.
- Emits rows 1-3 on the document subject IRI: dkg:sourceFile (to fileUri),
dkg:sourceContentType "text/markdown" (always the extractor input type,
even for PDF where the MD intermediate is what the extractor processed),
dkg:rootEntity (reflexive by default, frontmatter override supported).
- Frontmatter rootEntity key is consumed so it doesn't leak through as
schema:rootEntity via the generic frontmatter-to-predicate fallthrough.
**Daemon (packages/cli/src/daemon.ts import-file route handler)**
- Computes keccak256 via ethers.keccak256 alongside sha256. FileStoreEntry
now exposes both hashes; ImportFileResponse.fileHash + ExtractionStatusRecord
+ mdIntermediateHash all switched to keccak256 per spec §2.1:658 (file store
is keccak256-addressed).
- Mints fileUri as urn:dkg:file:keccak256:<hex> and passes it into the extractor.
- After Phase 2, builds rows 4-8 (file descriptor block: rdf:type dkg:File,
dkg:contentHash, dkg:fileName, dkg:contentType, dkg:size).
- Mints one fresh urn:dkg:extraction:<uuidv4> per import and emits rows 9-13
(ExtractionProvenance block with dkg:extractedFrom <fileUri>, dkg:extractedBy,
dkg:extractedAt, dkg:extractionMethod "structural").
- After assertion.write, writes rows 14-19 (always) and row 20 (conditionally,
only when Phase 1 actually ran for PDF/DOCX uploads) into the CG ROOT _meta
graph via agent.store.insert with explicit contextGraphMetaUri(contextGraphId)
— NEVER the sub-graph _meta graph, per §16.2.1:3449-3466.
- Row 15 (_meta sourceContentType) uses the ORIGINAL upload content type
(e.g. "application/pdf"), distinct from row 2 (data-graph sourceContentType)
which uses the extractor input "text/markdown". This row-2-vs-row-15 split
is explicitly tested in both directions.
- _meta insert failures flow through recordFailedExtraction so
/extraction-status doesn't get stuck at in_progress on partial writes.
**FileStore (packages/cli/src/file-store.ts)**
- Dual-hash store: sha256 remains the on-disk primary for back-compat; a
keccak256 pointer file is written under keccak256/<shard>/<hex> containing
the sha256 hex so FileStore.get() accepts either prefix and resolves to
the same blob.
- Non-breaking for any existing sha256 callers.
**Tests**
- extraction-markdown.test.ts grows from 36 to 41 tests: explicit assertions
that dkg:derivedFrom is NEVER emitted; rows 1-3 coverage with and without
sourceFileIri; rootEntity override precedence (frontmatter > explicit input
> reflexive); rootEntity frontmatter key no longer leaks through the
generic predicate fallthrough.
- import-file-integration.test.ts grows from 25 to 31 tests: row 4-13 data-graph
descriptor and provenance block verification with fresh UUIDv4 per import;
row 14-19 _meta graph verification; PDF content-type split (row 2 = text/markdown
AND row 15 = application/pdf on the same import); sub-graph routing with
explicit assertion that _meta quads always land in CG root meta, never
sub-graph meta; daemon-restart recovery (clear extractionStatus map, recover
hash from captured _meta quads, re-fetch blob via FileStore.get with byte
equality); FileStore dual-prefix acceptance.
Refs: 19_MARKDOWN_CONTENT_TYPE.md §10.1, §10.2, §3.2, §4
05_PROTOCOL_EXTENSIONS.md §6.3, §6.5
03_PROTOCOL_CORE.md §2.1 (file store)
EXAMPLE_FULL_FLOW.md §2
Companion spec PR: OriginTrail/dkgv10-spec#86
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
review Hardens scripts/devnet-test.sh sections 18-24 (introduced in PR #120) against a review pass that surfaced 4 P0 (blocking), 11 P1 (fix-in-PR), and 7 P2 (nice-to-have) findings. All P0 and P1 addressed; 5 of 7 P2 addressed. **P0 (all 4)** - §21 now SPARQLs the assertion data graph for the new source-file linkage triples (dkg:sourceFile, dkg:sourceContentType, dkg:rootEntity) AND the CG root _meta graph for dkg:sourceFileHash with keccak256 format regex and a drift-check that the _meta hash equals the wire fileHash from the import response. §21h asserts dkg:mdIntermediateHash is ABSENT for markdown uploads (row 20 is Phase-1-only). Previously §21 only checked the in-memory ImportFileResponse fileHash — a daemon that wrote zero linkage triples would have silently passed. - §23a (no-auth 401) now detects DEVNET_NO_AUTH=1 explicitly and emits [SKIP] cleanly; hard-fails if a real auth regression returns 200. Previously silently degraded to WARN under DEVNET_NO_AUTH=1, masking real regressions. - §23c / §23g switched from substring-grep-on-body to new http_post_capture helper that captures both body AND HTTP status; now requires 4xx AND error token. A 500 with body {"error":"internal"} no longer false-passes. - §18a catchup polling removed "idle" from success markers (idle is the INITIAL pre-catchup state, not a completion). Only completed|synced|done break the poll loop. **P1 (all 11)** - New c() helper bounds curl with --max-time 30 --connect-timeout 5 (env-overridable via DEVNET_CURL_TIMEOUT / DEVNET_CURL_CONNECT_TIMEOUT). A hung node can no longer stall CI ~40 min per polling section. - json_get normalizes Python booleans to lowercase true/false; all check "..." "True" callsites flipped to "true". - New safe_bindings_count and safe_quads_count helpers emit a PARSE_ERR sentinel on schema drift instead of silently returning "0". ~20 call sites converted. - §20d/§20f now route through c -X PUT for consistent timeout + auth. - §22c/§22d fragile inline python ternary replaced with explicit try/except that surfaces __ERR__ / __MISSING__ sentinels distinctly from legitimate status values. - New §21i: PNG upload graceful-degrade negative test asserts extraction.status == "skipped" AND tripleCount == 0 AND pipelineUsed null (spec §6.5 graceful-degrade path had zero coverage). - New §24g: write-to-unregistered-sub-graph negative test with nanosecond- suffixed name, requires 4xx response. - §23b no longer conflates PARSE_ERR with legitimate empty results. - §22a asserts triplesWritten >= 2 before 22b enqueues; a silent zero- write can no longer hide a broken publisher queue. - §21e promote check excludes __ERR__ in addition to __NONE__ and "0". - §18b/§18c hard-fail (not warn) when sync claimed completion but VM/SWM data is missing ("catchup reported complete but data missing — bug"). Paired with P0-4 this closes the two-layer cover that catchup bugs previously had. **P2 (5 of 7)** - GOSSIP_WAIT_S env var for the §24b settle window. - Explicit [SKIP] log when §22c-f skip due to missing jobId. - DEVNET_TMPDIR honors $TMPDIR for Windows/WSL developers. - shareOperationId / workspaceOperationId dual-lookup documented as intentional legacy alias (not an API-rename drift). - §24f gossip single sleep replaced with 5×1s poll-and-break loop. Not addressed (scope): P2-5 (positive auth control — adds new coverage rather than hardening existing) and P2-7 (stable test IDs — pure refactor). New helpers added: c(), ok/fail/warn/skip, json_get, check, safe_bindings_count, safe_quads_count, http_post_capture. Refs: spec §10.1, §10.2, §6.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ples feat: source-file linkage triples + devnet hardening (chain onto PR #120)
| // `releaseLock` is invoked in the outer `finally` block at the | ||
| // bottom of the handler so the next waiter unblocks regardless | ||
| // of success, failure, return, or throw. | ||
| const previousLock = assertionImportLocks.get(assertionUri) ?? Promise.resolve(); |
There was a problem hiding this comment.
🔴 Bug: This mutex still comes after other async work (listSubGraphs() and fileStore.put()), so same-assertion imports can still reorder before they ever queue here. A later small upload can reach this line first and commit ahead of an earlier large upload, which means the new "request-order" guarantee is not actually enforced. If ordering matters, acquire the per-assertion lock immediately after deriving assertionUri and move startedAt below the wait so extractedAt / status timestamps reflect actual processing time.
…entity fix: align markdown rootEntity override with promote identity
| let metaCleanupSucceeded = false; | ||
| let dataDropSucceeded = false; | ||
| try { | ||
| await agent.store.deleteByPattern({ subject: assertionUri, graph: metaGraph }); |
There was a problem hiding this comment.
🔴 Bug: This rollback state machine assumes a store mutation only happened if the promise resolved. That is not true for the current remote adapters: deleteByPattern() already performs the DELETE before its follow-up count query, and insert() / dropGraph() can also fail after the server has committed. In those ambiguous-failure cases these booleans are wrong, so we either skip a needed restore or reinsert the old snapshot on top of already-written new data. Please make the recovery decision from a post-failure re-read of both graphs, or move this whole sequence behind a storage-side transaction/CAS primitive instead of client-side success flags.
| * makes the contract honest: this field contains linkage triples, | ||
| * full stop. | ||
| */ | ||
| sourceFileLinkage: Quad[]; |
There was a problem hiding this comment.
🟡 Issue: This renames the extractor result to sourceFileLinkage, but the shared @origintrail-official/dkg-core ExtractionOutput contract still exposes provenance. That leaves the repo with two incompatible type contracts for the same phase-2 payload, and the next cross-package consumer will compile against the wrong field name. Please update the shared core type/export in the same PR so the rename is repo-wide.
- Fix TS2304: pass assertionImportLocks to handleRequest (was defined in daemon closure scope but referenced in standalone function) - Section 18: write fresh SWM data after subscribe instead of relying on pre-existing city1 that may already be on Node5, then poll for sync arrival - Section 19e: add includeSharedMemory:true to WM leak check on Node2 so regressions into SWM are caught - Section 20f: restore TTL via ttlDays (computed from ttlMs for precision) since the PUT endpoint only accepts ttlDays - Section 22c: promote publisher job timeout/failure from warn to hard fail — this is the only e2e validation of the publisher queue Made-with: Cursor
| try { | ||
| await agent.createContextGraph({ id, name, description }); | ||
| const skipChain = agent.publisher.getIdentityId() <= 0n; | ||
| await agent.createContextGraph({ id, name, description, private: skipChain }); |
There was a problem hiding this comment.
🔴 Bug: private: skipChain silently changes a normal context-graph create into a local-only/private graph whenever the node has no cached identity ID. On no-chain setups, or after a transient ensureProfile failure, this now returns the same success payload for a graph that will never gossip or register on-chain. Prefer surfacing an explicit error or retrying agent.ensureIdentity() here instead of inferring private from getIdentityId() <= 0n (the same issue is repeated in the legacy route below).
| // `releaseLock` is invoked in the outer `finally` block at the | ||
| // bottom of the handler so the next waiter unblocks regardless | ||
| // of success, failure, return, or throw. | ||
| const previousLock = assertionImportLocks.get(assertionUri) ?? Promise.resolve(); |
There was a problem hiding this comment.
🔴 Bug: this mutex is still acquired only after the request has already gone through readBodyBuffer(...), multipart parsing, and fileStore.put(...) above. A later small upload can finish those pre-lock steps first, grab the lock, and commit ahead of an earlier large upload, so the new "request order determines commit order" guarantee is still false. If request-order serialization is required, queue before the expensive pre-lock work (or narrow the documented guarantee to post-upload/post-parse order).
Remove the `private: skipChain` logic that was accidentally included from a local stash — not intended for this PR. The createContextGraph calls revert to their original form. Made-with: Cursor
| buildImportFileResponse({ | ||
| assertionUri, | ||
| fileHash: fileStoreEntry.hash, | ||
| fileHash: fileStoreEntry.keccak256, |
There was a problem hiding this comment.
🔴 Bug: This changes the existing import-file wire contract from sha256: to keccak256: on fileHash in-place (and the same pattern is applied to mdIntermediateHash / extraction-status). Any current client that validates or persists the old shape will break on upgrade even though the field names did not change. If the hash algorithm must change, keep the old fields stable for a compatibility window or version the response and add parallel keccak fields.
| // ── Atomic multi-graph insert: rows 1-13 + rows 14-20 in one call ── | ||
| // A single `store.insert` across two graphs — either both | ||
| // land or neither does, per the adapter contracts. | ||
| await agent.store.insert([...dataGraphQuads, ...metaQuads]); |
There was a problem hiding this comment.
🟡 Issue: The rollback path now depends on TripleStore.insert() being all-or-nothing across both graphs, but that guarantee is not part of the TripleStore contract. A non-atomic adapter can partially apply this batch, and replaying the old snapshots would then restore the previous state on top of partially-written new quads. Consider codifying atomic multi-graph inserts in the storage interface/tests, or explicitly deleting the newly-written batch before replaying snapshots.
Summary
scripts/devnet-test.sh, covering the biggest V10 e2e gaps identified after merging PRs test: harden V10 test coverage — assertions, publisher queue, memory layers, sub-graph gossip #118 and fix: address review feedback from merged PRs #110-#116 #119viewparam for verified-memory, shared-memory, and working-memory isolationTotal new test assertions: 38 (33 PASS, 5 WARN for pre-existing infra behavior, 0 FAIL on devnet)
Test plan
bash -n scripts/devnet-test.shpasses (syntax valid)Made with Cursor