Decision (2026-07-03): the claim graph is demoted from knowledge representation to verdict layer. Artifacts (full work-unit payloads) live in the blob store and a daemon-local embedding index; graph nodes are debated judgments about artifacts, referenced by sha256 digest. Inference retrieves artifacts by embedding similarity and re-ranks by graph standing. Equilibration continues to run only on the claim layer.
Rationale: experiments showed the graph-as-retrieval-index loses to
plain embedding retrieval (experiment_utility.py, May 2026) and the
locate→render path returned labels, not payloads (phase6 artifact,
May 2026). The substrate's differentiator is the trust picture it
attaches to a tool, not retrieval mechanics. (This was written as
"debate-priced verdicts"; the debate rail retired at v3 and the vetting
gate at v4.1 — the live differentiator is now the rep-weighted review
scores that drift a tool's position and rank discovery. See
docs/tool_substrate.md, Decision 2026-07-09.)
class ArtifactIndex:
def __init__(self, blob_store, embedder=None, index_path=None, dim=DEFAULT_DIM): ...
def add_artifact(self, payload: Dict[str, Any]) -> str:
"""blob_store.add_json(payload) -> digest; embed payload text;
append (digest, embedding, label) to index_path (jsonl).
Idempotent per digest. Returns the sha256 hex digest."""
def search(self, query_text: str, k: int = 5) -> List[Tuple[str, float]]:
"""Top-k (digest, cosine) by embedding similarity."""
def has(self, digest: str) -> bool: ...
def rebuild(self) -> int:
"""Re-embed every blob-store JSON payload that looks like an
artifact (has 'problem' or 'text' key). Returns count."""- Embedding text for a work-unit payload:
f"{problem}\n\n{resolution}"; for generic payloads, thetextfield. - Embedder default:
default_usefulness_embedder(dim=dim)fromusefulness_coords. - The index is derived, daemon-local state — NOT consensus state. It must be rebuildable from blobs. Never gossiped, never anchored.
- Persistence: jsonl at
index_path; loaded eagerly on construction; in-memory numpy matrix for search.
- New optional field
artifact_digest: str = ""onSubClaimSproutedinevents.py. Serialized into_dict()only when non-empty;event_from_dicttolerates absence (back-compat with recorded WALs and gossiped events from older daemons — replay of old logs MUST produce identical worlds). - Federated replay: the digest rides the event; peers fetch the blob lazily via the existing p2p blob protocol when rendering. The digest does not participate in node ids, coords, or equilibration — bit-identical epoch close is unaffected.
- Full
(problem, resolution, outcome)payload →{"problem": ..., "resolution": ..., "outcome": outcome_dict}→ArtifactIndex.add_artifact→ digest. - The per-work-unit claim node keeps its claim-text content and gains
artifact_digest(stored on the node as attribute and on the emitted SubClaimSprouted event). [SUPERSEDED by v3 — see Addendum 2026-07-08: work units no longer sprout claim nodes at all; ingestion stops atadd_artifact.] submit_conunchanged (disputes target claim nodes, which now transitively price artifacts). [STALE as of v3:submit_con/submit_supportare retired from the live path.]
New mode "artifacts":
def _infer_artifacts(input_data, world, artifact_index, blob_store,
k=5) -> Dict[str, Any]- Query text:
input_data["text"](orquery/question). artifact_index.search(query, k=k*3)→ candidates.- Standing per digest: over all claim nodes in the world with
artifact_digest == digest:standing = Σ net_score(PRO-positioned) − Σ net_score(CON-positioned). - Re-rank:
final = cosine * (1.0 + tanh(standing)), keep top k. - Return shape:
{"mode": "artifacts", "artifacts": [{digest, cosine, standing, final, payload, claims: [{content, position, net_score}]}, ...]}wherepayload = blob_store.get_json(digest)(None tolerated if the blob hasn't replicated yet — include the digest anyway). infer_with_world_modelgains optionalartifact_index=,blob_store=kwargs and dispatchesmode="artifacts"when both are provided and the input isn't turn-shaped. Existing alignment/general modes untouched.
WorldService.__init__constructs anArtifactIndexnext to its WAL (<state_dir>/artifact_index.jsonl) sharing the daemon's blob store when available (else a localBlobStoreunder<state_dir>/blobs/).- New method
infer_artifacts(query_text, k=5)→_infer_artifactsunder the service lock.
- Digests are sha256 hex from
blob_store.py— NOT IPFS CIDs. - Determinism of federated epoch close is sacred: nothing in this refactor may alter node ids, coords, equilibration inputs, or event ordering for existing event kinds.
- Never run the full pytest suite. Targeted files only.
- The scoring/re-rank formula is advisory (daemon-local inference), not consensus — exact constants may be iterated freely.
- Scoped/per-tendency equilibration (the O(N²) wall) — separate work.
- Any change to mint, reconcile, or the gate.
- P2P blob replication improvements.
Phase 8 (docs/phase8_prereg.md, docs/phase8_results.md) contested this
design end-to-end. Two findings feed back into this document:
- Retrieval validated. Plain artifact retrieval (payloads only, no
verdict text) beat no-context by +0.367 (Holm p=0.012) — the two-plane
data path (artifacts in blob store +
ArtifactIndex) is the product surface, confirmed on a real, unsaturated domain. - Verdicts-in-context hurt. Injecting claim/verdict text into the
prompt alongside payloads cost −0.280 (Holm p=0.012) relative to
payloads-only.
render_context()ininfer.pytherefore ships the arm-B format: standing-ranked payload text only (problem + resolution, proportional truncation), no claims/verdict text in the rendered prompt. Claims remain available in the structured result for programmatic consumers. Standing ranks and prices; it does not fill context. Seedocs/ledger_pricing.md(Change 3) for the exactrender_context/infer_artifacts(render=...)contract. - Equilibration demoted. The re-rank geometry contrast (equilibrated
vs vote-count standing) cleared significance but not the pre-registered
magnitude bar, so epoch-close pricing now defaults to the ledger mode
(
net_scoretree recursion) described indocs/ledger_pricing.md; equilibration is an opt-in experimental kernel pendingdocs/phase9_depth_experiment.md. This does not change the artifact plane / verdict plane split above — it changes how standing on the verdict plane is computed by default.
Per the v3 decision (docs/tool_substrate.md, Decision 2026-07-08),
the verdict plane narrows to TOOL MANIFESTS only:
- Work units leave consensus.
submit_work_unitsstops sprouting claim nodes; ingestion isadd_artifactonly (the ArtifactIndex was always daemon-local and never gossiped — §1 stands unchanged). Retrieval over work-unit artifacts becomes pure embedding ranking, which is exactly the arm phase8 validated (+0.367, payloads-only). - Re-rank formula (§4) changes for tool manifests: standing is
retired;
final = cosine × (1 + tanh(mean(head[correctness], head[simplicity])))whereheadis the tool's review-drifted charter head. Work-unit artifacts rank by pure cosine (their claim nodes no longer exist). The coverage-density blend is unchanged. submit_con/submit_supportretire from the live path; the arm-B render rule (standing ranks, never fills the prompt) carries over as "ratings rank, never fill the prompt".