Skip to content

Documentation Verification

Pages were checked against the EIDOS source code at more than one commit: 3 at 0481ba39896fbe5e4b59257b631ffe6218e86f6e on 2026-10-20, 19 at 4996d1ceb770fb4545edebc5fd140792bfb23830, 12 at 421c63ba3fab5894cdb9e707eed8ef694881d60d, 7 at 9e119ef0950aa1f8c8f87a640132addf0109872c, 7 at 4d5c9b5781eaaa62048db19cd10dc7039e402c98 and 1 at 59cdd26ce9c6fd83e4c19969b3f9cffd1a189edb. Each page's banner names the commit it was checked at. Pages pinned to a later commit had their KM sources corrected and were re-verified at that commit; on the others the KM sources are unchanged and divergences are marked at the top of each page — those marks live only in this reference site.

49 pages audited — 49 verified · 0 minor · 0 diverging · 0 findings.

Page Status Findings Notes
Atomic Write Verified — #956 D1b-4 (2026-10-20): 4 finding(s) fixed or closed — every overlay data write (nodes, relations, tombstones, idempotency, previews) goes through write_atomic, including heal-uuid; the _cache_version sentinel uses the lighter bump_version_counter (temp + os.replace under a flock, no fsync), named as a separate primitive; write_atomic cited at mutation_store.py:246-260 (module-level :668), lock helpers at 84-105. #1015 (2026-10-20): the heal-uuid finding is closed in code (write_lock() is held across the read-scan-write and it calls mutation_store.write_atomic at mutations_router.py:965). #1014 (PR #1024): the write_ownership finding is closed in code (the dead API was deleted; the page already says no ownership writer exists). Re-verified against main 4996d1ce.
Cache Coherence Verified — #956 D1b-4 (2026-10-20): 5 finding(s) fixed — the browser caches (EidosNodeStore._nodes, EidosTree._childrenOf, labelMap) are unbounded plain objects with no LRU/eviction; current_graph() is the sole loader entry (_load removed in #868 Slice 8); mutation_store pins invalidate :293, bump_version_counter :108, write_lock :166 (decorator :165, mkdirs MUTATIONS_DIR before the flock). Re-verified against main 4996d1ce. #935 S1 (2026-10-21): line pin moved (_cache at eidos_loader.py:40). Checked against archon/issue-935. #935 S7 (2026-10-22): a rebuild reads the overlay only (no .eidos file since S5a) and the tree declaration from the runtime domains.json. Checked against eidos_loader.py at a11d2636.
Canonical Deep Link Verified — #956 D1b-4 (2026-10-20): 1 finding(s) fixed — Doc360 links come from a direct infra.iri.node_deeplink call (documentation_360.py:587); build_node_url (:97) stays a thin re-export that only tests call. Re-verified against main 4996d1ce. #989 WP9 S3 (2026-10-22): resolveNodeBase sends GET /resolve the node IRI; an escaped IRI hash opens the node. Checked against frontend/app.js on the #989 branch (base 3c69a14f). #1127 #607 S3 (2026-10-25): an occurrence IRI hash opens its product (resolveNodeBase keeps the /resolve answer; restoreFromHash queues EidosCore.revealOccurrence) and the panel reveals the matching occurrenceId row. Checked against frontend/app.js and detail-panel.js on the #1127 branch (base 6774a2b6).
Composite View Assembly Verified — #956 D1b-4 (2026-10-20): 4 finding(s) fixed — _strip_prefix lives in infra/key_derivation.py:280 (duplicate at eidos_loader.py:26) and is reached via derive_slug_segment; _build_ref is notation-first (stored notation verbatim, else derive_slug_segment(label, is_root=not segments), missing ancestors skipped); current_graph() does not serialise loads (no lock, only the cache truthiness + sentinel check); index_ref matches the base build in eidos_loader.current_graph. Re-verified against main 4996d1ce. #935 S1 (2026-10-21): line pin moved (_strip_prefix duplicate at eidos_loader.py:33). Checked against archon/issue-935.
Config Register Verified — #956 D1b-4 (2026-10-20): 1 finding(s) fixed — the residual env reads now match the test_env_read_guard baseline (4 files/7 reads: run.py PORT/WORKERS, server.py LICENSE_KEY/LICENSE_FILE/PYTEST_CURRENT_TEST, the setup_router passthrough, the eidos_loader EIDOS_FILE presence check), the exempt iam/config.py and infra/log_config.py readers are named, and run.py's own WORKERS fallback is noted. Re-verified against main 4996d1ce.
Cross Domain Resolution Verified — #956 D1b-4 (2026-10-20): 6 finding(s) fixed — the relation's own uuid is derive_overlay_relation_uuid(idem_key) (WP4) and the external placeholder is not written to disk (eidos:target ExternalRef; read_target_ref_jsonld re-derives it or returns eidos:node); app.js apiFetch is a thin wrapper and routing/proxy rewrite/throw live in _resolveUrl in eidos-api.js; the http-client ratchet is 1 (domain_client only, the chat residual went in #579); getBaseURI sites app.js:2831/2875 fall back to BASE_URL, the repo-chip site :13344 to undefined; app.js pins re-cited (navigateTo 5129, seeds 5135/5137, neighbour fetch 7122, warn 7142, allSettled 3520); neighbour descriptions are cached by tgtUuid. Re-verified against main 4996d1ce. #967 WP8 P3 (2026-10-21): the TargetRef section now says the EXT_DOMAIN_KEY / EXT_REF_KEY / EXT_TARGET_REF_KEY public aliases are deleted and no module outside target_ref.py names a key constant (const ratchet pinned at 0; tools/ uses external_ref_of / external_ref_fields); the read_target_ref_jsonld pin moved to target_ref.py:215. Checked against the P3 change on top of main 562594ac. #967 WP8 P4 (2026-10-21): new subsection 'Placeholder upgrade on write' — create_relation upgrades a stored ExternalRef/UnresolvedRef placeholder to the real node identity on the next write of that relation (duplicate-triple branch or idempotency guard) via the injected id→domain index; never on load, never in bulk; misses, mismatches and index errors keep the placeholder byte-identical; sources gain graph_crud_engine.py and id_domain_index.py. Checked against the P4 change on top of main d0c1b465. #967 WP8 P4 send-back r1 (2026-10-21): the upgrade now requires the stored source, predicate and placeholder ref (and domain) to match the request, binds an UnresolvedRef only to a live non-tombstoned node, uses the cache-only resolve_id_domain_cached under the write lock (no refresh), emits one UpgradeRelationTarget audit event that the as-of fold applies, narrows the read/lookup/write catches, and skips dry runs at both call sites; sources gain reconstruction_service.py.
Cross Sync Verified — #956 D1b-1 (2026-10-20): 2 finding(s) fixed — noteOwnCommit call-site pins (app.js:11452/11622/12329/10508 at 421c63ba), GET /version gates via api.deps.current_user. Re-verified against main 421c63ba.
Deterministic Uuid Verified — #956 D1b-4 (2026-10-20): 5 finding(s) fixed — from_iri raises InvalidIRIError on a bare uuid and only GET /resolve falls back to the raw id; since WP4b the relation target is the single on-disk eidos:target field (ExternalRef/UnresolvedRef node or node IRI); derive_overlay_relation_uuid hashes only {commandType, idempotencyKey} and duplicates are caught by _find_existing_triple; payload_hash uses json.dumps default separators (not compact); build_preview calls derive_entity_uuid at mutation_engine.py:272. #1016 (2026-10-20, earlier): the HTTP AddNode preview/commit UUID finding was closed in code (preview and graph_crud_engine.create_node both build cmd with mutation_engine.normalize_add_node_cmd). Re-verified against main 4996d1ce. #989 WP9 S3 (2026-10-22): the frontend sends the IRI on every parsed public node-id input it calls (not /me/can-edit, which it does not call). Checked against frontend/app.js and detail-panel.js on the #989 branch (base 3c69a14f).
Documentation 360 Engine Verified — #956 D1b-3 (2026-10-20): 6 finding(s) fixed — node_deeplink's hash is the unescaped node_iri (#1000, iri.py:116), cross-domain pivot names come from the bounded _resolve_cross_domain_pivot_names round + _apply_pivot_names, plan_context takes excluded_predicates and build_report takes consumed (#637), an unregistered pivot domain raises the typed UnknownDomainError, a subtree-scoped documentContext id includes applies_to, detail-panel.js/index.html/app.js citations re-pinned (:3344, :2503, :1083, :6344/6354). Re-verified against main 9e119ef0.
Documents Engine Verified — #956 D1b-3 (2026-10-20): 7 finding(s) fixed — ImportedRepo fields are fingerprint/source/format_version/mode/root_id/nodes/relations/containers (no documents), preview keys relation status on the (from, to, predicate) triple (WP4 #953), _compute_risks returns orphaned/danglingAttachments/possibleRename/newNodes, FME 1.3.0-1.6.0 are accepted, commit takes filename=None, the risks call is at documents_import.py:326 (def :226), routes/documents.py blocklist citations re-pinned (:2155, :263-306, :1856, :760, :774, :871). Re-verified against main 9e119ef0. #1077 (2026-10-22): DocuGraph import boundary validation documented — canonical uuid / node_iri ids (INVALID_NODE_ID, INVALID_PARENT_ID, INVALID_RELATION_ENDPOINT), @id == node_iri(id) (NODE_IRI_MISMATCH), validate_against_graph (UNRESOLVED_RELATION_SOURCE, UNRESOLVED_RELATION_TARGET) in preview, commit and the commit endpoint pre-loop, and dangling external-ref relations written as eidos:ExternalRef / eidos:UnresolvedRef; checked against documents_import.py and routes/documents.py on archon/issue-1077 (base main c7aa9768). #1112 WP9 S4b (2026-10-23): FME 1.3.0-2.0.0 accepted, a 2.0.0 file (identity values as IRIs) reads to the same bare uuids as the same model in 1.7.0 through infra.iri.canonical_node_uuid, UNSUPPORTED_VERSION names the refused version, and a legacy urn node or relation id is refused LEGACY_ID_REFUSED; checked against documents_import.py on archon/issue-1112 at 86bac62b.
Domain Verified — #956 D1b-4 (2026-10-20): 4 finding(s) fixed — the EffectiveGraph and domain_root_uid/path are built by eidos_loader.current_graph(); a node's @id/iri is node_iri(uid) = https://id.ontoteq.com/{uuid} since #954 WP5 S7, publicURI only bases external relation target URLs; _domain_urls/_domain_public_urls copy config.domain_maps(), external_node_uri delegates to infra.iri.external_node_by_ref_url; _gate_proxy_domain (server.py:804-822) runs _assert_tier('multi_domain') before the IAM check. Re-verified against main 4996d1ce. #935 S1 (2026-10-21): hierarchyPredicate and formalType added to the domain schema (required by the generator); predicate is no longer described as the tree predicate; a domain may have no .eidos file. Checked against archon/issue-935. #1080 (2026-10-22): the deploy-time runtime domains.json generator also refuses a domain without hierarchyPredicate/formalType. Checked against tools/bootstrap/bootstrap.py on archon/issue-1080 at 3dd9a7aa. #935 S7 (2026-10-22): the loader takes the tree declaration from the runtime domains.json; no .eidos file declares a tree or supplies a domain's data since S5a. Checked against eidos_loader.py at a11d2636.
Domain Registry Verified — #956 D1b-1 (2026-10-20): 11 finding(s) fixed — IAM refresh via EidosCore.dispatchMutation kinds iam (eidos:tree-refresh retired), DomainRouting read via EidosCore.module (no window global), publicURI is the node-by-ref host while node @id is the identity IRI, node-update via EidosMutations, init skips hidden domains, loadRegistry via EidosApi.raw, app.js/server.py line pins. Re-verified against main 421c63ba. #1080 (2026-10-22): pass-through discipline now names the backend runtime generator (generate_backend_domains_json) carrying formalType/hierarchyPredicate on every deploy path, its UnknownDomainError refusal and the preflight check. Checked against tools/bootstrap/bootstrap.py, tools/bootstrap/domain_catalog.py and deploy.py on archon/issue-1080 at 3dd9a7aa.
Domain Routing Key Invariant Verified — #956 D1b-4 (2026-10-20): 5 finding(s) fixed or closed — the guarantee now states the apiFetchAbsolute exception (a non-IRI URI routes by its own origin, valid only for a registered baseURI, else 'Unregistered domain base'); _relUriBase sends IRI/bare-uuid input through resolveNodeBase and only /node/ URLs through resolveBaseForURI; app.js lines re-cited; sources.files names the real frontend/plugins/tests files. #1026 (b4e42da1): the app-cross-domain-routing guard-regex finding is closed in code (the regex matches EidosCore.module('DomainRouting') and the suite runs in CI; 3/3 pass at 4996d1ce); the page now says the u.origin guard matches only the 2-argument form. Re-verified against main 4996d1ce.
Domain Visibility Verified — #956 D1b-4 (2026-10-20): 1 finding(s) fixed — the combined hidden state is read by _isHiddenDomainBase (app.js:3067), by the init() tree-fetch filter (:3514-3516, which computes the union itself) and by renderDomainVisibilityList (:15997); all are tree/tab/Admin surfaces. Re-verified against main 4996d1ce.
Effective Graph Verified — #956 D1b-4 (2026-10-20): 2 finding(s) fixed — every accessor/lifecycle/cache reference now names eidos_loader.current_graph() (eidos_loader.py:123) instead of the removed load(); nodes.update at graph_merge.py:152, overlay_rels at 194-199; malformed overlay files are logged (load_overlay*_parse_failed) and skipped, not silent. Re-verified against main 4996d1ce. #935 S1 (2026-10-21): line pins moved (eidos_loader.py:191, graph_merge.py:214-219). Checked against archon/issue-935. #935 S7 (2026-10-22): the base layer is described as empty since S5a; the cache fields domains, propagation_rules and all_rules are always []; a rebuild reads mutations/ only. Checked against eidos_loader.py at a11d2636.
Effective Relations Engine Verified — #956 D1b-3 (2026-10-20): 2 finding(s) fixed — the 5.1b scope boundary now lists the 8 backend modules calling read_relations (incl. metrics/context.py ModelCtx.effective_outgoing) and states _BYPASS_BASELINE is 0 (5.1e #867). Re-verified against main 9e119ef0. #1126 #607 S2 (2026-10-25): new section 4 and the node_label injection — an inherited signal relation carries occurrence_id (key_derivation.derive_occurrence_uuid), occurrence_ref and derivation. Checked against backend/domain/effective_relations.py on the #1126 branch (base b4bead2f). #1127 #607 S3 (2026-10-25): section 4 addressing — GET /resolve answers an occurrence IRI with its product (id_domain_index.resolve_occurrence, the product's gate) and the proxy's overlay rows carry the occurrence fields (inherited_occurrence). Checked against id_domain_index.py, server.py and proxy_router.py on the #1127 branch (base 6774a2b6).
Eidos Action Verified — #956 D1a (2026-10-20): re-read at main 58abc0b5 (cited code unchanged at 0481ba39), 0 findings. KM YAML and compiled dev page match the code. All three canonical operations (registerAction, _dispatchAction, _hasAction) exist on EidosCore with the documented behaviours (duplicate-name-throws, blank-name-throws, non-function-throws, identity-guarded unregister, silent no-op on unknown name, submit preventDefault before handler, four delegated listeners, double-load guard). The C3 ratchet frozen at 0 is confirmed in tests/test_frontend_inline_handlers_ratchet.py.
Eidos Api Verified — #956 D1b-1 (2026-10-20): 1 finding(s) fixed — the three head plugins and shortcuts.js load before eidos-api.js; only app.js and later plugins follow it. Re-verified against main 421c63ba.
Eidos Core Boundary Verified — #956 D1b-1 (2026-10-20): 2 finding(s) fixed — EidosCore snapshot is 65 members (connectivityPredicates #918 + resolveNodeBase); 16 registered modules including SpiderEdges. Re-verified against main 421c63ba.
Eidos Iri Verified — #956 D1b-1 (2026-10-20): 1 finding(s) fixed — W5 share links carry the identity IRI hash (canonicalNodeShareUrl, #1000/#1006), not {BASE}/node/. Re-verified against main 421c63ba. #967 WP8 P3 (2026-10-21): TARGET_REF_KEY now cites the backend's private _EXT_TARGET_REF_KEY (the public alias is deleted; literal unchanged). Checked against the P3 change on top of main 562594ac. #989 WP9 S3 (2026-10-22): toIriParam added; app.js and detail-panel.js send every outgoing query/body node id as the IRI, the fetch ratchet counts bare ones at 0, and the escaped/unescaped IRI and legacy hashes are pinned. Checked against frontend/eidos-iri.js, app.js and detail-panel.js on the #989 branch (base 3c69a14f). #989 WP9 S4a (2026-10-23): FME 2.0.0 writes every identity value as its IRI; infra.target_ref.relation_target_iris writes to and external_uuid as node IRIs whatever the target's kind, a placeholder to kept as its IRI. Checked against target_ref.py and export_service.py on the #989 branch (base 8e8c8dd3). #1114 WP9 S5a (2026-10-23): toIri returns an id that is already a node IRI unchanged; the response paths still carrying a bare node uuid are frozen in a shrink-only allowlist (tests/test_wp9_s5_bare_id_ratchet.py). Checked against frontend/eidos-iri.js on the #1114 branch (base c7bada31). #1119 WP9 S5b (2026-10-23): every frontend and backend-internal reader of an allowlisted field reads the IRI companion (EidosIRI.idOf / infra.iri.companion_id), pinned by an AST taint scan and a stripped-response test; no response changed. Checked against eidos-tree.js, app.js, detail-panel.js, the export plugins and the backend fan-outs on the #1119 branch (base 4cafd4a7). #1120 WP9 S5c (2026-10-23): REST responses drop each bare node id whose IRI companion is set (infra.iri.without_bare_ids); a null companion keeps the value; the S5a allowlist is 42 -> 8, each kept value with its reason. Checked against backend/infra/iri.py and tests/test_wp9_s5_bare_id_ratchet.py on the #1120 branch (base fc560689). #1127 #607 S3 (2026-10-25): an occurrence IRI hash goes through the unchanged idOf; /resolve names its product. Checked against frontend/app.js on the #1127 branch (base 6774a2b6).
Eidos Loader Verified — #956 D1b-2 (2026-10-20): 3 finding(s) fixed — _build_ref is notation-first (stored skos:notation, else derive_slug_segment, WP2 #951), patch_add_relation has no production caller (mutation_store only uses patch_remove_relation), a missing EIDOS_FILE raises FileNotFoundError and only a corrupt archive raises BadZipFile. Re-verified against main 4d5c9b57. #935 S1 (2026-10-21): a missing EIDOS_FILE is no longer an error — current_graph() builds the domain from the overlay root node + config/domains.yaml (hierarchyPredicate, formalType); OverlayRootConflictError when the overlay has 0 or 2+ roots; tree_name_by_id now built after the merge; archive read and overlay-only tree extracted into _read_archive / _overlay_only_tree. Checked against archon/issue-935. #935 S2 (2026-10-22): documented the base-to-overlay converter (tools.migration convert: root + WORLD to overlay files, data.eidos archived with sha256 receipt, lossless-only refusals, dry-run, rollback). Checked against tools/migration/eidos_base_to_overlay.py on the #935 branch (base 3fd2448f). #1080 (2026-10-22): the overlay-only tree declaration is read from DOMAINS_FILE (the deploy-written runtime file on a deployed instance), which now carries both fields. Checked against persistence/eidos_loader.py and tools/bootstrap/bootstrap.py on archon/issue-1080 at 3dd9a7aa. #935 S7 (2026-10-22): the tree declaration is read from the runtime domains.json (generated from config/domains.yaml), not from config/domains.yaml directly. Checked against eidos_loader.py at a11d2636.
Eidos Nodestore Verified — #956 D1b-1 (2026-10-20): 1 finding(s) fixed — the EidosCore.nodeCache getter was removed in 5.2f (#906); only app.js's const alias remains. Re-verified against main 421c63ba.
Eidos Paths Verified — #956 D1b-1 (2026-10-20): 4 finding(s) fixed — head plugins load before eidos-paths.js, _EXCEPTIONS holds only domains.json (two-phase omission documented in the eidos-paths.js header), the contract guard is a lexical scan, the freshness test compares in memory. Re-verified against main 421c63ba.
Eidos Tree Verified — #956 D1b-1 (2026-10-20): 5 finding(s) fixed — _ingest(trees, freshIds) documented as the IAM /tree adapter into EidosNodeStore returning {trees, iamEnabled}, snapshot() copies _parentOf only, _rebuild(trees), one B2_BASELINE counter over six private maps, B1 scope. Re-verified against main 421c63ba.
Error Canon Verified — #956 D1b-4 (2026-10-20): 1 finding(s) fixed — enforced_by now names the single @app.exception_handler(BackendDomainError) at server.py:1212, which reads http_status/error_code off the raised error (the nine per-type handlers were collapsed into it). Re-verified against main 4996d1ce. #935 S1 (2026-10-21): OverlayRootConflictError (500, overlay_root_conflict) added to the table and to the frozen mapping in tests/test_error_canon_guard.py. Checked against archon/issue-935.
Excel Importer Verified — #956 D1b-2 (2026-10-20): 4 finding(s) fixed — evaluate_commands and the parse_excel/normalize_paths node-import routes are documented as retired (#588) with node import on /mutations/import/excel/nodes/*, the UNRESOLVED relation uuid comes from derive_overlay_relation_uuid(idem_key) (WP4 #953), the relation commit lives in mutations_io_router. Re-verified against main 4d5c9b57.
Export Engine Verified — #956 D1b-2 (2026-10-20): 7 finding(s) fixed — admin helpers are _buildFullModelDownloadPath/_buildProjectExportPath, export_service imports infra.iri (id_from_envelope) and infra.target_ref, build_export_doc lists all four extra kwargs, the share link is origin+pathname+'#'+EidosIRI.toIri(uuid), 15 export formats, exports filter via IamScope, plugin tests use .test.mjs (no export-boq test). Re-verified against main 4d5c9b57. #946 M1 (2026-10-21): FME 1.7.0 — relation @id in every mode, mode=migration (strict provenance/tombstones/auditByDomain, derived markers), GET /admin/export/full-model/audit; written against export_service.py and routes/domain_io.py on the #946 branch, pin moves to the merge commit after merge. #946 M2 (2026-10-21): Row shape corrected — archive mode, not only migration, drops created/createdBy (every _RELATION_PROVENANCE_KEYS key) on inherited rows, M1 behaviour the page omitted; migration node/relation fan-out now strict (ExternalServiceError, no reachable:false); tools/migration package builder documented. Checked against export_service.py _stamp_relation_ids at 79cc74e7 and routes/domain_io.py on the #946 branch. #946 M3 (2026-10-21): migration package sidecars documented — per-domain access_rules.db/interfaces.db (online backup + VACUUM), notifications.db (notifications + user_changelog_seen only), instance.json, relation_catalog.json; --shared users.json (tokens dropped, class secret), rulebook, property catalogs; access_rules reference check and secret scan refuse the build. Checked against tools/migration on the #946 branch (base 59cdd26c). #1049 RDF WP0-c (2026-10-21): migration mode now carries secondary provenance and full secondary tombstones via the new strict GET /admin/export/full-model/provenance fan-out; the "not included yet" sentence removed. Checked against routes/domain_io.py and export_service.py on the #1049 branch, KM pin re-verified at main 59cdd26c. #946 M4b (2026-10-21): migration import now rewrites legacy urn:eidos:* @id values in mutations/{nodes,relations,node_tombstones,tombstones} to the canonical IRI of the same uuid, and urn:eidos:audit: values of eidos:createdAuditRef/updatedAuditRef/auditRef to audit_iri of the same uuid (eidos:source stays literal: the external placeholder uuid is derived from it), refuses an unparseable or file-name-mismatched id or an unparseable audit reference before any write, writes id-mapping.json (rewritten + auditRefsRewritten) per domain target, fsyncs staged files and refuses equal or nested --domain/--shared targets; M4a import paragraph corrected. Checked against tools/migration on the #946 branch (base e29f1810). #935 S2 (2026-10-22): tools.migration convert subcommand noted with a pointer to the EIDOS Loader page. Checked against tools/migration/cli.py on the #935 branch (base 3fd2448f). #1071 RDF WP0-c (2026-10-22): an audit reference that is exactly urn:eidos:audit: (empty id) is kept byte-identical and listed under emptyLegacyRefs in id-mapping.json instead of refusing; an audit reference must name a uuid, and an empty urn in @id still refuses. Checked against tools/migration/identity_rewrite.py on the #1071 branch (base 19059cc0). #935 S7 (2026-10-22): the raw data endpoints read the overlay-only effective graph, no .eidos seed. Checked against mutation_store.py at a11d2636. #1102 S8a (2026-10-22): migration import writes every relation in the current eidos:target form with the placeholder uuid explicit as eidos:node, drops the four pre-WP4b keys, makes an IRI-form eidos:source the bare uuid, refuses a ref-without-domain or uuid-less in-model target, and lists every changed field under relationFieldsRewritten in id-mapping.json; the M4b "eidos:source stays literal" sentence removed. Checked against tools/migration/identity_rewrite.py on the #1102 branch (base 538bb015). #989 WP9 S4a (2026-10-23): FME 2.0.0 — every node/relation identity value (world id, hasDomain from/to, node id/parent, relation from/to/source/id/inherited_onto/inherited_from/external_uuid, engineeringInherited source_uid, tombstone id) is its IRI in every mode via export_service._identity_iris on copies the export owns (a placeholder to kept as its IRI, via target_ref.relation_target_iris), after every bare-uuid join and before the fingerprint; audit sections and treeId keep the bare uuid; /internal/reconstruct passes identity_iris=False; tools.migration builds, verifies and imports 2.0.0, the DocuGraph reader is unchanged (S4b). Checked against export_service.py on the #989 branch (base 8e8c8dd3). #1112 WP9 S4b (2026-10-23): the readers accept 2.0.0 — the DocuGraph importer reads it to the same bare uuids as 1.x, tools.migration build refuses an export whose formatVersion is not in SUPPORTED_FME_VERSIONS or that carries a legacy urn node id, an unknown major is a typed refusal naming the version, import accepts 1.7.0 and 2.0.0; checked against documents_import.py, tools/migration/cli.py, builder.py and refcheck.py on archon/issue-1112 at 86bac62b. #1125 #607 S1 (2026-10-25): every FME node row carries the additive eidos:propertyOrigin (own / inherited with the source node IRI), from domain.property_origin via routes.domain_io._flat_export_node, also on a secondary's GET /nodes?full=true rows; eidos:engineering unchanged, fingerprint moves with the content. Checked against domain_io.py on archon/issue-1125 (base b4bead2f). #1125 send-back r1 (2026-10-25): FME 2.1.0 — FORMAT_VERSION bumped for eidos:propertyOrigin and the #1126 inherited-row occurrence_id/occurrence_ref/derivation; documents_import and tools.migration accept 2.1.0, 2.0.0 stays readable. Checked against export_service.py, documents_import.py and tools/migration/importer.py on archon/issue-1125 (base 535c9321).
Frontend Crud Verified — #956 D1b-1 (2026-10-20): 13 finding(s) fixed — EidosMutations.preview/commit replace the absent previewMutation/commitMutation ops and the apiFetch commit samples, loadNodeDetail full signature, caches are plain objects (not LRU), apiFetchAbsolute IRI branch, eidos-api.js routing pins, app.js/detail-panel.js line pins (also the dynamic first-arg baseline 18 → 17 from #1008). Re-verified against main 421c63ba.
Graph Merge Verified — #956 D1b-2 (2026-10-20): 8 finding(s) fixed — graph_merge has no _strip_prefix (it lives in key_derivation and eidos_loader), the caller is eidos_loader.current_graph() not _load(), _build_ref is notation-first via derive_slug_segment (WP2), line pins corrected (graph_merge.py:48/82/144/194-198, eidos_loader.py:373-395/385/397/285), base_graph['relations'] is reassigned unconditionally. Re-verified against main 4d5c9b57. #935 S1 (2026-10-21): select_domain_root added; Pass 2 logs overlay_node_orphaned for a chain that reaches no tree root (attach outcome unchanged); line pins moved (graph_merge.py:214-218, eidos_loader.py:332/422/446). Checked against archon/issue-935. #935 S7 (2026-10-22): the merge starts from empty base dicts; select_domain_root is the root source for every domain since S5a. Checked against graph_merge.py and eidos_loader.py at a11d2636.
Iam Engine Verified — #956 D1b-3 (2026-10-20): 7 finding(s) fixed — the IAM-filtered export is GET /api/export (api_misc_router.export_graph), GET /nodes and GET /node/{id} read through IamScope (filter_readable / can_read), per-route 403 bodies (No access to domain / Access denied / typed P-READ_ACCESS_REQUIRED), ten modules incl. scope.py, write gates live in api/mutations_router.py (_iam_reject_if_locked) and routes/documents.py (_gate_item_write), /admin/iam/* uses require_admin = deps.admin_user, iam_rules_as_of(t, mutations_dir=None). Re-verified against main 9e119ef0. #1120 WP9 S5c (2026-10-23): GET /admin/iam/rules, /admin/iam/domain/{domain}/tree and /admin/iam/users/{subject}/access drop the bare id / parent_id / subtree_root where the IRI companion is set; the domain functions keep their shape. Checked against backend/api/admin_iam_router.py on the #1120 branch (base fc560689).
Import Pipeline Verified — #956 D1b-4 (2026-10-20): 6 finding(s) fixed — the relation-import uuid is derive_overlay_relation_uuid(idem_key) with relation_import_idem_key(file_hash, base_id, rowIdx), shared by router and engine; the on-disk target is a single eidos:target (node IRI / ExternalRef{domain,ref} with no placeholder written / UnresolvedRef{eidos:ref}), toNodeId/unresolvedTargetRef are legacy-read only; no re-keying when an endpoint resolves; parse_excel/normalize_paths still exist (only the routes and excel* helpers were retired); the result status counts committed>0 OR enriched>0 (BL-IE-018 back-fill); the graph engine path is backend/domain/graph_crud_engine.py. Re-verified against main 4996d1ce.
Metric Verified — #956 D1b-4 (2026-10-20): 1 finding(s) fixed — the definition now says only model-scope metrics declare a merge rule (one of MERGES); node-scope metrics take none (CatalogError, catalog.py:88). Re-verified against main 4996d1ce.
Metric Merge Verified — #956 D1a (2026-10-20): re-read at main 58abc0b5 (cited code unchanged at 0481ba39), 0 findings. backend/metrics/engine.py _merge() implements exactly the five rules named by the KM: sum, max, mean (weighted by inputs[0]), dict_sum, none (primary only). catalog.py MERGES tuple matches and @metric enforces mean requires inputs. KM definition matches code.
Metric Scope Verified — #956 D1a (2026-10-20): re-read at main 58abc0b5 (cited code unchanged at 0481ba39), 0 findings. backend/metrics/catalog.py defines SCOPES = ('node', 'model') and the @metric decorator validates scope against it. engine.compute_node iterates node_ids; compute_model computes per-domain and merges — matches the KM definition (per node for a list of ids; per model for one domain then merged).
Metrics Engine Verified — #956 D1b-3 (2026-10-20): 1 finding(s) fixed — GET /activity/heatmap success responses carry no errors key; the audit-unavailable branch adds errors, degraded: true and changesOnRemovedNodes null (server.py:4433-4442). Re-verified against main 9e119ef0.
Mutation Engine Verified — #956 D1b-2 (2026-10-20): 5 finding(s) fixed — build_overlay_relation writes one eidos:target field (WP4b #965), derive_overlay_relation_uuid takes idem_key (WP4 #953; caller graph_crud_engine.py:1535), NAMESPACE_EIDOS/_TRANSIENT_FIELDS live only in key_derivation, build_preview* return (diff, scope), the domain comes from resolve_this_domain() (EIDOS_DOMAIN, THIS_DOMAIN, DOMAIN_KEY). Re-verified against main 4d5c9b57.
Mutation Pipeline Verified — #956 D1b-4 (2026-10-20): 1 finding(s) fixed — mutations_router.py route pins are now node/validate :204, node/preview :224, node/commit :270, node-update/preview :324, node-update/commit :355, relation/preview :414, relation/commit :452 (paths and status codes unchanged). Re-verified against main 4996d1ce.
Mutation Store Verified — #956 D1b-2 (2026-10-20): 3 finding(s) fixed — merge_overlay is called from eidos_loader.current_graph() (no _load since #868 Slice 8), load_overlay logs a WARNING per malformed file instead of skipping silently, remove_node_tombstone is at mutation_store.py:491 (method) and :711 (wrapper). Re-verified against main 4d5c9b57.
Operation Record Engine Verified — #956 D1b-2 (2026-10-20): 1 finding(s) fixed — OperationRecord carries no event-id field; id_from_envelope is used only by record()/record_rejected() to return the appended event's id. Re-verified against main 4d5c9b57.
Overlay Verified — #956 D1b-4 (2026-10-20): 3 finding(s) fixed — the merge caller is eidos_loader.current_graph(); malformed overlay files are logged via load_overlay_{node,relation,tombstone,node_tombstone}_parse_failed (mutation_store.py:357-437) and then skipped; nodes.update at graph_merge.py:152, engineering/productType carry-over 146-151, DomainStore.write_atomic 246-260. Re-verified against main 4996d1ce. #935 S1 (2026-10-21): line pin moved (nodes.update at graph_merge.py:167). Checked against archon/issue-935. #935 S7 (2026-10-22): the page describes the overlay-only model (Effective Graph = overlay minus tombstones; root and WORLD are overlay files); the sparse-patch-on-an-immutable-base model and the Immutability Contract section are marked as history; the write-ahead-layer wording is dropped. Checked against eidos_loader.py and graph_merge.py at a11d2636. #1102 S8a (2026-10-22): an eidos:UnresolvedRef may carry eidos:node (explicit placeholder, never derived), and the loader reads an IRI-form eidos:source as its bare uuid (infra.iri.relation_source_id). Checked against target_ref.py and mutation_store.py on the #1102 branch (base 538bb015).
Overlay Wins On Conflict Verified — #956 D1b-4 (2026-10-20): 2 finding(s) fixed — merge_overlay's sole caller is eidos_loader.current_graph() (eidos_loader.py:385; _load() was removed); nodes.update is cited at graph_merge.py:152 and the overlay_rels filter + concat at 194-199. Re-verified against main 4996d1ce. #935 S1 (2026-10-21): line pins moved (eidos_loader.py:422, graph_merge.py:214-219). Checked against archon/issue-935. #935 S7 (2026-10-22): the guarantee states the empty base since S5a; tools.migration convert does not call merge_overlay but applies the same rule (an existing overlay file is kept, only augmented with eidos:engineering); merge_overlay call pinned at eidos_loader.py:223. Checked against tools/migration/eidos_base_to_overlay.py at a11d2636.
Preview Before Commit Verified — #956 D1b-4 (2026-10-20): 2 finding(s) fixed — the guarantee is scoped to the AddNode/UpdateNode/CreateRelation preview/commit pairs; DELETE node/relation, PATCH heal-uuid and the Excel node/relation/property import commits take no previewId by design; cleanup_stale_previews call at server.py:922-923, docstring at mutation_store.py:224. #1016 (2026-10-20, earlier): the HTTP AddNode preview/commit UUID finding was closed in code (preview and graph_crud_engine.create_node both build cmd with mutation_engine.normalize_add_node_cmd). Re-verified against main 4996d1ce.
Relation Catalog Verified — #956 D1b-3 (2026-10-20): 6 finding(s) fixed — the G4 section notes WP4 #953 derives the relation uuid from the per-row idem_key, get_cardinality is called from effective_relations.py:113 and graph_crud_engine.py:1580 (not server.py), six Hierarchy predicates incl. contains, three comment keys, creation is POST /mutations/relation/preview + /commit (mutations_router.py:414/:452), the primary domain comes from resolve_this_domain() (EIDOS_DOMAIN, THIS_DOMAIN, DOMAIN_KEY). Re-verified against main 9e119ef0.
Reports Engine Verified — #956 D1b-2 (2026-10-20): 3 finding(s) fixed — collect() calls loader.current_graph() (_load() removed, #868), service_token is kept only for signature compatibility (DomainClient owns the service headers), the /api/reports/* routes are defined in api_reports_router.py. Re-verified against main 4d5c9b57.
Rulebook Engine Verified — #956 D1b-3 (2026-10-20): 7 finding(s) fixed — get_relation_rule/is_relation_inheritable/relation_inheritance_overridable take (predicate, node_id=None, ancestors=None) with no direction, load_default seeds documentPropagation with the two seed envelopes (#508), documentPropagation rows are always included by rules_for_node, inter-scope propagation needs tier3, a node-scoped stop rule folds to cancelled without ancestor, for-node resolves via key_derivation.resolve_slug, the file has six top-level keys. Re-verified against main 9e119ef0. #1127 #607 S3 (2026-10-25): a rule-carried signal relation is the product's occurrence (derivation "rule"), addressable through /resolve. Checked against effective_relations.py on the #1127 branch (base 6774a2b6). #1125 #607 S1 (2026-10-25): GET /node/{id} carries eidos:propertyOrigin, one row per group.property key — own, inherited (ancestor IRI) or propagated (rule id + source IRI), own > inherited > propagated; the value lists are unchanged and the rule id is not published on them. Checked against server.py and domain/property_origin.py on archon/issue-1125 (base b4bead2f).
Search Engine Verified — #956 D1b-1 (2026-10-20): 3 finding(s) fixed — Wave A/B are delivery phases and _search_v2_impl returns one _gs_score-sorted list, cross-domain dedup key includes baseURI, GLOBAL_SEARCH_V2 code default is off. Re-verified against main 421c63ba.
Ui Primitives Verified — #956 D1b-1 (2026-10-20): 3 finding(s) fixed — closeAllModals runs each onClose (no focus restore), app.js copies the modal methods from _preEidosCore into a fresh literal (not Object.assign), eidos-mutations.js loads after app.js. Re-verified against main 421c63ba.
View Refresh Verified — #956 D1b-1 (2026-10-20): 3 finding(s) fixed — graphs/documents-list run the graph-refresh/documents-refresh generics (window._eidosRefreshGraph/_eidosRefreshDocuments retired); the engine is live (8 reconcilers + stale badge, ~25 dispatchMutation call sites). Re-verified against main 421c63ba.
Visible Not Accessible Verified — #956 D1b-4 (2026-10-20): 1 finding(s) fixed — enforced_by now lists the Add-Information filter (_visibleDomDefs, app.js:9874) that drops a registry-hidden domain as a new-relation target (Document excepted); resolution of existing relations is unaffected. Re-verified against main 4996d1ce.