feat(ka-routes)!: remove /api/assertion — unify on /api/knowledge-assets (→ main)#1041
Closed
branarakic wants to merge 59 commits into
Closed
feat(ka-routes)!: remove /api/assertion — unify on /api/knowledge-assets (→ main)#1041branarakic wants to merge 59 commits into
branarakic wants to merge 59 commits into
Conversation
Bring the design source-of-truth into the working tree so it travels with the code and is linked from PRs (per the v10 build plan). These mirror dkgv10-spec PR #122. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ny entity count (OT-RFC-44)
Removes the entity-count == KA-count conflation that blocked multi-entity
publishes ("V10 publish requires exactly one root entity per request").
- dkg-publisher.ts: all entities of a file become members of ONE KA
(shared tokenId), kaCount sent on chain is 1 (matches the contract's
knowledgeAssetsAmount==1), and the kaCount!==1 guard is removed.
- metadata.ts generateKCMetadata: group entries by KA → emit one
KnowledgeAsset node with N dkg:rootEntity members + summed triple
counts (RFC-43 §7 <UAL/1> shape); per-entity private roots attach to
the single node. Single-entity and distinct-tokenId behavior unchanged.
- storage-ack-handler.ts: the receiver no longer refuses to ACK a
multi-entity KA — replaced rootSubjects.size==kaCount with kaCount==1
+ entity-count bijection. This was the silent cross-node failure in
OT-RFC-43 §2.7.
- node-ui: relax all four single-root publish guards (api.ts,
layer-widgets collectPublishRoots/fetchSwmPublishRoots, MemoryLayerView).
- metadata.test.ts: add a Design B test (N entities, one tokenId -> one
KA node with N members, summed counts).
Build green across core/storage/chain/query/publisher; publisher unit
(96) + metadata (75) + storage-ack (14) tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e cross-node ACK path (OT-RFC-44) The cross-node canary (agent/test/e2e-publish-protocol: a 3-node, 3-entity publish→ACK→finalize) surfaced that the first Design B pass was incomplete: removing the publisher's kaCount!==1 guard was necessary but not sufficient. Three more sites assumed entity-count == KA-count and silently blocked a multi-entity KA from being ACKed by a separate node (OT-RFC-43 §2.7 — the exact failure single-node tests can't catch): - dkg-publisher.ts: remove the second guard (MultiRootPublishNotAtomicError) in the shared-memory publish path. N roots -> ONE KA in ONE tx is atomic, which is what that guard was protecting (V8/V9 mapped N roots -> N txs). - cli/routes/memory.ts: remove the synchronous-publish 409 (MULTI_ROOT_PUBLISH_NOT_ATOMIC) — the daemon now publishes N roots as one KA. - storage-ack-handler.ts (THE deep one): the receiver recomputed verifiedKACount = rootSubjects.size (= N) and signed an ACK digest with kaCount=N, while the publisher and contract both use kaCount=1. So a multi-entity KA's receiver ACKs never validated -> quorum unmet -> publish failed cross-node. Fixed to the Design B invariant kaCount=1 (data integrity is already proven by the SWM merkle-root check). Tests updated to Design B (these encoded the old single-root conflation): - storage-ack-handler.test.ts: 2-entity ACK signs kaCount=1. - dkg-publisher.test.ts / publisher-evm-e2e.test.ts: multi-entity publish now succeeds as ONE KA (inverted from "rejects"). - shared-memory-publish-boundary.test.ts: multi-root selection publishes as one KA (inverted; describe renamed; drop MultiRootPublishNotAtomicError import). - cli/memory-graph-events.test.ts: route publishes multi-root (inverted 409). New canary: agent/test/e2e-publish-protocol.test.ts — "Design B: multi-entity file publishes as one KA, ACKed cross-node" (4 tests, all green). Verified: canary 4/4; publisher metadata+storage-ack+dkg-publisher+boundary 129/129; cli memory-graph-events 23/23; publisher/agent/cli build closures green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…3 §10.5)
A coherent, layer-explicit resource model for a Knowledge Asset — the
working tree is WM, SWM/VM are remote branches, layer is explicit in every
write path. Purely ADDITIVE: the legacy /api/assertion/* + /api/shared-memory/*
routes are untouched (308 redirects from them are a follow-up).
POST /api/knowledge-assets create KA + open WM draft
(atomic: quads auto-write+finalize;
alsoShareSwm / alsoPublishVm tails)
GET /api/knowledge-assets/:name KA lifecycle state
GET /api/knowledge-assets/:name/{wm,swm,vm} per-layer status
POST /api/knowledge-assets/:name/wm/write append quads
POST /api/knowledge-assets/:name/wm/finalize seal (git commit)
POST /api/knowledge-assets/:name/wm/discard drop the draft
POST /api/knowledge-assets/:name/swm/share advance SWM pointer (promote→share)
POST /api/knowledge-assets/:name/vm/publish mint/update on chain
These delegate to the SAME agent lifecycle methods the legacy routes use
(agent.assertion.{create,write,finalize,promote,discard,history},
publishFromFinalizedAssertion), so behavior is identical — only the URL shape
changes. Atomic-create returns 201, or 207 Multi-Status when a create+finalize
succeeds but an opt-in also* tail fails (the sealed assertion is a real,
retryable artifact). Registered in handle-request.ts before the assertion routes.
Identifier note (§10.5.7): addressed by lifecycle NAME + contextGraphId for the
v10.0 floor; minter-namespaced (agent, number) addressing layers on with Option 1
on these same routes.
Verified: cli closure builds clean; new route contract test 10/10 (create,
atomic+autofinalize, 207 partial-failure, write, finalize, share, publish,
status read, pull-from 501, path-ignore).
Follow-ups: wm/pull-from impl (returns 501 now — net-new git-checkout primitive,
§10.5.3), 308 redirects from legacy routes, client-SDK migration, per-layer
status enums.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… §10.5.3) The net-new "git checkout" primitive for the GitHub-shaped KA API. WM is the only writable surface; to edit content already in SWM or VM you pull it into a fresh WM draft, edit, then re-share / re-publish. - publisher: DKGPublisher.assertionPullFrom(cg, name, agent, 'swm'|'vm', opts). Reads the file's member entities from the assertion seal (lifecycle URN, dual-reading dkg:assertionRootEntity|dkg:assertionEntity), gathers the source layer's quads scoped to those entities + their skolemized children (the same filter the publish gather / RS prover use), drops trust/ownership bookkeeping, and seeds a fresh WM draft. onConflict guards a dirty draft: 'reject' (default, throws WM_DRAFT_CONFLICT) or 'replace' (git force-checkout). - agent: agent.assertion.pullFrom facade. - cli route: POST /api/knowledge-assets/:name/wm/pull-from now calls it (replacing the 501). 400 on bad layer, 409 on WM_DRAFT_CONFLICT. Verified: store-backed publisher test (4) — entity-scoped gather excludes co-resident other-file entities + filters workspaceOwner; reject vs replace conflict; throws when unfinalized (no sealed entity list). Route test now 12/12 (pull-from success / bad-layer 400 / conflict 409). cli closure builds clean. Note: pull-from reads the entity set from the last finalize's seal; re-pulling a draft before finalizing it isn't supported (finalize or discard first). Stacked on #971. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t (OT-RFC-43 §10.5) Additive client SDK for the new /api/knowledge-assets surface (#971, #972). Legacy assertion/* + shared-memory/* methods are untouched for back-compat. New layer-explicit methods on ApiClient: createKnowledgeAsset(cg, name, { quads?, authorAgentAddress?, alsoShareSwm?, alsoPublishVm? }) getKnowledgeAsset(cg, name) -> GET /api/knowledge-assets/:name knowledgeAssetWrite / Finalize / Discard / PullFrom -> .../wm/* knowledgeAssetShare -> .../swm/share knowledgeAssetPublish -> .../vm/publish Verified: cli builds clean; 5 fetch-mocked SDK tests assert URL + method + body (atomic create with quads/alsoShareSwm, URL-encoded name on write, share→swm/share, publish→vm/publish, pull-from layer+onConflict, GET with contextGraphId query). Stacked on #972. Migrating the other clients (mcp-dkg, adapters, node-ui) to these methods + 308 redirects from legacy routes are separate follow-ups. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-suite CI) The full sharded publisher suite (Tornado: publisher [1/4] & [4/4]) caught two issues the per-file test runs missed: 1. Over-strict receiver check. The A1 storage-ack change added an entity-count *bijection* (rootSubjects.size === rootEntities.length). But `rootEntities` is a selection, not a complete enumeration — in the SWM-fallback branch it is the entity filter passed to `loadSWMQuads`, so a caller may legitimately declare a subset of the subjects present. The check also tripped on an empty `rootEntities: []` (length 0 is not nullish, so the `?? rootSubjects.size` fallback compared against 0 and pre-empted the Merkle check). Remove the bijection; keep the real invariants — `kaCount === 1` (Design B) and the per-entity "declared ⊆ actual" presence loop. Payload integrity is guaranteed by the flat-KC Merkle root, not by counting subjects. 2. Pre-Design-B test fixtures. Several StorageACKHandler tests still encoded `kaCount = entity count = 2`. Under Design B the receiver signs verifiedKACount=1, so those fixtures asserted a digest the handler no longer produces. Move them to `kaCount: 1` (one KA, N member entities) and the expected-digest arg `2n → 1n`. Collector-side peer simulations updated in lockstep so each quorum test stays internally consistent. Full publisher suite: 72 files, 1117 passed, 6 skipped, 0 failed. tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Correct the KnowledgeAssetsLifecycle + DKGKnowledgeAssets NatSpec/comments that claimed update authority delegates to curators/PCA agents. The live runtime (_executeUpdateCore) is owner-only: the EIP-712-attested author MUST equal ownerOf(kaId). Comment-only; zero bytecode change. Lands before the OT-RFC-43 Option-1 contract audit so the auditor reviews a contract whose docs match its code (Plan B B1.4 / sequencing step 1). Also vendors OT-RFC-43/44/45 into docs/rfcs/ so the design travels with the code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tity (variant 1a) Replace the global ++_knowledgeAssetsCounter with caller-supplied packed ids kaId = (uint160(author) << 96) | uint96(number): - createKnowledgeAsset takes a kaId param; require((kaId >> 96) == uint160(author)) so a wallet can only mint within its own (attested) namespace, then _safeMint(author, kaId). Fail-fast on reuse via _ownerOf AND a slot-empty invariant (merkleRoots.length == 0, audit hardening). - Drop the global counter; retain its storage slot as an unused gap (__deprecated_knowledgeAssetsCounter) so layout is safe under both redeploy and in-place upgrade (R3). Deprecate getLatestKnowledgeAssetId (now reverts). - Thread reservedKaId through PublishParams -> _executePublishCore. reservedKaId is deliberately NOT added to the ACK digest: the on-chain namespace require() is the authority, so a publisher choosing a different number in its own namespace is harmless (avoids R5 storage-node coordination). Namespace binds to the attested AUTHOR (EIP-712 signer / initial owner), a deliberate divergence from OT-RFC-43 §7's msg.sender diagram (the two coincide in the dominant self-publish case). ABI regenerated (+ chain/abi synced). Test fixtures supply a packed reservedKaId; getLatestKnowledgeAssetId assertions removed; 5 new B5 cases (packed mint, namespace-mismatch revert, dup-reservation revert, deprecated getter, owner-only-after-transfer). 16 evm-module tests green. NOTE: requires the off-chain reservedKaId supply (allocator -> publish orchestration -> chain adapter) to function; that T0 integration is the immediate follow-up (Bucket 2). Until it lands, real-adapter publishes revert KaIdNamespaceMismatch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…B2 core)
Off-chain allocator that mints deterministic packed kaIds:
- KaNumberStore interface (core) + SqliteKaNumberStore on the shared dashDb
(node-ui), schema v19 -> v20 adding ka_numbers(author_address PK, next_number).
allocate() is a single atomic INSERT ... ON CONFLICT DO UPDATE ... RETURNING;
reconcileFloor() raises the floor (never lowers); durable state, not pruned.
- KaNumberAllocator (agent): allocate(author) -> { kaId, number } packing
kaId = (uint160(author) << 96) | number entirely in bigint (no Number()), a
reconciled-before-allocate cold-start guard (RFC-43 §4.5), and a static unpack().
- chain-event-poller: a KnowledgeAssetCreated listener decoding
number = kaId & ((1<<96)-1) for startup reconciliation.
- Construct store + allocator in the daemon lifecycle. The publish-time
allocate() call site (T0 stamping) is the deferred Bucket-2 integration.
16 allocator unit tests; node-ui schema-v20 migration tests; node-ui 96 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-RFC-43 §9, B4) Packed kaIds (and uint64 wire ids) exceed 2^53, so Number()/protoToNumber silently corrupts them. Route the publish gossip path through full-precision bigint: - publish.ts: add bigIntToProtoSafe (low/high split + MAX_UINT64 guard, mirroring finalization.ts) and apply it to tokenId/startKAId/endKAId in encodePublishRequest; widen those proto fields to number|bigint|Long. - dkg-agent.ts broadcastPublish: pass raw bigints instead of Number(...). - gossip-publish-handler.ts: decode tokenId via protoToBigInt (not protoToNumber). - publish-handler.ts: widen the local protoToBigInt to accept bigint (pass-through) so the widened proto fields type-check. blockNumber stays number|Long (a chain block number, never bigint). Full turbo build green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mcp-dkg, openclaw (OT-RFC-43 §10.5, A5) #973 added the new /api/knowledge-assets surface to the cli ApiClient only — so the clean KA API was unreachable from the UI, the MCP server, and the OpenCLAW adapter. This completes Plan A5 across the remaining first-party clients, mirroring the cli reference verbatim (same routes, bodies, layer verbs): createKnowledgeAsset / getKnowledgeAsset knowledgeAssetWrite / Finalize / Discard / PullFrom (WM) knowledgeAssetShare (SWM) knowledgeAssetPublish (VM) - node-ui (packages/node-ui/src/ui/api.ts): positional-arg functional exports over the existing post()/get() helpers, matching the file's style. - mcp-dkg (packages/mcp-dkg/src/client.ts): args-object methods over this.request(), normalizing the context-graph id like the sibling methods. - adapter-openclaw (packages/adapter-openclaw/src/dkg-client.ts): positional methods over this.post()/this.get(); + fetch-mocked contract tests pinning the route/method/body for create, write (URL-encoded name), pull-from, share and publish. adapter-hermes is intentionally left out — it is a specialised hermes-channel adapter (connect/send/persist-turn), not a general KA-publishing client. Routes are identical to the cli surface (already contract-tested in api-client.test.ts) and validated again end-to-end on the integration devnet. typecheck clean across all three packages; openclaw suite 70/70. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…T-RFC-43 §10.4) The function never partitioned into Knowledge Assets — it skolemizes blank nodes under their parent entity and indexes the result by entity (Map<entity, Quad[]>). The "autoPartition" name is the misnomer that led readers (and patches) to treat the entity index as a list of KAs and conclude "kill autoPartition", re-entrenching the entity-count == KA-count bug Design B removes. No behavior change. - auto-partition.ts: rename the export to skolemizeByEntity; fix the misleading JSDoc (it indexes by entity; the skolemization is consensus load-bearing, the grouping is just an index). Keep `autoPartition` as a @deprecated alias for one release so external test/script consumers migrate. - index.ts: export both names. - Migrate ~29 internal call sites (publisher, agent, cli, core) to the new name. Tests keep working via the alias. Verified: agent build closure green; publisher tests (dkg-publisher, metadata, boundary, pure-functions, canonical-publish-payload, publish-lifecycle, ka-update) all pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The node-ui, mcp-dkg and openclaw knowledge-assets wrappers only accepted
`subGraphName`, so callers could not reach daemon-supported controls that the
cli ApiClient already exposes. This dropped VM-publish behaviour and blocked
external-signer / pre-signed-attestation flows through the new clean KA surface.
Mirror the cli reference verbatim in all three clients:
- KnowledgeAssetFinalizedPublishOptions ({ clearAfter, publishEpochs,
publisherNodeIdentityIdOverride }) + finalizedPublishOptionsPayload helper,
serialized under the `options` key on `.../vm/publish` (clearAfter maps to
the daemon's `clearSharedMemoryAfter`; bigint override -> decimal string).
- knowledgeAssetFinalize forwards `authorAgentAddress`,
`preSignedAuthorAttestation` and `schemeVersion`.
- createKnowledgeAsset accepts the same finalize fields and an
`alsoPublishVm` options object (translated to the daemon body shape).
mcp-dkg/node-ui widen `publisherNodeIdentityIdOverride` to bigint|string|number
since JSON callers cannot send a bigint; the wire payload is still a decimal
string identical to the cli.
node-ui carried the same gap the bot flagged on openclaw/mcp-dkg; fixed here too
so the three first-party clients stay byte-for-byte aligned with the cli surface.
Tests: openclaw dkg-client 74/74 (4 new KA contract cases); new mcp-dkg
knowledge-assets-client contract test 5/5; tsc clean across all three packages.
Co-authored-by: Cursor <cursoragent@cursor.com>
…RFC-43 §10.1) NOT COMPLETE — safe foundation only; do not open a PR from this yet. - core/entity-predicate.ts (new): the migration toolkit — DKG_ENTITY / DKG_ROOT_ENTITY_LEGACY (+ assertion variants), ENTITY_PRED_ALT SPARQL alternation, isEntityPredicate(). Documents the de-dup hazard (dual-write means alternation reads double-count -> require SELECT DISTINCT / COUNT(DISTINCT)). - metadata.ts: dual-write the KA/share entity-member predicate (6 emit/delete sites) under BOTH dkg:rootEntity and dkg:entity via entityMemberQuads(). Verified: core+publisher build clean; publisher metadata/storage-ack/ dkg-publisher tests 125/125 (legacy readers unchanged, still resolve). REMAINING (focused follow-up, consensus-sensitive): - Seal dual-write (assertion-seal.ts assertionRootEntity->assertionEntity) — needs verification that adding _meta quads doesn't affect any seal-integrity check (merkle is over data, signature over the EIP-712 struct, so expected safe — but confirm with the RS-proof test). - finalization-handler emitter dual-write. - Dual-READ ~20 consumers (SPARQL alternation + DISTINCT/COUNT(DISTINCT); code-equality -> isEntityPredicate). The RS-proof reader (ka-extractor) is the critical one — must dedup or leaf reconstruction doubles. - Tests: dual-read-window (legacy-only data read by new code & vice versa) + RS proof on a multi-entity KA with dual-written data. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ters (OT-RFC-43 §10.1) Finishes RFC §10.1 step 1 (dual-write) on top of the #970 foundation: every emitter of the entity-list predicate now writes BOTH the legacy and the new honest name. Readers are deliberately left on the legacy predicate (which is always present during dual-write) — the reader dual-read + legacy-drop are the RFC's explicit *later-release* steps (§10.1 steps 2-4) and carry the only consensus risk (the random-sampling proof path), so they are not rushed here. dkg:rootEntity + dkg:entity (KA/share member list) dkg:assertionRootEntity + dkg:assertionEntity (assertion seal) - core/assertion-seal.ts: add ASSERTION_ENTITY; buildAssertionSealQuads dual-writes both per entity. Adding this _meta quad does NOT affect seal integrity (merkle is over data; the EIP-712 attestation signs a fixed struct, not the entity-list quads). parseAssertionSealQuads still reads the legacy name (always present). - agent/finalization-handler.ts: the op entity-list cleanup deletes BOTH names. - (metadata.ts emitters were dual-written in the #970 foundation commit.) Verified: agent build closure green; core seal test (9, incl. new dual-write assertion) and publisher metadata test (75, incl. dual-write assertion) pass; dkg-publisher (incl. Design B) unchanged. NEXT RELEASE (not in this PR, by design): switch the ~28 reader sites to the dual-read alternation (core's ENTITY_PRED_ALT) with SELECT DISTINCT / COUNT(DISTINCT) — especially ka-extractor (RS proof) — gated by the dual-read- window + RS-proof-on-multi-entity tests; then drop the legacy write. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… deferred seam) Makes OT-RFC-43 Option-1 deterministic identity work end-to-end (single-node mint verified on hardhat; multi-node gossip round-trips). Supply chain (allocator → publish → adapter → mint): - chain: V10PublishParams.reservedKaId + ChainAdapter.getMaxKaNumberForAuthor; the real EVM adapter encodes reservedKaId (fail-loud on missing/wrong-namespace before gas) and enumerates KnowledgeAssetCreated-by-author for reconciliation; mock honors reservedKaId + tracks per-author max. - publisher: a structural KaIdAllocator option (no agent→publisher cycle); ensureReservedKaId lazily reconciles each author's floor against the chain (max(local, chainMax)+1 → satisfies the RFC §4.5 cold-start guard) then allocates; allocate-at-publish threads reservedKaId into the mint and asserts the on-chain id == the reserved id (no UAL/chain split). - agent/cli: kaNumberAllocator flows DKGAgentConfig → DKGPublisher; the daemon lifecycle now wires it (the void placeholder is gone). 256-bit id wire (OT-RFC-43 §9): a packed kaId = (uint160(author)<<96)|number is ~256-bit and overflows uint64, so the gossip protos now carry KA ids (tokenId/batchId/startKAId/endKAId) as DECIMAL STRINGS, not uint64 — across publish/finalization/ka-update/verify + their encode (idToProtoString) and decode (protoToBigInt handles string) sites. blockNumber/timestampMs/txIndex stay uint64. This supersedes B4's uint64 widening for the id fields with the real 256-bit fix. Tests: reservedKaId wired across ~38 real-adapter test files via a shared in-memory allocator helper; 3 new chain Option-1 tests (deterministic-mint, replay-revert KaIdAlreadyMinted, namespace-guard); gossip/finalization/lifecycle round-trip green. Full build green (15 turbo tasks). Deferred (needs Plan A #971 routes): A2 per-layer pointers + API status enums + VM-pointer create-vs-update + (agent,number) addressing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…' into integration/v10-devnet
# Conflicts: # docs/rfcs/OT-RFC-43-deterministic-ka-identity.md # packages/agent/src/gossip-publish-handler.ts
…A store (R3 seam) + cover it (#985) * feat(evm): route submitProof verification through the per-challenge KA store (R3 seam) + cover it `RandomSampling.submitProof` read the challenged KA's merkle data from the contract's bound `knowledgeAssetStorage` singleton, while the challenge record already carries `knowledgeAssetStorageContract` — the store the challenge was issued against — which was written but NEVER read. This routes the four verification reads through `DKGKnowledgeAssets(challenge.knowledgeAssetStorageContract)` instead. Today there is exactly one KA store, so `challenge.knowledgeAssetStorageContract == address(knowledgeAssetStorage)` and the change is strictly behaviour-neutral. It (1) keeps the recorded field LIVE so an audit/cleanup pass won't prune it as dead code — re-adding it later would mean a storage-layout change to the reward-critical RandomSamplingStorage; (2) makes verification immune to a mid-period Hub re-point of the singleton; and (3) is the seam a future multi-generation RandomSampling needs under the no-proxy "versioned storage coexistence" recovery model (R3 / OT-RFC-43): a challenge issued against generation-N's store MUST verify against that store. The neighbouring `kaToContextGraph` CG-store read stays on the singleton — cross-generation CG resolution needs the cutover-time generation registry and is out of scope here. Adds the first executing coverage for the V10 submitProof success path (the integration suite's Proof-Submission block is behind `describe.skip(... OBSOLETE: V8 stake pipeline)`), using the V10-clean direct-storage setup (createProfile + ShardingTable.insertNode; no stake — proof verification is stake-independent): - Test A: seed KA + public CG + node, create challenge, submitProof succeeds (solved=true). - Test B (the seam): after the challenge is issued (recording store-A), re-point the Hub's DKGKnowledgeAssets to an empty store-B and re-initialize RandomSampling, then assert the proof still verifies. Verified RED on the pre-edit code (MerkleRootMismatchError — reads empty store-B) and GREEN with the seam, so it is a true regression test for the field. Behaviour-neutrality independently confirmed by adversarial review. 110 passing across the new tests + the RandomSampling unit/curated/storage suites; no regressions. (Tests use a single-leaf tree so submitProof(root, []) verifies trivially, side-stepping a kcTools sorted-pair vs on-chain positional-pair merkle mismatch that is orthogonal to the store-routing under test.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(evm): pin curation classification per-challenge (R3 seam, curated KAs) Review on #985 flagged that submitProof rerouted only the KA-store reads through the per-challenge `knowledgeAssetStorageContract`, while `isCurated` was still re-derived live from the `ContextGraphStorage` singleton (`kaToContextGraph` + `getIsCurated`). A ContextGraphStorage generation cutover (or a Hub re-point to a store where the KA's CG binding differs or is absent) would reclassify an older curated challenge as public mid-period and verify it against the wrong (merkle vs ciphertext) root/count pair. Fix symmetrically with the KA-store pin: record `isCurated` on the Challenge at issuance in `_generateChallenge` (from the eligible cgId the picker returned) and read `challenge.isCurated` back in submitProof. submitProof now resolves BOTH the store and the curation branch from the challenge itself and no longer consults any live singleton for branch/root selection. The new bool packs into the existing `solved` storage slot. Adds Test C to the submitProof seam suite: a curated challenge survives a ContextGraphStorage cutover (re-point to an empty store + re-init) and still verifies against the ciphertext root — RED on the pre-fix code (re-derived public branch reads the decoy merkle root → MerkleRootMismatchError). Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Branimir Rakic <aleatoric@Branimirs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…<ual> KnowledgeAsset node (#989) Re-authors the two genuine read/metadata gaps from PR #968 onto integration/v10-devnet, dropping the 8 commits that would REGRESS #980 (the N-root→1-KA publish-boundary guards, the startKAId+tokenIdx RANGE tokenId / metadataTokenId compatibility-row scheme, and the uint64 gossip wire encoding — all deliberately superseded by #980's Design-B + string-id work). Gap 1 — multi-entity resolveKA (query): resolveKA read only `bindings[0].rootEntity` and filtered the data query to one root, so a Design-B KA with multiple entities returned only the first entity's triples. Now collects every `?ka <rootEntity>` binding (dedup, ORDER BY ?ka), filters the data graph across ALL member roots, and returns `rootEntities[]` (`rootEntity` retained = first member, backward-compat). QueryEngine interface updated. Gap 2 — aggregate <ual> KnowledgeAsset node (metadata): generateKCMetadata typed the bare <ual> only as dkg:KnowledgeCollection; the per-row <ual>/N subjects were the only dkg:KnowledgeAsset nodes. Now also emits an aggregate node on the bare UAL (dkg:KnowledgeAsset + summed publicTripleCount + every member tokenId + every member entity, dual-written dkg:rootEntity/dkg:entity per §10.1). Uses ka.tokenId (NOT the dropped metadataTokenId). Deliberately OMITS the original `<ual> partOf <ual>` self-edge — it would make the bare node match `?x partOf <ual>` member enumeration (incl. resolveKA) and double-count members; the aggregate node is purely self-describing. Verified: query 260/260; publisher 1143 passed / 0 failed (incl. the two existing Design-B metadata tests updated for the aggregate node). agent + cli show ONLY failures confirmed PRE-EXISTING on the clean #980 tip (agent: e2e-security, finalization-handler; cli: auto-update-versioned-e2e — an environmental `git tag` failure), all unrelated to this change. Co-authored-by: Branimir Rakic <aleatoric@Branimirs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fixes (PR #976 F5–F9) onto #980 (#987) Re-authors the allocator review-fix commits from PR #976 (991a5a2, 45f665c) onto integration/v10-devnet, WITHOUT the stacked #975 contract commit (11b480a) — that commit reverses the deliberate R5 decision (reservedKaId out of the 4-field AuthorAttestation typehash + mid-struct layout) and is intentionally NOT included here. These are consensus-adjacent: a missed historical KCCreated leaves the per-author allocator floor at 0, so the allocator re-issues an already-minted number → UAL collision. - F5: remove the dead `event.type === 'KnowledgeAssetCreated'` poller branch (the adapter only ever yields `'KCCreated'`) and fan reconcile out off the actually-yielded KCCreated event (handleBatchCreated + handleKACreated). - F6: number→bigint end-to-end (core KaNumberStore, agent allocator, node-ui SqliteKaNumberStore + .safeIntegers(true)) — the per-author number can exceed 2^53; Number() would silently lose precision and re-issue a minted id. - F7/F8: allocator zero-address rejection + canonical-author key. - F9: cold-start guard observes full KCCreated history when watching KA-created so markReconciled() stays sound. - New test: chain-event-poller-ka-created.test.ts. Reconciliation onto #980 (not in the PR — #980-specific consumers the PR's base didn't have): - dkg-publisher.ts: KaIdAllocator interface → bigint; reconcile(author, chainMax) passes the bigint straight through (drops Number()). - dkg-agent-publish.ts: same Number(chainMax) → chainMax drop. - test mocks (publisher + agent test/_helpers/ka-allocator.ts): bigint, replacing Math.max (throws "cannot mix BigInt") with a bigint comparison. Verified: publisher full suite 1147 passed / 0 failed; agent 1214 passed — the 2 remaining failures (e2e-security, finalization-handler) are confirmed PRE-EXISTING on #980 (identical on the clean tip) and unrelated to this change. Targeted allocator(23) / db(78) / chain-event-poller-ka-created(4) all green. Co-authored-by: Branimir Rakic <aleatoric@Branimirs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tes source before dropping the draft (#992) assertionPullFrom had a latent bug: it read the file's member entities from `assertionLifecycleUri` (urn:dkg:assertion:...), but finalize stamps the seal under `contextGraphAssertionUri` (the assertion-graph URI / wmGraph) — a DIFFERENT subject. So the entity lookup found nothing and pull-from threw "No sealed entity list" for EVERY properly-finalized assertion. The existing tests masked it by seeding entities under the lifecycle URN (matching the buggy read). - 335e8d8: read the seal from the assertion-graph URI via `parseAssertionSealQuads` (the same subject finalize writes); require a real finalized seal. - 335e8d8: VALIDATE the source has content (`PULL_FROM_EMPTY_SOURCE`) BEFORE dropping the WM draft — a drop-before-validate could destroy an open draft when the source turned out empty. - a6740ac: a sub-scoped VM pull reads the SUBGRAPH's data graph (`subGraphUri`), not the root data graph; `ensureSubGraphRegistered` instead of a pure name check. Tests: rewrote `sealEntities` to build a REAL seal under the assertion-graph URI via `buildAssertionSealQuads` (as finalize does) — the prior helper wrote under the lifecycle URN, validating the broken read. Added a validate-before-drop test (empty-source replace pull preserves the dirty draft). Verified: assertion-pull-from 5/5; publisher full suite 1144 passed / 0 failed. agent + cli show only failures confirmed PRE-EXISTING/flaky on the clean #980 tip (e2e-security, finalization-handler; publish-jsonld passes 32/32 isolated — a chain-backed full-suite flake; cli auto-update-versioned-e2e env `git tag`). Follow-up: #972's reopen-event refinements (246012a/16006dc — PullFromPreconditionError 409 + suppress the spurious create event on reopen) are smaller observability deltas, deferred. Co-authored-by: Branimir Rakic <aleatoric@Branimirs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ter + VM marker flip (#986) * fix(node-ui): promote file-imported WM assertions (recognize the lifecycle-URN memoryLayer marker) The WM "Promote" button no-op'd ("0 triples promoted") for file-imported assertions in any context graph, even though their content was fully promotable. Root cause: a marker-form mismatch. Every create path stamps the canonical `dkg:memoryLayer "WM"` lifecycle marker on the lifecycle URN (`urn:dkg:assertion:<cg>:<agent>:<name>`, `assertionLifecycleUri`). Regular create ALSO stamps a data-graph-URI marker; a FILE IMPORT stamps ONLY the URN form. The UI's `listAssertions('wm')` parser accepted ONLY the data-graph-URI form (`startsWith(cgPrefix)` + `/`-split), so it silently dropped every file-imported WM assertion → the bulk-promote loop iterated zero times. Fix #1 (packages/node-ui/src/ui/api.ts): the WM `listAssertions` parser accepts BOTH marker forms (the canonical lifecycle URN + the data-graph URI), deduping by (subGraph, name). Names may contain ':', so the 0x agent address is peeled deterministically. Fix #2 (packages/publisher/src/dkg-publisher.ts): `assertAssertionDataPersisted` also reads `memoryLayer` via the `dkg:assertionGraph` back-link, so the "already promoted (SWM/VM) -> harmless no-op" guard recognizes file-imported assertions (URN-only marker) on a re-promote instead of misfiring `AssertionNotPersistedError`. Verified: reproduced the drop on a real file import (old parser -> [], new -> [name]); 3 new regression tests in ui-api-pure.test.ts; node-ui ui-api-pure 38/38, publisher draft-lifecycle 45/45, full build 20/20. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(node-ui): seamless WM→SWM→VM publish — auto-register on-chain + finalize-before-share + per-assertion VM publish "Publish to Verifiable Memory" failed for multi-entity content ("Shared-memory publish currently requires exactly one root entity"), and even single-root content couldn't publish from a UI-created project. Three root causes: 1. VM = the on-chain layer, but the UI creates projects OFF-CHAIN by default (CreateProjectModal registerOnChain=false) → finalize/publish errored "context graph is not registered on-chain". 2. The Publish button used the legacy single-root shared-memory publish (collectPublishRoots throws on >1 root) instead of the Design-B per-assertion vm/publish (one KA per file, any entity count). 3. vm/publish requires a FINALIZED assertion, but Promote shared without finalizing and moved content out of WM — after which finalize is impossible. Fixes (packages/node-ui): - api.ts: add registerContextGraph + ensureContextGraphOnChain (fetch status → register if off-chain). The UI now auto-registers a CG on-chain transparently before any operation that needs it. - layer-widgets.tsx LayerActionsWidget: - Promote (WM→SWM): ensureContextGraphOnChain → finalize each draft → promote. Finalizing here (before content leaves WM) is what makes the shared assertions publishable to VM later. - Publish (SWM→VM): ensureContextGraphOnChain → per-assertion vm/publish (Design B, one KA per file, any entity count) — replaces the single-root publishSharedMemory. Verified end-to-end on devnet (fresh off-chain CG): create → import → Promote (auto-registers onChainId, finalizes, shares) → Publish → status "confirmed", one KA, multi-entity. + regression test for ensureContextGraphOnChain. NOTE for review (A2/B3 / Design-B lifecycle owner): - Promote now registers the CG on-chain + finalizes (spends gas) — a behavior change; a production build may want a cost/confirm prompt. - The same single-root pattern remains in MemoryLayerView + entities.tsx (other publish surfaces) and should get the same treatment. - Assertions promoted WITHOUT a prior finalize (before this fix, or on an off-chain CG) are unrecoverable (finalize needs WM content) — re-import. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(node-ui): list SWM assertions from the memoryLayer marker, not async ShareTransition records "Publish to Verifiable Memory" reported "Nothing to publish — promote assertions to Shared Memory first" immediately after a successful promote, even though the SWM layer clearly held entities. listAssertions('swm') enumerated `dkg:ShareTransition` records in `<cg>/_shared_memory_meta`. Those are written by the ASYNC SWM share (sender-key setup + gossip) and therefore LAG the promote, so a publish fired right after promoting found zero shared assertions. (They also include the reserved `meta` system assertion, e.g. project-ontology.) Fix: enumerate SWM from the canonical `dkg:memoryLayer "SWM"` lifecycle marker in `<cg>/_meta` — written synchronously by promote, the same source the WM listing already uses — with the identical URN/data-URI-tolerant parser, dedupe, and `meta` sub-graph exclusion. Verified on the devnet: a freshly promoted multi-entity assertion is now found and publishes to VM (status "confirmed", one KA). + regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(node-ui): apply the publish lifecycle to the Assertions tab too + show the UAL per row The Assertions tab (AssertionsList) had its OWN promote/publish handlers that still used the legacy single-root path, so "Publish to VM" on a row threw "Publish requires one root entity" (and, worse, tried to publish EVERY SWM root, not the clicked one). Only the layer-summary widget was fixed previously. - entities.tsx: both the per-assertion (handlePromote) and bulk (handlePromoteAll) actions now use the correct lifecycle — Promote: ensureContextGraphOnChain -> finalize -> promote; Publish: ensureContextGraphOnChain -> per-assertion vm/publish (Design B, one KA per file, any entity count). - api.ts: fetchAssertionUals(cg) -> { name: reservedUal }; the assertions list renders the deterministic Option-1 UAL (did:dkg:evm:<chain>/<author>/<n>) in gray under each filename. - layer-widgets.tsx: removed the now-dead collectPublishRoots / fetchSwmPublishRoots single-root helpers. Verified: per-assertion vm/publish confirmed on-chain; typecheck clean; ui-api-pure 41/41 (incl. fetchAssertionUals). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(node-ui): drop already-published assertions from the SWM Assertions list A published assertion lingered in the Shared-Memory list. Root cause is a backend lifecycle gap: vm/publish records a dkg:vmCurrentAssertion pointer (+ dkg:kaId) but does NOT flip dkg:memoryLayer off "SWM" / state off "promoted". generateAssertionPublishedMetadata (which would flip them) is wired into publishFromSharedMemory but its trigger SPARQL gate (dkg-publisher.ts:1523) joins on dkg:rootEntity/dkg:agent predicates the lifecycle URN never carries (it has prov:wasAttributedTo), so the flip never fires. The reliable "is-published" signal is the presence of dkg:vmCurrentAssertion. listAssertions('swm') now excludes any assertion carrying a vmCurrentAssertion pointer (keyed by (subGraph,name) so both marker forms drop), so published assertions leave the Shared-Memory list as expected. Verified live (dadaa): published BRANCH_SUMMARY/SPEC/TWO-LAPTOP dropped, un-published MIGRATE_TO_NPM kept. + regression test. ui-api-pure 42/42. NOTE (A2/B3 lifecycle owner): the proper fix is backend — make vm/publish flip memoryLayer SWM->VM + state->published (fix the generateAssertionPublishedMetadata trigger gate at dkg-publisher.ts:1523). This UI filter is the safe interim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(agent): publish flips the lifecycle marker to VM (proper root fix) publishFromFinalizedAssertion recorded the dkg:vmCurrentAssertion pointer + kaId on a confirmed VM publish but did NOT flip dkg:memoryLayer off "SWM" / dkg:state off "promoted" — the dedicated published-metadata flip (generateAssertionPublishedMetadata) never fired because its trigger SPARQL gate (dkg-publisher.ts:1523) joins on dkg:rootEntity/dkg:agent predicates the lifecycle record never carries (it has prov:wasAttributedTo). So a published assertion kept reading SWM/promoted and lingered in the Shared-Memory layer. Now, on a confirmed/tentative publish, flip dkg:memoryLayer -> "VM" and dkg:state -> "published" on BOTH the lifecycle-URN and data-graph-URI forms (promote stamps "SWM" on both), right where the vmCurrentAssertion pointer is stamped. The VM lifecycle record is now equivalent to the WM/SWM records (a memoryLayer marker + state) with the extra transaction metadata (vmCurrentAssertion + kaId + on-chain UAL) layered on top. Verified live (node1 reloaded): create -> finalize -> promote -> vm/publish -> the assertion's URN AND data-URI both read memoryLayer="VM", state="published", + vmCurrentAssertion. Full build 20/20. The node-ui SWM-list exclusion (by vmCurrentAssertion) remains as a backstop for assertions published BEFORE this fix (their marker stays "SWM"). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Branimir Rakic <aleatoric@Branimirs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pi/knowledge-assets route (#990) PR #971's /api/knowledge-assets HTTP surface is largely already consolidated on integration/v10-devnet (the route, B3 addressing via classifyKaIdentifier/resolveByKaId, the implemented pull-from, MAX_PUBLISH_EPOCHS in sibling routes, validateAssertionName, hasPendingSharedMemoryWrites are all present). The genuine, confirmed remaining gap this lands: the `vm/publish` verb passed `parsed.options` straight into publishFromFinalizedAssertion with NO validation, so malformed epochs / identity overrides / flags surfaced as opaque 500s deep in the publisher, and nonsensical fields (assertionName, author overrides, partial selection) were silently ignored. - validateFinalizedAssertionPublishRequest: 400s on assertionName / author overrides / non-"all" selection (the URL name + seal already select the assertion and encode the author). - resolveFinalizedPublishOptions: validates + normalizes publishEpochs|epochs (positive, safe-integer, <= uint32 MAX_PUBLISH_EPOCHS), publisherNodeIdentityIdOverride (non-negative integer), clearAfter/clearSharedMemoryAfter (boolean); returns the normalized options or null after writing a 400. - Wired into the explicit vm/publish verb ONLY — a standalone request where a 400 mutates nothing. Deliberately NOT wired into the alsoPublishVm atomic tail: it runs after create+finalize succeed, so a 400 there would abort partial success; its bad options correctly surface as a 207 phase error instead. Re-authored onto #980 (not cherry-picked) — the PR's commits conflict (they revert skolemizeByEntity->autoPartition and drop kaAllocator/createV10UpdateACKProvider on #980). Dropped the superseded owner-model (dcd6a69 — #980 uses classifyKaIdentifier/resolveByKaId) and the 501-pull-from (7bcdd4a — #980 implements pull-from). Verified: knowledge-assets-route 26/26 (5 new vm/publish validation cases); cli full suite 1924 passed, only auto-update-versioned-e2e fails — confirmed PRE-EXISTING on the clean #980 tip (environmental `git tag` failure), unrelated. Follow-up (separate, assessed next): the #971 seal-field response enrichment (88f628c) and operation-tracker wiring (F22) are additional observability deltas, tracked separately. Co-authored-by: Branimir Rakic <aleatoric@Branimirs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e (200/207/502) (#991) PR #972's cbb13fb ("gate KA publish side effects") flagged that #980's vm/publish returned 200 for ANY publish result. On this SYNCHRONOUS route a non-confirmed publish is not a normal in-flight state ("no silent tentative downgrade"). The publish result is a 3-state machine (status: 'tentative'|'confirmed'|'failed') plus an orthogonal contextGraphError. Mapping (owner decision): confirmed, no contextGraphError → 200 (fully done) confirmed + contextGraphError → 207 (partial: KA minted on-chain, context-graph binding failed) tentative | failed → 502 (publish did not confirm) - Explicit vm/publish verb: returns the mapped status; body carries kaId/ual/ txHash + contextGraphError + a reason on 207/502. - Atomic create `alsoPublishVm` tail: only a fully-confirmed publish is "vm-confirmed"; a partial/non-confirmed outcome is pushed to the tail errors so the atomic response is the existing 207 rather than a misleading success. (#980 has no `emitPublished` in this route, so cbb13fb's event-gating is moot here — only the HTTP-status contract changes.) Verified: knowledge-assets-route 24/24 (3 new 207/502 cases + existing); cli full suite 1922 passed, only auto-update-versioned-e2e fails — confirmed PRE-EXISTING on the clean #980 tip (environmental `git tag`), unrelated. NOTE: overlaps PR #990 (#971 vm/publish input validation) on the same handler — merge #990 first, then this rebases trivially (validation is at the top of the block, this is the return). The #972 pull-from publisher-internals chain (335e8d8 subject-mismatch + validate-before-drop, reopen 409s, VM subGraph routing) is the remaining #972 work, tracked separately. Co-authored-by: Branimir Rakic <aleatoric@Branimirs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…onciliation onto #980) (#984) * fix: validate KA client payloads * fix: reject invalid KA create payloads * fix: harden KA client option payloads * fix: align KA client validation * fix: reject unknown KA publish options * fix: validate MCP KA publish options * docs: narrow entity predicate migration contract * fix(cli): enforce mutually-exclusive author fields in knowledgeAssetFinalize Review on #984 flagged that the finalized-publish validation drifted across the CLI / MCP / OpenClaw / node-ui SDK clients: knowledgeAssetFinalize gained the self-sign-vs-external-signer mutual-exclusion precheck on every surface EXCEPT the CLI ApiClient, which forwarded a conflicting {authorAgentAddress + preSignedAuthorAttestation} body to the daemon instead of rejecting it client-side like the others. Reconcile the drift by calling assertExclusiveAuthorFields in the CLI's knowledgeAssetFinalize too, so all four clients enforce the same contract. Adds a regression test asserting the conflict throws before any HTTP call. (The broader DRY suggestion — hoisting these helpers into a shared package — is a deliberate cross-package refactor better done on its own, not folded into this land PR.) Co-authored-by: Cursor <cursoragent@cursor.com> * chore: revert accidentally-committed localhost deployment artifact The previous commit's `git add -A` swept in a hardhat localhost deployment JSON regenerated by a local turbo build. It is unrelated to the SDK fix — restore it to its prior state. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Branimir Rakic <aleatoric@Branimirs-MacBook-Pro.local> Co-authored-by: Cursor <cursoragent@cursor.com>
…utes (#988) * feat(agent-tools): migrate assertion lifecycle tools to the v10 KA routes The v10 KA memory refactor (PR #980) added the /api/knowledge-assets/* lifecycle and KA client methods, but left every agent-facing tool on the legacy /api/assertion/* + /api/shared-memory/* routes. This repoints the assertion lifecycle tools onto the new KA client methods in both agent surfaces, keeping tool names unchanged so existing agent prompts/skills keep working. Mapping (same engine calls underneath; per-name, like the KA routes): create -> createKnowledgeAsset (POST /api/knowledge-assets) write -> knowledgeAssetWrite (.../wm/write) promote -> knowledgeAssetShare (.../swm/share) discard -> knowledgeAssetDiscard (.../wm/discard) history -> getKnowledgeAsset (GET /api/knowledge-assets/:name) Left as-is (no KA equivalent yet, like the import/enrichment aux tools): dkg_assertion_query -> queryAssertion (the KA GET returns lifecycle state, not a quad dump, so the quad-dump tool stays on /api/assertion) Notes: - create preserves the legacy idempotent contract by catching the KA route's "already exists" error. - openclaw write keeps the publisher's N-Triples literal escaping (normalizeDkgPublisherQuads) before handing off to the daemon. - history drops the per-author agentAddress filter: the KA GET surface is keyed by (contextGraph, name). - knowledgeAssetWrite's quad `graph` relaxed to optional (the WM-write engine derives it; the legacy triples path relied on exactly this). - publish tools (context-graph-scoped) intentionally untouched — a model shift, not a rename — to be handled separately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(agent-tools): align create idempotency with verified KA route behavior Live-tested the migrated tools against a devnet node (integration branch, commit 3861487). The KA create route is idempotent AND non-destructive: re-creating an existing name returns the open WM draft (HTTP 201, status: draft-open) without clearing its quads — it does NOT error with "already exists" the way the legacy /api/assertion/create route did. So the "already exists" catch in both create handlers was dead code. Removed it; the create tool now just reports "Created" (idempotent, safe to call repeatedly). Updated the create tool description, the FakeClient harness (non-throwing get-or-create), and the idempotency test to assert non-destructive re-create instead of an "already exists" message. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(agent-tools): address PR review — KA route parity + keep author-scoped history Resolves the codex review findings on #988. Claim A/B (error semantics + name validation): the KA daemon routes had only a blanket 500 catch and no name validation, so user mistakes (bad name, missing assertion, unsafe/reserved IRI) surfaced as 500s instead of the 400s the legacy /api/assertion/* routes return. Brought knowledge-assets.ts to parity: validate the name in the create handler, and route engine errors through a shared mapper that mirrors assertion.ts (400 for not-found/Invalid/ Unsafe/reserved, 409 for AssertionNotPersistedError, 500 only for the genuinely unexpected). Added route-level tests covering each mapping. Claim C (history author scoping): the KA GET surface is keyed by (contextGraph, name) only and defaults to the local agent, so it cannot fetch another author's history. Reverted dkg_assertion_history (both mcp-dkg and openclaw) back to the legacy getAssertionHistory and restored the agentAddress parameter. Reads now stay on the legacy routes (history + query) until the KA read surface reaches parity; only the write-side lifecycle migrates to KA. Create contract: the openclaw create tool documented a duplicate returning `{ assertionUri: null, alreadyExists: true }`, which the idempotent non-destructive KA route never produces. Updated the tool description to the new contract (`{ name, assertionUri, status }`) so the documented behavior matches reality. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(agent-tools): address 2nd review pass — keep create on legacy, scope KA error mapping Resolves the follow-up codex findings on #988. 1. Create contract + name validation: the KA create route is an idempotent get-or-create that cannot report `alreadyExists` (the engine's assertionCreate doesn't surface pre-existence) and lacks the legacy name validation. Rather than half-preserve a broken contract, `dkg_assertion_create` now stays on the legacy `/api/assertion/create` route in BOTH surfaces — keeping the documented `{ assertionUri, alreadyExists }` shape and name validation intact. Reverted the speculative `validateAssertionName` addition to the KA create route (which also had a non-string-name 500 hazard). 2. Publish error mapping: `respondAssertionError` no longer wraps `vm/publish`. On-chain/storage/publisher failures there can carry "Invalid"/"Unsafe" text and must stay 500 — publish now has its own generic-500 catch, matching the legacy publish path. The 400/409 parity mapping applies only to the WM/SWM mutation verbs (write/finalize/discard/ pull-from/share). Net migration boundary: write / promote / discard -> KA routes (with 400/409 error parity); create / history / query stay on the legacy routes until the KA surface reaches parity (alreadyExists reporting, name validation, author-scoped GET, quad-dump). Added a route test asserting vm/publish stays 500; removed the now-invalid create-name-validation test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Branimir Rakic <aleatoric@Branimirs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y + add wm/quads First increment of the assertion→knowledge-assets unification (issue: remove /api/assertion entirely; base integration/v10-devnet; targeting rc.17). Brings the EXISTING /api/knowledge-assets/* routes to full parity with the legacy /api/assertion/* handlers, fixing the ambiguity bugs the dual-route model caused (PR #971/#988 reviews): - contextGraphId resolution/validation on every mutation (resolveRequiredWrite ContextGraphId) — bad/foreign id is a 400, not an opaque 500 - :name decode + validateAssertionName on all verbs + GET - side-effects restored: emitMemoryGraphChanged + recordAssertionActivity + notification SSE on create/write/finalize/discard/share/publish (dashboards and the bell pane were going silent under the KA routes) - full finalize payload (assertionUri/authorAddress/schemeVersion/chainId/ kav10Address) and full vm/publish payload (assertionUri/authorAddress/ merkleRoot/kas/blockNumber) - create now reports `alreadyExists` (idempotent get-or-create); GET accepts author-scoped `agentAddress`; strict boolean validation of alsoShareSwm/ alsoPublishVm; published-activity attributed to the seal author - NEW route: GET /api/knowledge-assets/:name/wm/quads (replaces the legacy POST /api/assertion/:name/query quad dump) Full migration blueprint (6 missing routes, 30-helper relocation, 35 parity gaps, ~80 consumers) captured in docs/migrations/assertion-to-knowledge-assets.md. Typecheck-clean (cli tsc). Route unit-test mocks (listContextGraphs writability) and the remaining 5 missing routes follow in subsequent Stage-1 increments. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o shared-assertion-helpers.ts Moves 30+ helpers (import-artifact resolution, owner-guard, semantic-enrichment RDF builders, promote-async job views, quad sort) out of assertion.ts into a new shared module so the new /api/knowledge-assets route ports can reuse them and assertion.ts can be deleted in Stage 4. assertion.ts re-imports them; behavior unchanged. Typecheck-clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nt onto KA surface
Adds POST /api/knowledge-assets/import-artifact/{resolve,read-markdown} and
POST /api/knowledge-assets/semantic-enrichment/write as faithful ports of the
legacy /api/assertion/* equivalents (new module knowledge-assets-import.ts,
reusing shared-assertion-helpers). Identical owner-guard (#872 relax on
public+open CGs for reads, strict for enrichment), 400/403/404/409/413 mapping,
response shapes, and the semantic_enrichment_written SSE. Collection-level paths
dispatched before :name parsing. Typecheck-clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n-status onto KA surface
POST /api/knowledge-assets/:name/wm/import-file (multipart) and
GET /api/knowledge-assets/:name/wm/extraction-status — verbatim ports of the
legacy /api/assertion/:name/{import-file,extraction-status} handlers (same
multipart parsing, MIME inference, per-assertion lock, extractionStatus
lifecycle, two-graph atomic insert, SSE side-effects, 400/409/413/415 mapping).
import-file dispatch is placed BEFORE the JSON safeParseJson preflight so the
multipart stream is read raw. Typecheck-clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ync + share-jobs Ports all 5 legacy /api/assertion/promote-async* routes onto the KA surface: POST /api/knowledge-assets/:name/swm/share-async (enqueue) GET /api/knowledge-assets/swm/share-jobs (list, ?state/limit/contextGraphId) GET /api/knowledge-assets/swm/share-jobs/:jobId (status) DELETE /api/knowledge-assets/swm/share-jobs/:jobId (cancel) POST /api/knowledge-assets/swm/share-jobs/:jobId/recover (recover) New module knowledge-assets-async-share.ts; reuses shared promote-job helpers. Collection job routes dispatched early (incl. new DELETE handling); enqueue inlined in the SWM section. Faithful 503/400/404/409 mapping + PromoteJobView shapes. Typecheck-clean. This completes the 6 missing KA route families — the /api/knowledge-assets surface is now a full superset of /api/assertion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…w route families) Updates the route-test harness for the new parity (mock listContextGraphs writability, ctx side-effect spies, full seal/publish payloads, alreadyExists) and adds coverage for every new route: wm/quads, import-artifact resolve/ read-markdown, semantic-enrichment/write, wm/import-file (multipart), wm/ extraction-status, swm/share-async, and swm/share-jobs list/status/cancel/ recover (incl. 503 worker-unavailable, agentAddress scoping). 61 passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dge-assets Repoints every /api/assertion/* caller across the codebase to the unified KA surface (path mapping per docs/migrations/assertion-to-knowledge-assets.md): - cli api-client.ts (central SDK): all 7 assertion methods → KA; queryAssertion changed from POST-body to GET /wm/quads with querystring; promote→swm/share - mcp-dkg client.ts + adapter-openclaw dkg-client.ts: all assertion methods → KA (query POST→GET, history→GET :name, createAssertion reads alreadyExists) - adapter-hermes Python client.py: all 9 endpoints → KA (py_compile clean) - node-ui (src/api.ts, ui/lib/ontologyInstall.ts), network-sim (client + the server-side sim mock), agent/core doc-strings - ~14 devnet/test scripts (curl/fetch) repointed; living docs + SKILL.md updated Historical records (CHANGELOG, RFCs, ADRs, bug reports, async-promote specs) and unit/e2e test files left for later stages. Every touched TS package typechecks clean; Python py_compile clean; shell bash -n / node --check clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…client/devnet tests - cli daemon route-level tests (promote-async-routes, promote-async-daemon- lifecycle, promote-route-not-persisted, async-promote-queue-e2e, import- artifact-routes, memory-graph-events, context-graph-write-path-validation) migrated from handleAssertionRoutes → handleKnowledgeAssetsRoutes at the KA paths, preserving every edge case (409 not-persisted, owner-guard 403, 503 worker-unavailable, validation 400s, SSE operations). Ran green via chain-free configs (31+2+4+5+32+23+28). - adapter-openclaw/hermes + node-ui + agent client tests repointed to KA URLs/ methods/shapes (openclaw 82+191+58 green); devnet integration tests + bootstrap repointed for the final live run. Also fixes a discard parity gap the migration surfaced: the KA wm/discard handler now evicts the cached extraction-status record (extractionStatus.delete via contextGraphAssertionUri) exactly as the legacy discard did, so a re-import after discard doesn't see a stale "completed". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/kc reads Removes packages/cli/src/daemon/routes/assertion.ts (3909 lines) and its dispatch in handle-request.ts. The /api/assertion/* HTTP surface is gone — everything now flows through /api/knowledge-assets/*. Two read-only chain-metadata routes that happened to live in assertion.ts but are NOT part of the assertion lifecycle (GET /api/kc/:id, GET /api/kc/:id/author) are extracted verbatim into the new routes/kc-chain-metadata.ts module and dispatched separately, preserving their exact paths/shapes. kc-author-route.e2e.test.ts repointed to the new module. BREAKING: /api/assertion/* routes no longer exist. All first-party clients, adapters, scripts, docs, and tests were migrated to /api/knowledge-assets/* in Stages 2-3. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rix + rc.17 (#996) * fix(storage): blank-node-safe delete on SPARQL backends (sparql-http, blazegraph) `SparqlHttpStore.delete(quads)` (and the identical `BlazegraphStore.delete`) serialized every quad into a single `DELETE DATA { … }` block. SPARQL forbids blank nodes in DELETE DATA, so any quad whose subject/object is a blank node made a spec-compliant endpoint (Oxigraph server, Fuseki, Blazegraph) reject the whole statement with HTTP 400 ("Variables and blank nodes are not allowed in DELETE DATA"). This broke "Promote All → Shared" (WM→SWM, assertionPromote → store.delete of the CONSTRUCT'd source quads) for any entity containing nested/ anonymous RDF — the common case — and exposed every other store.delete(quads) caller (tentative/finalization cleanup, etc.) on the oxigraph-server backend. Only the SPARQL-over-HTTP adapters are affected. The embedded OxigraphStore and oxigraph-worker delete via the native object API (blank-node-safe), which is why every promote/share unit test (all on OxigraphStore) and the devnet (oxigraph- worker default) stayed green — while a fresh `dkg init`, which defaults to the oxigraph-server backend, hit it on real data. Fix: new `buildBlankNodeSafeDelete()` keeps ground quads on the fast DELETE DATA path and removes blank-node quads via `DELETE { … } WHERE { … }`, grouping connected blank-node components (linked by shared labels) into separate statements and rewriting each blank node to a query variable — the only spec-legal way to target existing blank-node structure over SPARQL. Per-component emission avoids cross-product WHERE joins that would silently delete nothing. Both adapters now share the builder. Tests (packages/storage/test/sparql-http-blank-nodes.test.ts, 20 cases): a broad matrix of blank-node shapes (object/subject bnodes, nested entities, RDF lists, shared bnodes, disjoint components, deep chains, mixed ground+bnode, cross-graph, the CONSTRUCT→delete promote reproduction) executed against a REAL embedded Oxigraph engine (same engine oxigraph-server runs) plus the full SparqlHttpStore HTTP path. A control test asserts the legacy DELETE-DATA-with-bnode form still throws on that engine. Verified: 20/20 pass on the fix; 16/20 fail on the legacy behaviour (the suite genuinely gates the regression). Root-cause of the test gap: the prior sparql-http test used a mock HTTP server that recorded the update string but never parsed/executed it, so invalid SPARQL passed. The new suite runs generated SPARQL through a real engine. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: bump version to 10.0.0-rc.17 rc.17 ships the blank-node-safe SPARQL delete fix (see previous commit). No contract changes — the on-chain contracts stay at 10.0.3 and need no redeploy. Root + all 18 workspace packages 10.0.0-rc.16 → 10.0.0-rc.17. Internal deps are workspace:* and pnpm-lock.yaml does not pin workspace versions, so frozen-install CI stays green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(storage): run the conformance matrix against the sparql-http backend by default + fix deleteByPattern Adds the SPARQL-over-HTTP backend (the oxigraph-server default a fresh `dkg init` gets) to the shared TripleStore conformance matrix, backed by an in-process endpoint running the real Oxigraph engine — so it executes the adapter's generated SPARQL (no server binary, runs on every CI run). Also adds blank-node cases (object/subject bnodes, nested-entity-with-bnode-children = the WM→SWM promote shape) to the shared suite so EVERY backend is exercised with blank nodes. This is the matrix entry that would have caught the rc.16 blank-node DELETE-DATA bug. Wiring sparql-http into the matrix immediately surfaced a SECOND latent bug in the same class: `deleteByPattern`'s no-graph branch emitted `DELETE { ?g_ctx { … } }` (missing the GRAPH keyword) — invalid SPARQL, HTTP 400 on any real endpoint, never executed by a test before. Fixed in both `sparql-http.ts` and `blazegraph.ts`. devnet.sh already runs a backend matrix (nodes 1-2 oxigraph-worker, 3-4 blazegraph, 5-6 sparql-http/oxigraph-server) but SILENTLY fell back to the embedded store when Docker was unavailable — which is how no devnet node ever ran sparql-http during rc.16 validation. Added a loud coverage summary and an opt-in `DEVNET_REQUIRE_ALL_BACKENDS=1` strict gate so CI cannot silently shrink the matrix. Storage suite: 181 passed / 3 skipped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(pr996): address CodeQL + e2e review feedback - storage test: stop reflecting the caught exception text in the mock SPARQL server's HTTP response (CodeQL: exception-text-as-HTML + stack-trace info exposure). The adapter only inspects the 400 status; return a constant text/plain body and log the real reason to stderr. - node-ui: add the `widget-promote-all-btn` / `widget-publish-vm-btn` / `layer-action-result` data-testids the WM→SWM UI-cycle spec targets. They were missing, so the regression spec's first toBeVisible() would time out and the promote flow never ran. Tag only the LayerActionsWidget surface (the one the default WM/SWM `items` tab actually renders) to avoid .or() strict-mode multi-matches. - e2e: correct the stale SWM→VM `fixme` rationale — the bulk publish control now mints one KA per SWM assertion (Design B), so on the shared seeded CG (~25 roots) it fires ~25 on-chain mints; single-root publish (PublishPanel) is not wired into ProjectView nav. Kept deferred (on-chain mint already covered by the API sibling spec) with accurate reasoning. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(pr996): silence CodeQL in the new sparql endpoint helper The in-process oxigraph SPARQL endpoint added in 25124c2 reintroduced the same pattern just fixed in sparql-http-blank-nodes.test.ts: its catch block reflected the caught exception text into the HTTP response, which CodeQL flags as exception-text-as-HTML (#3984) + stack-trace info exposure (#3985). The SparqlHttpStore adapter only inspects the 400 status, never the body, so return a constant text/plain message and log the real reason to stderr. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(e2e): sort WM assertion graphs by name segment, not full URI (PR review) findWmAssertion sorted candidate graph URIs by the full string. URIs are .../assertion/<agent>/<name>, so a raw sort orders by the <agent> segment first and could pick an older assertion from a different agent on a shared devnet, making the rest of the spec read counts/markers from the wrong graph. Sort by the trailing assertion-name segment (the stable per-import key) instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(e2e): poll for CG readiness + isolate adapter-path store per test (PR review) - wm-swm-vm-ui-cycle.devnet: /api/status answering doesn't mean the seeded primary CG has finished registering, so listContextGraphs() can briefly return [] on a cold boot and the spec falsely skips. Poll a bounded window before the skip-check; a genuinely-empty operator devnet still skips. - sparql-http-blank-nodes (adapter-path describe): the long-lived endpoint served a single shared embedded store across both cases, so leftover data / an early failure could leak into the next case. Reset to a fresh store in beforeEach for isolation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(e2e): drive the WM→SWM→VM UI spec on the production-default oxigraph-server backend (no Docker) (#1014) * test(e2e): run devnet UI node on the production-default oxigraph-server backend (no Docker) The devnet's node 1 (the node the UI/e2e suite drives) ran `oxigraph-worker`, while fresh installs default to `oxigraph-server`. So the UI e2e never exercised the real backend — and SPARQL-over-HTTP-only bugs like #996 (blank-node DELETE DATA) could not reproduce in the UI flow. oxigraph-server was only available via Docker (nodes 5-6), which silently degraded when Docker was absent — the exact rc.16 trap. Switch devnet nodes 1-2 to the DAEMON-MANAGED `oxigraph-server` backend: the daemon auto-downloads + SHA-256-verifies + caches the Oxigraph binary and spawns it on its own port — NO Docker, cross-platform, production parity (it's what HariSeldon runs). Keep the Dockerized external Oxigraph on 5-6 as optional extra coverage; update the backend-matrix summary + DEVNET_REQUIRE_ALL_BACKENDS gate accordingly. Add a HARD precondition to wm-swm-vm-ui-cycle.devnet.spec.ts: assert node 1's /api/status storeBackend === 'oxigraph-server' and FAIL (not skip) otherwise, so the bug-reproducing backend can never silently regress to a trivial pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(devnet): pin per-node oxigraph-server ports (7900+n) to avoid the 7878 collision The daemon-managed oxigraph-server defaults to port 7878; two nodes on one host (devnet nodes 1-2) collided with 'Address already in use'. Give each its own port via store.options.port. Caught by booting the e2e devnet on the new oxigraph-server backend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(e2e): make the WM→SWM→VM UI spec green — stale-state wipe, import-result hooks, publish-policy retry Three issues surfaced by running the flagship oxigraph-server UI spec end-to-end: 1. devnet.sh: each `start` redeploys a FRESH chain but kept persisted per-node store state, so a node's stale `dkg:contextGraphOnChainId` triple made registerContextGraph short-circuit ("already registered") and never create the CG on the new chain → isContextGraphActive=false, publishPolicy reads 0, and every VM publish hit LU-5 "publish access-policy is unknown". Now wipe per-node state on a fresh-chain boot (keeping the cached Oxigraph binary), so nodes re-register against the new chain. Fixes local repeated-boot flakiness (CI was masked by always starting from a clean .devnet). 2. ImportFilesModal: the result element exposed `className="v10-import-result"` but none of the `data-testid="import-result"` / `data-import-status` / `data-import-triples` hooks the spec asserts on. Added them (the spec was written test-first; the component just never exposed the contract). 3. devnet-publish.ts: retry the SPECIFIC LU-5 "policy-not-confirmed" transient in publishToVm (≤45s) — defensive hardening for the genuine few-blocks registration→confirmation lag (distinct from #1 above), centralized so the global-setup seed and every publish caller benefit. Result: wm-swm-vm-ui-cycle.devnet.spec → import + promote (the #996 blank-node WM→SWM migration) pass through the REAL UI on the production-default oxigraph-server backend; VM-publish stays an intentional test.fixme (covered by the API-driven sibling spec). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Branimir Rakic <aleatoric@Branimirs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * fix(e2e): query the _meta marker with a hardcoded GRAPH IRI (PR review) readMemoryLayerMarker queried the meta partition with `GRAPH ?mg` + a CONTAINS(/_meta) filter. Under the daemon's scoped-query guard (documented in this file's header) the `<cg>/_meta` partition is ONLY reachable via a hardcoded `GRAPH <iri>`; the variable+filter form is for the data / _shared_memory partitions and 400s on a strict daemon, so the marker read would fail even after a successful promote. Build the exact meta-graph IRI (`did:dkg:context-graph:<cg>/_meta`, identical to what the agent's gossip/finalization handlers use) and query it directly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(e2e): scope dropzone 'supported-format hint' assertions to the modal import-files + import-data-display asserted page-wide getByText(/\.md/) for the dropzone's static supported-formats hint. On the shared devnet that also matches any .md document already imported (e.g. the WM→SWM→VM lifecycle spec's single-entity-fixture.md), tripping strict-mode with multiple matches. The hint lives in the import modal, so scope the assertions to importFilesModal.overlay. Hardens them against any shared .md content (root cause; green on main only because nothing else imported a .md there). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Branimir Rakic <aleatoric@Branimirs-MacBook-Pro.local> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…tegration # Conflicts: # packages/adapter-hermes/test/hermes-adapter.test.ts # packages/adapter-openclaw/test/plugin.test.ts # packages/agent/src/dkg-agent.ts # packages/cli/src/daemon/handle-request.ts # packages/cli/src/daemon/routes/assertion.ts # packages/cli/src/daemon/routes/knowledge-assets.ts # packages/cli/test/import-file-integration.test.ts # packages/cli/test/knowledge-assets-route.test.ts # packages/cli/test/promote-route-not-persisted.test.ts # packages/mcp-dkg/src/client.ts # packages/mcp-dkg/src/tools/assertions.ts # packages/node-ui/src/ui/api.ts # packages/publisher/src/dkg-publisher.ts # packages/publisher/src/storage-ack-handler.ts
…utes Applies the same browser-client repoint as the migration's other ~80 consumers, to main's copy of ui/api.ts (import-file, create/publishTriples, promote, extraction-status -> /api/knowledge-assets wm/import-file, POST, swm/share, wm/extraction-status). Closes the last live /api/assertion caller on the main-reconciled tree. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
branarakic
added a commit
that referenced
this pull request
Jun 7, 2026
D6: demote the workspaceOwner first-writer-wins THROW (dkg-publisher.ts share path) to an advisory warn+skip — co-claims are no longer rejected (OT-RFC-46 §17.4). Under the still-bucket SWM the foreign root is skipped (no clobber); the skip is removed once SWM is per-KA (rc.17b). Receiver-side gate + the layout-entangled substrate/identity/migration work is spec'd, not written blind. docs: detailed per-file rc.17a/rc.17b implementation + devnet-test plan (the 15 work items, the two sub-trains, the feature-flag/dual-read + 4 hard requirements). PENDING CI — this throwaway clone has no pnpm/build/devnet env, and the work is gated on #1041 landing on main. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Author
|
Subsumed into rc17-vm-wip (the rc.17 integration branch → main via #1053) — this work shipped as part of integrated rc.17. Closing as superseded. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
main-targeted version of #1039. Same migration (remove the legacy/api/assertion/*HTTP surface; unify every assertion-lifecycle op on/api/knowledge-assets/*), reconciled ontomaininstead ofintegration/v10-devnet, with the one gap that reconciliation surfaced now fixed.#1039 targets
integration/v10-devnet(where it was developed). Sincemainis the active line and has caught up on the KA hardening, this PR retargets the work tomain. Recommend landing this and closing #1039.What's here
−3909-lineassertion.tsdeleted;/api/kcchain-reads preserved in a newkc-chain-metadata.ts; all assertion-lifecycle ops on/api/knowledge-assets/*; ~80 consumers repointed.main— 14 conflicts resolved (consensus files byte-identical between integration and main → zero sync-risk;knowledge-assets.ts= migration + main's Fix oversized SWM promote failures and UI loading hangs #1007 413-handler;assertion.tsstays deleted; main's test-suite splits accepted).fix(node-ui): the last consumer gap —ui/api.ts(the browser client) still had 4 live/api/assertioncalls onmain(import-file, create, promote, extraction-status); repointed to the KA surface. Audited: no live/api/assertioncallers remain anywhere in source.Verification
oxigraph-serverfleet):v10-core-flows5/5 on a clean isolated run — lifecycle SSE, subgraph view routing, RFC-26 operator fees, both staking flows/api/assertion→ 404 on a live node; KA create/write/quads/share/discard verified; bootstrap publishes confirmed on-chain/api/assertion/*no longer exists. All first-party consumers are repointed in this PR. External callers must move to/api/knowledge-assets/*(mapping indocs/migrations/assertion-to-knowledge-assets.md).🤖 Generated with Claude Code