Orchestrata Ultimate Decisions
RATIFIED 2026-07-19: the Architecture Ontology (31 node types / 44 relationships) — all eight rulings decided (MR !4): ADR guard+gate immutability; unified project-qualified
Component;Rule→Constraintfold; phase-2 Memgraph semantic twin;ArchitectureDecisiondistinct fromDecision; lazyCloudService+ curated seed; OpenSpec owns theChangekey; converged writer both-phased with a full architecture sync at session start (amended 2026-07-19, backgrounded + ADR-guard enforced). Phase 1 implemented & merged 2026-07-19 (MR !6, merge340f3429): schema extension, unifiedComponent, convergedArchitectureWriter, both-layer ADR guard, reference seed, and the SessionStart full-sync hook — 236/236 phase tests observed, live graph conforms. Phase 2 implemented & deployed 2026-07-19: the ADRs below are liveArchitectureDecisionKG nodes (20 at the 2026-07-19 deploy; later ADRs sync via the SessionStart full-sync hook) (all Accepted, ruling-1 immutability guard active — an Accepted ADR's body on this page must NOT be edited in place; record a new ADR and supersede), and their prose is semantically retrievable from the Memgraph twin (ArchitectureChunk, 20 ADR + 11 design-doc chunks). The SessionStart hook keeps both graphs in step.
ADR categories at a glance (24 ADRs below, grouped by concern)
ADR-001: Memgraph as Semantic Graph Backend
- Status: Accepted
- Date: 2026-07-07
- Context: Need a scalable, performant graph database for RAG, GraphRAG, and semantic graph use cases.
- Decision: Use Memgraph Community Edition.
- Consequences: Bolt protocol reuse via Neo4j driver; native vector search via
vector_search.search; HA built externally via Python controller.
ADR-002: Repository Pattern Over Direct Bolt
- Status: Accepted
- Date: 2026-07-07
- Context: Caller code should not know Bolt/Cypher details.
- Decision: Expose
SemanticGraphinterface withMemgraphSemanticGraphadapter; client discovers MAIN via HA controller. - Consequences: Easy to swap backends; failover transparent to callers.
ADR-003: Single HA Controller Container (Interim)
- Status: Accepted
- Date: 2026-07-07
- Context: Need automatic failover and failback without Enterprise license.
- Decision: Build a Python HA controller that polls nodes, promotes replicas, and reintegrates recovered nodes.
- Consequences: Controller is a temporary SPOF; future work is Raft/3-controller cluster.
ADR-004: ASYNC Replication Default
- Status: Accepted
- Date: 2026-07-07
- Context: Trade-off between consistency and write latency.
- Decision: Default replication mode is
ASYNC. - Consequences: Higher write throughput; potential brief data loss on failover.
ADR-005: Monkeypatch Fully Forbidden Suite-Wide
- Status: Accepted
- Date: 2026-07-08
- Context: Test purity doctrine already banned mocks;
monkeypatch.setenv/delenvremained a grey area contradicting the checker. - Decision: Monkeypatch (including setenv/delenv) is forbidden across the entire test suite; use explicit
env=dicts passed to subprocesses/factories instead. CLAUDE.md Key Rules updated to match the checker. - Consequences: Resolves the checker-vs-docs contradiction; existing violations tracked as P2 remediation.
ADR-006: Governance Wired into PipelineRunner (P0.1)
- Status: Accepted
- Date: 2026-07-08
- Context: The Prolog governance gate existed but was not enforced on the production pipeline path.
- Decision: Wire the governance gate into
orchestrator/pipeline_runner.pyso volk actions passmay_executechecks (after P0.2 injection fix landed first, per the remediation ordering). - Consequences: Governance is live on real pipeline runs; fail-closed behavior (P0.3) follows.
ADR-007: Prolog Atom Escaping (P0.2)
- Status: Accepted
- Date: 2026-07-08
- Context:
prolog_gate.pyinterpolated agent/action/fact strings into Prolog queries with only a naive quote replace — injection risk. - Decision: Add
_escape_prolog_atom()(backslash, single/double quote, newline) and apply it at every interpolation site (may_execute,assert_fact,assert_task_output,assert_open_question,retract_open_question), with an injection-prevention unit test. - Consequences: Governance queries are injection-safe; landed before P0.1 wiring by design.
ADR-008: get_neighbors With depth < 1 Returns Empty
- Status: Accepted
- Date: 2026-07-08
- Context: Semantic-graph
get_neighborshad undefined behavior for non-positive depth. - Decision:
depth < 1returns an empty result rather than raising or defaulting to 1. - Consequences: Callers get a total, predictable function; no exception handling needed for degenerate depths.
ADR-009: Fail-Closed Governance by Default (P0.3)
Date: 2026-07-08 · Status: Accepted
All governance error paths (may_complete, requires_human_consent, constitutional
check, prolog_client queries) fail CLOSED when ORCHESTRATA_GOVERNANCE_STRICT
is unset or true (read at call time via governance/_strict.py). Set the flag
to false for legacy permissive behavior in dev/test.
ADR-010: All Persistence Through Core Factories (D12 P1)
Date: 2026-07-08 · Status: Accepted, enforced
Every Neo4j/Redis client construction routes through core.neo4j_factory /
core.redis_factory (22 call sites migrated). Enforced by a blocking
architecture_checker AST rule — direct GraphDatabase.driver/redis.Redis
calls outside the factories fail CI.
ADR-011: NO_AUTH Sentinel for Unauthenticated Bolt
Date: 2026-07-08 · Status: Accepted
core.neo4j_factory.NO_AUTH means "explicitly no auth" (Memgraph nodes);
auth=None means "unset — fall back to NEO4J_USERNAME/NEO4J_PASSWORD env".
ADR-012: core.protocols Is the Canonical Interface Home (P2)
Date: 2026-07-08 · Status: Accepted
All interface imports use core.protocols; core.interfaces survives only as
a thin re-export shim for legacy test paths. Forward references are
TYPE_CHECKING-only imports from canonical modules (no = Any sentinels).
Companion P2 refactors: gateway start() decomposed into _init_* steps,
LLM provider registration is a data-driven PROVIDER_CONFIGS table with
double-checked-locked manager init, deprecated CXP packet shims live in
cxp/compat.py.
ADR-013: Hybrid RRF Retrieval + TESTS Edges in the Semantic Graph
Date: 2026-07-08 · Status: Accepted, live
Semantic search fuses vector (0.6) and keyword (0.4) rankings via RRF
(k=60) with the vector candidate pool always preserved into the reranker;
module-name and log(prominence) boosts apply. Canonical chunk label is
CodestralChunk. The enrichment layer now includes TestCase/TestModule
entities with 13k TESTS edges, completing spec→requirement→code→test
navigation. Cheap retrieval CLI: scripts/semantic_search.py. QA benchmark:
82% avg coverage, 0/100 low-quality (plans/semantic_graph_qa_results_latest.md;
older snapshots archived).
ADR-014: review_edit Is a Deterministic Fail-Closed Floor
Date: 2026-07-08 · Status: Accepted
Cheetahclaws /edit reviews are answered by U:Cd/U:Qa/U:Ar deterministic
checks (fragment-tolerant AST: syntax, mass-deletion, bare-except,
test-weakening via real assert-statement counting, relative imports, direct
driver construction) — a cheap fail-closed floor, NOT a correctness review.
LLM-backed review remains a possible later upgrade.
ADR-015: No Docker Deployment Work — Incus Is the Runtime
Date: 2026-07-08 · Status: Accepted (standing ruling)
The remediation checklist's P3.3 "fix Docker deployment" is intentionally not implemented: containers run on Incus; docker-compose.yml is kept only as a topology reference.
ADR-016: The Agent Runtime Kernel Is REQUIRED Infrastructure
Date: 2026-07-10 · Status: Accepted (non-negotiable rule)
- Context: The Agent Runtime kernel (
orchestrata_ultimate.agents.runtime_main) is the central coordination process of the system — agents, CXP dispatch, governance, and the pipeline all route through it. Instart_test_services.pythe kernel was classifiedrequired=False. An optional service that is down is short-circuited tostatus="skipped"in_run_onewithout any start attempt, so the kernel was never started and every full-suite report showed⏭️ Kernel … skipped — optional service not started. Kernel-dependent paths then silently no-op, self-skip, or take a fallback while the suite reports green — the actual heart of the product goes untested. This was never a configuration problem:CXP_GRPC_PORTwas set the whole time; the service was simply mis-classified. - Decision: The kernel is REQUIRED infrastructure, never optional. Every
authoritative test-suite run MUST start and gRPC-verify it; a run that skips it
is not a valid run — it is false-green.
start_test_services.py'sSERVICEStable now marks the kernelrequired=True.required=Truemakes_run_onestart it when down (start_kernel→ source.env→uv run python -m orchestrata_ultimate.agents.runtime_main) and verify it by opening a real gRPC channel (verify_kernel) onCXP_GRPC_PORT(read at call time, never hardcoded). If start or verify fails,run_all(...).all_required_okis False,start_test_servicesexits non-zero, andrun_tests.pyaborts the run. The only sanctioned opt-out is explicit and loud —run_tests.py --skip-services kernel— and any such run is non-authoritative and must be labelled so in reports/handoffs. - Consequences: Full-suite runs now genuinely exercise the coordination
heart; the false-green class of report is closed. Never re-flip the kernel to
required=Falseto make a suite pass — that re-introduces the defect for everyone. Recorded in CLAUDE.md / AGENTS.md / LEVEL0 rules anddocs/kernel-is-required.md.
ADR-017: Free-Threaded janus-swi Is the Deployed Prolog Path (Fixed at the C Level)
Date: 2026-07-10 · Status: Accepted, live
- Context: The goal (SPEC-023) is to run the governance / tabling Prolog paths
under Python 3.14t with the GIL genuinely off (not forced via
PYTHON_GIL=0) and zero crashes. Stock janus-swi 1.5.3 broke this two ways under a free-threaded interpreter: (1) single-phasePyModule_Create+PyUnstable_Module_SetGILfailed to suppress CPython's process-wide GIL re-enable, soimport janus_swisilently turned the GIL back on; and (2) once the GIL was off, the FTpy_gil_ensurehad droppedPyGILState_Ensure(), so SWI→Python callbacks fired insidePL_next_solution'sPy_BEGIN_ALLOW_THREADSregion with noPyThreadState→ NULL per-thread mimalloc heap → a deterministic SEGV on the tabling restraint path (ownership_chain/3,max_answers(20), cyclic graph). A third class of rare race appeared once the GIL no longer serialized process-global C state (py_module_table, thedelayeddecref queue). - Decision: Fix the root cause in the extension itself and vendor the result —
do not treat it as an upstream SWI bug to defer, and do not route around
it with an out-of-process MQI subprocess (that path was explored and rejected as
the wrong architecture). The patched fork is vendored at
scripts/_janus_patched/(source only) and wired as a path source inpyproject.toml. Multi-phase init inmod_swipl.cdeclaresPy_mod_gil = Py_MOD_GIL_NOT_USED;py_gil_ensure/py_gil_releaseattach and detach a real tstate exactly as the GIL build does for foreign threads; a recursivepthreadmutex (janus_data_lock) guardspy_module_tableand a mutex-guarded drain protects thedelayedqueue. The package MUST build--no-build-isolationagainst this project's free-threaded interpreter ([tool.uv] no-build-isolation-package = ["janus-swi"]) — an isolated build picks a non-FT backend, leavesPy_GIL_DISABLEDundefined, and compiles the FT-safe declarations out into a GIL-re-enabling.so. - Consequences:
import janus_swi(and theprolog_adapter/ governance Prolog path) now leaves the GIL off with no env var, and the tabling crash is gone (deterministic reproducer 8/8 crash → 0/N on the release-O3uv-built binary). Validate only the-O3binaryuv syncinstalls — an-O0/ASAN build masks bug #2 and is a false green. This makes janus GIL-safe, not the whole process:msgpack._cmsgpack(CXP binary serialization) still re-enables the process-wide GIL on import, so the full CXP/governance suite runs GIL-on while the Prolog-only paths run genuinely GIL-off. Making msgpack GIL-safe is a separate, non-Prolog step. Seedocs/free-threaded-prolog.md.
ADR-018: pytest-xdist Is Banned With a Single Source of Truth
Date: 2026-07-10 · Status: Accepted, enforced
- Context:
pytest-xdist(-n/--numprocesses/--dist) deadlocks this suite ~78% of the time — the tests share real Redis/Neo4j/gRPC infrastructure and free-threaded Prolog, which xdist's process fan-out serializes into deadlock. Despite that, xdist was still declared as a dependency and invoked (CIvalidate_ci.pyeven required-n auto), leaving the ban aspirational rather than enforced. - Decision: Ban xdist and purge it. Drop it from
pyproject.toml; parallelism comes only fromscripts/run_tests.py(batched ThreadPoolExecutor) andscripts/run_tests_threadpool.py. Make.claude/banned-libraries.txtthe single source of truth — add a package there once and it is blocked everywhere — enforced in four layers: (1) theno-xdist.shPreToolUse Bash guard, (2) theno-banned-deps.shPreToolUse guard onpyproject.tomlwrites, (3)code-review.py's BANNED-DEP gate overpyproject.toml, and (4)test-review.pyrules T15/T16 flagging banned imports/usage in test files.health_check.py/agent_check.py/kimi-startup.shwere rewritten onto the threadpool runner (which gained--ignore), andvalidate_ci.pygate 4 was flipped from requiring-n autoto forbidding xdist in CI job scripts. - Consequences: The deadlock class is closed and the ban is mechanically
enforced rather than convention. Adding any future forbidden dependency is a
one-line edit to
banned-libraries.txt; all four layers pick it up.
ADR-019: D13 — Raw Memgraph Coordinator Probe Lives in the Allowlisted HA Seam
Date: 2026-07-10 · Status: Accepted
- Context: MAIN discovery needs a raw-driver
SHOW INSTANCESprobe — a Raft admin query only a coordinator answers over a direct (non-routed)bolt://connection. That raw graph access had leaked intosemantic_graph.main_discovery, a lightweight writer-path module that should stay free of direct driver construction (D13 graph-seam rule). - Decision: Move the single raw coordinator probe into the allowlisted
Memgraph HA seam as
ha_controller.probe_coordinator_for_main(bolt_uri), which opens a best-effortcreate_neo4j(uri=..., auth=NO_AUTH, connection_timeout=5)session, runsSHOW INSTANCES, and returns the MAIN data-instance'sbolt://URI (orNonewhen unreachable / not a coordinator / no MAIN).main_discovery._resolve_via_coordinatorsnow imports and delegates to it lazily (avoiding pulling the controller's FastAPI dependency into the writer-path module) and does only env-driven orchestration. - Consequences: The discovery module keeps no raw graph access of its own; all direct coordinator driver access is concentrated in the one allowlisted HA seam, satisfying the architecture rule while preserving lazy import to keep the writer path lightweight.
ADR-020: The Memgraph Adapter Retries Transient Storage-Access Contention
- Status: Accepted (session 226).
- Context: The semantic-graph runs on a SHARED Memgraph HA cluster whose
periodic snapshots (and competing writers) transiently hold storage access.
A write/read that cannot acquire access within
storage.access_timeout_secis aborted server-side with a retryableTransientError("Cannot get unique/shared access to the storage ... Periodic snapshots also hold storage access").MemgraphSemanticGraph._execute_with_reconnectretried only connection errors and surfaced this one to the caller, so writes failed intermittently under load (e.g.test_memgraph_adapter, 3/24 red). - Decision: Treat storage-access contention as the transient, retryable
condition it is.
_execute_with_reconnectnow classifies the error (byTransientErrortype or the storage-access message markers) and backs off (exponential, capped at 6s, bounded to 8 retries ~= 26s total) retrying the SAME driver — distinct from the connection-error path, which reconnects to a possibly-new MAIN. This is production-correct for ANY client of the shared cluster, not a test-only workaround. - Consequences:
test_memgraph_adapteris reliably green (3/3 fully clean solo). Real ingestion/RAG writes ride through snapshot windows instead of failing. Retries are bounded, so persistent contention still surfaces rather than hanging. Covered by two real-infra tests driving the retry loop via the operation-callable seam (no mocking).
ADR-021: Org-Mode Is Rejected as a Project Format
- Status: Accepted (session 244, user-ratified after adversarial review).
- Context: An investigation (
reports/research-org-mode.md) evaluated adopting org-mode for planning/tracking artifacts. Every render/tooling surface in the stack is markdown-native (Wiki.js, GitLab, the RAG chunker, the session CLI); the Python org ecosystem is read-mostly with no full-fidelity round-trip parser outside Emacs; and org's features duplicate already-wired systems (session CLI backlog, wiki, specs corpus). - Decision: REJECT org-mode. Markdown + the existing session CLI/wiki/specs machinery remain the project formats. This is the fourth ratification of reuse-over-invention (after SPEC-104 OpenSpec, SPEC-105 OWL/SHACL, and the no-new-DSL ruling).
- Consequences: The rationale is now durable and retrievable here (the
RAG evaluation found it previously lived only in the repo's
reports/, invisible to wiki recall). Any future format proposal must displace this ADR explicitly.
ADR-022: Five Prolog Anti-Goals Are Settled Negatives
- Status: Accepted (session 244, user-ratified).
- Context: The Prolog next-impact analysis
(
reports/proposal-prolog-next-impact.md§3) benchmark-falsified or duplicate-ruled five candidate directions. Recording them prevents re-litigation. - Decision: The following are settled NO rulings: (1) no porting Python recursion to Prolog for speed (falsified on all four benchmark workloads); (2) no fixing the in-process reindex ABI; (3) no MQI/socket Prolog service (previously rejected, stays rejected); (4) no re-implementing graph analytics in Prolog (Neo4j GDS owns that); (5) no resurrecting the statement layer.
- Consequences: Prolog investment concentrates on the ratified positive track: CodebaseTools volk wiring + KB lifecycle, then proof-tree explanations merged with governance verdict provenance, then the postman enforcement flip on its named trigger.
ADR-023: The 2026-07-21 Corrected Decision Set Is Ratified
- Status: Accepted (session 244; ratified by the user after an adversarial correctness-first review overturned or sharpened 12 of 23 orchestrator recommendations).
- Context: Six investigation reports produced 23 open decisions. The adversarial review's standing rule was itself ratified: no interim state without owner + trigger + expiry — date-less deferral is the incident root-cause pattern (governance forensics root cause #6).
- Decision: The corrected set is ratified in full. Load-bearing calls:
graded RTM + EXECUTED tier replaces the deleted legacy RTM; CONTRADICTED
traces are flag-only permanently; the trace ratchet is a delta ratchet on
new uncited decorators;
@traces-on-tests is the canonical citation form; intent lexicon and KG constant blocks are generated from single sources; governance deny-by-default flips on the metric trigger (ERROR_ALLOW==0, all POLICY_DENYs attributable) after the fact-base phase; PAMEnforcer is WIRED (SPEC-060 mandates runtime enforcement — kill option closed); gateway gating extends NOW to orchestrator.intent, volk.register/deregister and session.open/close with only cxp.send behind a thresholded benchmark; the wiki RAG interim default is the measured-best no-rerank config pending the 100-query goldset; installer decisions I-D1..I-D6 as corrected (per-tool opt-in routed through the governance gate). Execution registry: repoplans/master-execution-plan.md. - Consequences: The
open-decisionswiki page now holds only genuinely open items (new-spend calls and future triggers). Every ratified item is IN-FLIGHT, NOW, or trigger-named in the master plan.
ADR-024: One Factory Per Neo4j Database (Deployment Contract)
- Status: Accepted (session 253, ruled by Mohamed 2026-07-24 — binding, do
not re-open). Recorded in repo
docs/adr/0004-facts-keyed-by-worktree-commit.mdand at thePromotionStore.gcdocstring (commit8add3728). - Context: SPEC-033 B4a keys build-factory facts by
(worktree, commit); abandoned-worktree GC judges every non-trunkFactNamespacein the graph against the calling factory's livegit worktree list. During B9 verification, a mis-isolated parallel test run put two factory pipelines on one shared Neo4j — each gc deleted the other's in-flight namespaces ("trunk missing C": facts vanished between build and promote). - Decision: Exactly one factory (one repo) owns each Neo4j database.
Isolation is by database, not by key — namespaces carry no repo identity
and gc is deliberately not repo-scoped. The alternative (repo identity on
FactNamespace+ repo-scoped gc) was considered and rejected as unnecessary schema weight under the contract. - Consequences: Parallel test verification of factory code MUST use the
per-worker Neo4j instances (
ORCHESTRATA_WORKER_SLOT=gwN→NEO4J_URI_GWN; bareNsilently falls back to the primary — the failure mode that produced this ruling). Any future multi-factory topology requires revisiting ADR-0004 first.
Session-253 canonical-key enforcement (no new decision — canon applied)
The recurring kg_schema conformance failures were traced to live code
re-creating retired drift on every suite run, and fixed per the already-ratified
canon (commit 17ca5a9a): audit event nodes and orchestrator Fact nodes now
carry the canonical id key; the retired component_name (bare-name) and
redundant project_id constraints no longer ship in schema.py / sync.py
DDL. All four Neo4j instances brought to conformance via
migrate_neo4j_canonical.py. Standing rule reaffirmed: rulings are binding —
gap #4 (enums reconcile-vs-retire) was closed per the recorded W3 ruling rather
than re-surfaced as open.
ADR-025: KG Zero-Orphan Invariant Enforced; D1(b) Executed; Session CLI Refuses Auto-Detected Roots Under Pytest
Date: 2026-07-30 · Status: Accepted
Ratified during the fix-all-defects goal (session 256), closing the hub-link plan's open decisions:
- D1 → option (b) executed. Of the seven generated dogfood "project" slugs, five real
LRU-cache dogfood specs got
:Projecthubs (minted with canonical identityid = slug, linked viaHAS_SPEC); the degenerateimplement-as-specifiedtree (1 Spec + 16 Requirements + 5 UserStories) was deleted — a slugified instruction is not a project identity. The null-slug orphan Spec from the plan no longer existed at execution time. Result:check_kg_connectivityreports 0 unreachable (71,306/71,306). - D2 → whole
:Factpopulation swept. All remaining 66 reachable:Factnodes carriedsource='test'({'i': N} loop fixtures;log_facthas no production caller). The fingerprint(n:Fact AND n.source = 'test')is now a permanent deep-marker clause insweep_test_pollution.py. - D3 → Resolution single-key.
graph_audit._write_eventnow stamps the label's canonical merge key (canonical_schema.CANONICAL_MERGE_KEYS) alongsideidwhenever it differs — Resolution audit events carryresolution_id, aligning the audit writer with the escalation resolver. One label, one identity property. repair_orphan_hub_links.pyis genuinely idempotent. The drift guard accepts a DELETE predicate matching 0 as "already applied" (still refusing any other count), the dry-run reports measured counts (never historical expectations), and spec-bearing hubs are id-backfilled so the sweeper's husk fingerprint (id IS NULL) cannot eat them.- SPEC-023 session-CLI pytest guard.
orchestrata_ultimate.session.cli.mainrefuses an AUTO-DETECTED project root whenPYTEST_CURRENT_TESTis in the environment (explicit--rootandSESSION_CLI_ALLOW_UNDER_PYTEST=1stay allowed). Root cause: the codedirector volk'ssession worksubprocess inherited the test process cwd and appended SKELETON_FLOW fixtures into the LIVE repo'scontext.jsonduring gate runs. The volk also gained asession_cwdseam, and the escape tests now assert the live tracker's bytes are unchanged. The concurrent-operator false-positive ruled out extending the git sentinel to watchcontext.json(an orchestrator legitimately logs session work while a gate runs).
ADR-0039: Ruleset design — one commit per design version (F12)
Date: 2026-08-28 · Status: Accepted
Every version of plans/ruleset-runtime-design.md (folds and amends) is snapshotted and
committed before the next adversarial round; the header version label is the audit key.
Full record: docs/adr/0039-ruleset-design-commit-per-version.md.
ADR-0040: Ruleset traceability contract + one-word approval UX (F13)
Date: 2026-08-29 · Status: Accepted
The trace is part of the plan (SPEC-115/REQ-115 map fixed in advance), totality both
directions via @traces, verification automatic through five named gates; ratification is
one harness-mediated approval in Mohamed's session — zero manual cryptography, durable
no. Full record: docs/adr/0040-ruleset-traceability-contract-and-approval-ux.md.
ADR-0041: Ruleset engine — per-surface immutable versions, named compositions (F1)
Date: 2026-08-29 · Status: Proposed
Rules live in immutable per-surface RulesetVersions; a named Composition pins each
surface to one version; every activation/rollback/denial targets a name. Stable ×7
snapshots, zero findings rounds 13–15, recommended 7 consecutive rounds. Awaiting
Mohamed. Full record: docs/adr/0041-ruleset-per-surface-versions-named-compositions.md.
ADR-0042: Wiring-gate supersession by the ruleset engine's §5 (F4)
Date: 2026-08-29 · Status: Proposed
For the ruleset subsystem, §5's structural reachability (a rule unreachable from an
active composition is dead by construction) supersedes the standalone wiring gate.
Awaiting Mohamed. Full record: docs/adr/0042-ruleset-wiring-gate-supersession.md.
ADR-0043: Rollback window N + process-cap arithmetic (F8-cap)
Date: 2026-08-29 · Status: Proposed
Rollback window = last N ratified generations per name; CAP >= 2×max_pins + rollback
headroom, checked at ratification. Split from F8's deny-all half. Awaiting Mohamed.
Full record: docs/adr/0043-ruleset-rollback-window-and-cap-arithmetic.md.
ADR-0044: Registry entries self-admit via the ratify chain (r12-decision-1)
Date: 2026-08-29 · Status: Proposed
The remedy channel for a compromised projector must not route through the projector:
ratify-signed, chain-ordered registry entries need no AdmissionRecord. Fallback stated.
Awaiting Mohamed. Full record: docs/adr/0044-ruleset-registry-entries-self-admit.md.
ADR-0045: Per-lineage bearer token for submitter continuity (r12-decision-2, refined)
Date: 2026-08-29 · Status: Proposed
Queue-minted 256-bit bearer token, salted-hashed in the minting record; join-only (mint
is never refused); re-mint supersedes = revocation; dies with the lineage. Fallback
priced. Awaiting Mohamed. Full record: docs/adr/0045-ruleset-lineage-bearer-token.md.
ADR-0046: Every ADR lives in three synchronized homes — docs, wiki, graph
Date: 2026-08-29 · Status: Accepted
Mohamed's ruling: "ADR must be in graph, wiki and docs." docs/adr is the file of record,
this page is the human register, ArchitectureDecision nodes are the graph home;
check_adr_graph_sync.py is the standing three-way drift gate.
Full record: docs/adr/0046-adr-three-synchronized-homes.md.
ADR-0047: Two ADR registers, string-significant ids — namespace, renumber nothing
Date: 2026-08-29 · Status: Accepted
Mohamed's ruling (option 1) on the id collision ADR-0046 exposed: ids match as
zero-padded strings — ADR-0001 (factory register, docs/adr) and ADR-001 (project
register, this page) are different ids. register becomes a graph-node property; the
gate matches (register, padded id); nothing renumbers, every citation stays valid; the
three-homes rule completes by mirroring both series.
Full record: docs/adr/0047-adr-two-registers-string-significant-ids.md.
ADR-0048: A ratified ADR is sufficient design authority for bounded implementation
Date: 2026-08-29 · Status: Proposed
RUL-022's DESIGN_WAIVER, generalized: work whose entire scope executes an Accepted ADR
cites DESIGN_WAIVER: <ADR id> instead of a contract file that would restate the ADR;
scope beyond the ADR loses the waiver. Exercised once (ADR-0047 implementation).
Awaiting Mohamed. Full record: docs/adr/0048-ratified-adr-is-design-authority.md.
ADR-0049: Adversarial-review evidence is repo-committed and enriches wiki + graph
Date: 2026-08-29 · Status: Proposed
Review evidence gets the decisions treatment: every round's lens reports + verifier
files committed to plans/reviews/docs/adr/0049-review-evidence-three-homes.md.
ADR-0050: Amend F13(D) — target semantics, not current-CLI claims; correct the payload
Date: 2026-08-29 · Status: Proposed
R16-4 (HIGH): F13(D)'s ratified ratification-interface clause describes today's CLI
doing things it does not do, and enumerates a signed payload missing seven fields.
Proposed in-place amendment by its ratifier: D's steps are the SPEC-115 TARGET (the
existing verbs are the UX pattern only), the payload becomes the full C-6 signed
pointer payload, a rollback verb joins the set. Sole criterion-1 blocker on four §11
items. Awaiting Mohamed. Full record: docs/adr/0050-amend-f13d-target-semantics.md.
ADR-0001: Enforcement boundary is committed worktree state, not tool interception
Date: 2026-07-23 · Status: Accepted
The enforcement boundary is the committed worktree state.
Full record: docs/adr/0001-enforcement-boundary-committed-worktree-state.md.
ADR-0002: Factory gates fail closed; interactive sessions stay fail-open
Date: 2026-07-23 · Status: Accepted
Two explicit modes.
Full record: docs/adr/0002-factory-gates-fail-closed.md.
ADR-0003: Neo4j is the fact system of record; Memgraph deferred
Date: 2026-07-23 · Status: Accepted
Neo4j is the system of record for facts, now.
Full record: docs/adr/0003-storage-neo4j-system-of-record.md.
ADR-0004: Facts are keyed by (worktree, commit); merge is fact promotion
Date: 2026-07-23 · Status: Accepted
All AST/trace facts are keyed by (worktree, commit).
Full record: docs/adr/0004-facts-keyed-by-worktree-commit.md.
ADR-0005: Prolog is a stateless reasoning view; the graph is the sole fact store
Date: 2026-07-23 · Status: Accepted
The knowledge graph is the single system of record for facts.
Full record: docs/adr/0005-prolog-is-a-stateless-reasoning-view.md.
ADR-0006: Projection is rule-driven, with a completeness meta-check
Date: 2026-07-23 · Status: Accepted
The projection is generated from the rules, not chosen by hand.
Full record: docs/adr/0006-rule-driven-projection-with-completeness-check.md.
ADR-0007: Traces are structurally validated, never model-declared
Date: 2026-07-23 · Status: Accepted
A trace is valid only if both hold, checked structurally: 1.
Full record: docs/adr/0007-traces-are-structurally-validated.md.
ADR-0008: The determinism boundary — gates check structure and transcription, not intent
Date: 2026-07-23 · Status: Accepted
State the boundary explicitly in the spec, and design to it: - Deterministic: existence, public signatures, cross-component dependency edges, complexity thresholds, test structure, coverage, trace validity (ADR-0007)...
Full record: docs/adr/0008-determinism-boundary-structure-not-intent.md.
ADR-0009: Asymmetric amendment — tightening needs quorum, loosening needs the human
Date: 2026-07-23 · Status: Accepted
Amendments are asymmetric: - Tightening (the amended rulebase accepts strictly less) — quorum = 2 suffices.
Full record: docs/adr/0009-asymmetric-amendment-tighten-quorum-loosen-human.md.
ADR-0010: Constitutional invariants require stratified negation
Date: 2026-07-23 · Status: Accepted
Constitutional invariants MUST NOT depend, via negation-as-failure, on any extension_point/1 predicate — i.e.
Full record: docs/adr/0010-constitutional-invariants-require-stratified-negation.md.
ADR-0011: Detect incompleteness rather than prove completeness
Date: 2026-07-23 · Status: Accepted
Replace the completeness proof with a battery of incompleteness detectors, run at design time rather than discovered mid-build.
Full record: docs/adr/0011-detect-incompleteness-not-prove-completeness.md.
ADR-0012: Semantic anchors are requirement-derived, not code-derived
Date: 2026-07-23 · Status: Accepted
The ratified artefact carries requirement-derived semantic anchors — constraints on behaviour that do not derive from the implementation, and so cannot be co-authored by the code: - Concrete input→output examples per...
Full record: docs/adr/0012-semantic-anchors-are-requirement-derived.md.
ADR-0013: Anchors are constitution-tier and human-only; anchor edits are loosening
Date: 2026-07-24 · Status: Accepted
- Anchors are constitution-tier: human-authored/ratified only, stored with the gate assets under the OS trust boundary (ADR-0018), never writable from a worktree.
Full record:
docs/adr/0013-anchors-are-constitution-tier.md.
ADR-0014: Adjudications are promoted into the Project KB (detector memory)
Date: 2026-07-24 · Status: Accepted
Adjudicated assumptions and ratified anchors are promoted into the Project KB as reusable pinning artefacts, and the detectors consult them.
Full record: docs/adr/0014-adjudications-promoted-into-project-kb.md.
ADR-0015: DoR is necessary-not-sufficient, with a human floor and forced-choice axes
Date: 2026-07-24 · Status: Accepted
- DoR = no detector fires AND a positive, human-ratified floor.
Full record:
docs/adr/0015-dor-necessary-not-sufficient-forced-choice-axes.md.
ADR-0016: Anchor classes are ranked; differential oracles are mandatory for amendments
Date: 2026-07-24 · Status: Accepted
- Rank the classes by independence: differential oracle > property/metamorphic > example.
Full record:
docs/adr/0016-anchor-class-ranking-oracles-for-amendments.md.
ADR-0017: Governed writers are (binary, config)-attested pure functions, re-executed trusted-side
Date: 2026-07-24 · Status: Accepted
- A registered writer's identity is the pair (binary-hash, config-hash), not a name.
Full record:
docs/adr/0017-governed-writers-are-attested-pure-functions.md.
ADR-0018: Three-uid execution model (agent / sandboxed executor / trusted gate)
Date: 2026-07-24 · Status: Accepted
Three distinct uids, none of which may impersonate another: 1.
Full record: docs/adr/0018-three-uid-execution-model.md.
ADR-0019: Gate reads require causal consistency — snapshot + watermark, not replicas
Date: 2026-07-24 · Status: Accepted
- Gate reads must be causally consistent with the commit under evaluation.
Full record:
docs/adr/0019-gate-reads-require-causal-consistency.md.
ADR-0020: Amendment direction — syntactic sufficient condition, else the human
Date: 2026-07-24 · Status: Accepted
Direction is decided fail-closed, by a three-step cascade: 1.
Full record: docs/adr/0020-amendment-direction-syntactic-then-human.md.
ADR-0021: Edge accounting over the transitive closure; boundary changes are loosening
Date: 2026-07-24 · Status: Accepted
- Component-boundary changes (merge/split/move) are loosening by definition — human ratification, never quorum-only, regardless of how the edge count moves.
Full record:
docs/adr/0021-edge-accounting-over-transitive-closure.md.
ADR-0022: Interpretation mutation must be adversarially separated; promote the model-free detector
Date: 2026-07-24 · Status: Accepted
- Anchors are ratified and frozen before C2 runs.
Full record:
docs/adr/0022-c2-independence-and-promote-c4.md.
ADR-0023: The fast path requires anchors satisfied and unchanged
Date: 2026-07-24 · Status: Accepted
A change qualifies for the fast path only if, in addition to the structural within-contract proof: - the anchors of every touched acceptance criterion remain satisfied, and - those anchors remain unchanged.
Full record: docs/adr/0023-fast-path-requires-anchors-unchanged.md.
ADR-0024: The assumption-matching arbiter and its independence guarantee
Date: 2026-07-24 · Status: Accepted
A three-stage cascade, with the model used only where its high recall is an asset: 1.
Full record: docs/adr/0024-assumption-matching-arbiter.md.
ADR-0025: The governance rulebase bans dynamic predicate construction
Date: 2026-07-24 · Status: Accepted
In the governance rulebase (constitution, patterns, project rules — not application code): - Ban call/N on unbound goals and any dynamic goal construction.
Full record: docs/adr/0025-ban-dynamic-predicate-construction.md.
ADR-0026: A governance predicate may not return a truncated answer set
Date: 2026-07-24 · Status: Accepted
No governance predicate may return a truncated answer set.
Full record: docs/adr/0026-no-truncated-answers-in-governance-predicates.md.
ADR-0027: Retain Prolog as the reasoning engine
Date: 2026-07-24 · Status: Accepted
Retain SWI-Prolog.
Full record: docs/adr/0027-retain-prolog-as-the-reasoning-engine.md.
ADR-0028: Completeness axes are delivered as a construction procedure, not a checklist
Date: 2026-07-24 · Status: Accepted
The completeness axes are retained in full, and are delivered as a construction procedure.
Full record: docs/adr/0028-completeness-axes-are-a-construction-procedure.md.
ADR-0029: The lexical incompleteness detector set is tiered by measured precision
Date: 2026-07-24 · Status: Accepted
REQ-033-C3's detector set is specified explicitly and tiered by measured precision, with each rule carrying its provenance and its measured accuracy where one exists.
Full record: docs/adr/0029-lexical-detector-set-tiered-by-measured-precision.md.
ADR-0030: Provenance uses in-toto / SLSA attestations rather than a bespoke format
Date: 2026-07-24 · Status: Accepted
Adopt in-toto attestations as the provenance format, and SLSA as the level vocabulary.
Full record: docs/adr/0030-provenance-uses-in-toto-slsa-attestations.md.
ADR-0031: Every gate decision carries its proof tree
Date: 2026-07-24 · Status: Accepted
Every governance decision carries its proof tree, produced by a Prolog meta-interpreter, and the tree is a first-class artefact: - Allow carries the derivation: rules fired, facts consumed.
Full record: docs/adr/0031-gate-decisions-carry-proof-trees.md.
ADR-0032: The gate adopts the admission-control contract
Date: 2026-07-24 · Status: Accepted
Adopt the admission-control contract explicitly: 1.
Full record: docs/adr/0032-gate-adopts-the-admission-control-contract.md.
ADR-0033: No agent merges its own work; dispatch gates on duplication and staleness
Date: 2026-07-24 · Status: Accepted
Full record: docs/adr/0033-no-agent-merges-its-own-work.md.
ADR-0034: Governed-action classification is property-derived, not name-enumerated
Date: 2026-07-24 · Status: Proposed
An action's category membership is DERIVED from typed attributes of the action and the resources it targets, never from the action's atom name.
Full record: docs/adr/0034-classification-is-property-derived-not-name-enumerated.md.
ADR-0035: Authorization is a scoped grant matched to the action instance, from two sources
Date: 2026-07-24 · Status: Proposed
An action is authorized iff some grant's selector matches THIS action instance's attributes.
Full record: docs/adr/0035-scoped-authorization-two-grant-sources.md.
ADR-0036: Classification facts are stamped at creation under the trusted boundary
Date: 2026-07-24 · Status: Proposed
A resource's classification attributes are written by its CREATOR at creation time, under the trusted-side identity, and are immutable to the acting (agent) identity.
Full record: docs/adr/0036-classification-provenance-under-the-trust-boundary.md.
ADR-0037: Idempotent KG event ids are SHA1 content digests
Date: 2026-07-22 (decided by U:Ar in a live pipeline run); recorded here 2026-07-26 · Status: Accepted
Idempotent event ids are derived from a SHA1 digest of the event's identifying content, truncated to 16 hex chars, embedded in a typed, readable prefix: - Pipeline artifact...
Full record: docs/adr/0037-idempotent-kg-event-ids-are-content-digests.md.
ADR-0038: The CXP envelope schema supersedes the pre-CXP Postman schema
Date: 2026-07-06 (the decision, as executed) · first written 2026-08-19, corrected same day · Status: Accepted
postman.types.MessageEnvelope's CXP-native shape (to/from_/thread_id/subject/ created_at) is canonical, and always has been — it is not a decision this ADR is recording the making-of; it predates commit...
Full record: docs/adr/0038-cxp-envelope-schema-supersedes-postman-types.md.
ADR-0051: F13(A) decomposition map made total — buckets for §0, §1, §10
Date: 2026-08-29 · Status: Proposed
Round 16 (life-R16-9): F13(A)'s map buckets §2–§9 + clause D and omits §0 Doctrine, §1 Domain model and §10 Logic/data boundary — clauses B+C then block BOTH branches for those sections. Proposed: three new buckets (7xx doctrine · 8xx domain model · 9xx logic/data boundary) so "every future id is anticipated" is true. Full record: docs/adr/0051-f13-decomposition-map-totality.md
ADR-0052: F13(C) bindings — wire the five unbound gates (recommended) or amend the clause
Date: 2026-08-29 · Status: Proposed
Round 16 (life-R16-7): of F13(C)'s eight named verification bindings, 2 are accurate, 1 partial, 5 false or unbound (check-traces is a net-count ratchet at --max 2219; trace-audit/trace_intel/spec-authority/factory B2+B3 bind to no failure point). Recommended option 1: WIRE them as named + a SPEC-115-scoped --max 0 lane — ratified text untouched. Option 2: amend the clause to the actual homes. Mohamed's binding condition on option 1 (2026-08-29, verbatim in the full record): gate functionality that does not exist is PLANNED, REVIEWED, DESIGNED and BUILT — never wired as a config flip. Full record: docs/adr/0052-f13-verification-bindings-wire-or-amend.md
ADR-0053: Infrastructure is guilty until proven live — probe, never trust
Date: 2026-08-30 · Status: Accepted (Mohamed's ruling, by fiat)
Mohamed deliberately deleted the entire prod orchgraph stack; for days every doc, gate and report kept asserting it existed — nothing probed it. Law: he may destroy any environment without notice; infrastructure claims carry live probe evidence or are violations; docs asserting dead infra are defects; graphs are derived caches of git-rooted truth, never a durable "home". A liveness gate is proposed work under the ADR-0052 build discipline. Full record: docs/adr/0053-infrastructure-guilty-until-proven-live.md
ADR-0054: Misplaced infrastructure seams relocate to a new L1 infra package
Date: 2026-09-21 · Status: Accepted (executed under Mohamed's direction)
The measured cluster of L3→L4 upward violations traced to misplaced modules, not bad consumers: orchestrator/prolog_bridge.py and orchestrator/redis_state.py are infrastructure. New src/orchestrata_ultimate/infra/ package (Layer 1, registered in LAYER_ASSIGNMENTS + standards doc) receives them — final-granularity placement mapping 1:1 onto the target orchestrata.infra. prolog_worker relocation queued (obsoletes a KNOWN_VIOLATIONS waiver).
Full record: docs/adr/0054-infra-package-for-misplaced-seams.md
ADR-0055: volk_registry lives in the agents layer; L0 protocols never name concretes
Date: 2026-09-21 · Status: Accepted (executed under Mohamed's direction)
The agent registry moved kernel/ → agents/ (its domain layer), resolving the map-vs-plan contradiction in the plan's favor. Rule: L0 protocol modules import no concretes, even under TYPE_CHECKING — core/protocols/volk_health.py now depends only on the structural VolkRegistryProtocol/VolkHealthProbe. Precedent for K6 (core purity).
Full record: docs/adr/0055-volk-registry-agents-layer.md
ADR-0056: Two-gate architecture measurement — runtime gate + static ratchet with a pinned baseline
Date: 2026-09-21 · Status: Accepted (executed under Mohamed's direction)
architecture_checker.py --strict (top-level, 0 violations) is joined by scripts/architecture_static_check.py (every edge incl. TYPE_CHECKING + function-scoped, plus Tarjan cycles) with a pinned baseline JSON (100 total / 6 SCCs at pinning) as the authoritative debt ledger and a blocking architecture-ratchet CI job. Debt can only decrease; the review's 141/10-cycle figures (deleted-branch snapshot) are superseded.
Full record: docs/adr/0056-static-ratchet-baseline.md
ADR-0057: PEP 649/749 annotation convention — Any + named comment, never TYPE_CHECKING-only names in evaluable annotations
Date: 2026-09-21 · Status: Accepted (executed under Mohamed's direction)
Under Python 3.14 lazy annotation evaluation, TYPE_CHECKING-only names NameError when inspect.signature resolves the annotation. Where a type can't be imported at runtime (layer boundary or import side-effect cost like janus), annotate Any and name the real type in an adjacent comment with the reason. Three live instances fixed (volk_registry.audit, prolog_worker.query_explained ×2), surfaced by the regenerated generated-tests pins.
Full record: docs/adr/0057-pep649-annotation-convention.md
ADR-0058: HA controller tests run against a dedicated two-node orchgraph sandbox, never the shared test node
Date: 2026-09-22 · Status: Accepted (ruled by Mohamed 2026-09-22)
Six test_memgraph_ha tests register an unreachable placeholder replica on the shared test orchgraph (10.53.2.10); the Debug-build fork then asserts in ShowReplicas (replication_handler.cpp:449) and core-dumps, and the registration persists across restarts (two crashes on 2026-09-22, 5 red tests, 1 reconnect log line per second). Proposed: two disposable orchgraph containers in orchestrata-test as TEST_MEMGRAPH_HA_MAIN_URI / TEST_MEMGRAPH_HA_REPLICA_URI, the fixture uses a real replica and restores both nodes at teardown, the shared node is repaired once and never mutated again. Fork fix is a follow-up.
Full record: docs/adr/0058-ha-tests-dedicated-orchgraph-pair.md
ADR-0059: REQ propagation in generated tests is uncapped — traceability is not sampled
Date: 2026-09-22 · Status: Accepted (ruled by Mohamed 2026-09-22)
EnhancedTestGenerator._extract_traces used to truncate a symbol's @traces(reqs=[...]) to reqs[:3] at PARSE time, so a fourth requirement was gone before anything downstream could see it and showed up in the RTM as untested (measured casualty: REQ-004-US-009-017). Commit f260b6e73 (IR-1 item G-3) removed the cap; one old-tree test still asserted it. Ruled: REQ propagation is uncapped — every declared id reaches the generated test, in order, with no maximum and no silent truncation. The test asserting a cap of 3 asserted a defect and is corrected to the contract.
Full record: docs/adr/0059-req-propagation-is-uncapped.md
ADR-0060: The RUL-032 content key covers every edge the selector routes on, not only imports
Date: 2026-09-22 · Status: Proposed (awaiting Mohamed's ruling)
RUL-032 defines a test module's "code state" as its own content, its transitive src/ import closure, its conftest chain and pyproject.toml — "nothing else is in the key". Built literally, the first probe showed tests/scripts/test_trace_intel.py keying on everything except scripts/trace_intel.py: the test loads its subject via importlib.util.spec_from_file_location, so there is no import edge, and the file is outside src/ anyway — both restrictions fail here independently. The same day, two real defects in that subject were fixed (95d375eb4, c28e70909) and took the module from 3 failed to 0; under the literal key neither fix would have moved the key, and the ledger would have reported the module proven while it was red. The hole covers tests/scripts/**, tests/claude/** and every .sh/.pl-subject test. Proposed: clause 2 becomes every first-party file (src/, scripts/, claude/) reached by the same four edge sources affected_tests.py routes selection on — imports, the test_<stem> convention, the manifest, reference edges — and the safety property is restated as "every file the selector routes to a test is one of that test's key inputs", asserted by TestSelectorAgreement against the live selector and the live snapshots. The amendment is monotone: it only ever ADDS key inputs, so it can forgo reuse but never fabricate it — which is why it was recorded as Proposed and flagged rather than blocking a ruling Mohamed had already given.
Full record: docs/adr/0060-rul-032-key-covers-the-selector-not-only-imports.md
ADR-0061: The one ADR-body bypass also covers an empty factory body, not only a register-less node
Date: 2026-09-22 · Status: Proposed (awaiting Mohamed's ruling)
sync_adr_docs was writing NOTHING into the graph: it raised AdrImmutabilityError on ADR-0039 and aborted. That node held the WIKI shape — a 243-character decision, with context/alternatives/consequences all empty — because a pre-filter sync_adrs had upserted it from the mirror prose, stamping register = 'factory' on the way in and so putting it permanently beyond migrate_legacy_adr_registers' WHERE a.register IS NULL gate, while Accepted status locked the docs ingest out of correcting it. A live census found 20 stub-bodied nodes, four of them Accepted factory ADRs (0039, 0040, 0046, 0047). The cost was not four stuck records: the ingest raises on the FIRST such node, so 0 of 55 docs ADRs were reaching the graph — the factory register's third home (ADR-0046) had been silently broken for everything numbered 0039 and above, including ADR-0059 and ADR-0060 minted the same day. Proposed: widen the migration's predicate to also select a factory-registered node whose body is entirely empty, re-sourcing it from the docs file of record (ADR-0047) — never re-keying it, and skipping it when the docs record is itself bodiless. The justification is that an empty body was never the ratified content of an Accepted ADR, so filling it completes a stalled migration rather than editing a decision. A scripts/repair_*.py one-shot was rejected because it would create a SECOND site writing an Accepted body outside ruling-1's guard; this keeps exactly one, with a wider predicate. Result: 12 stubs re-sourced, 55 docs ADRs landed, ADR-0059 and ADR-0060 now OK in sync. Not fixed: ADR-0054..0058 exist only on the wiki with no docs/adr/ file and remain MISSING_FROM_GRAPH — an ADR-0047 question, not a repair.
Full record: docs/adr/0061-adr-immutability-bypass-covers-empty-factory-bodies.md
ADR-0062: Production runs from a pinned-ref git checkout under /opt/orchestrata
Date: 2026-09-23 · Status: Proposed (awaiting Mohamed's ruling)
RUL-027 named ~/prod/orchestrata-ultimate as the production checkout; measured 2026-09-23, ~/prod does not exist on this host and never ran anything here (ADR-0053: a document asserting dead infrastructure is a defect), while the test container app already runs a git-archive copy at /opt/orchestrata under system units — host and container disagreed about the prod root. Rulings R1+R5 (Mohamed, 2026-09-23): /opt/orchestrata is a mohamed-owned git checkout reset --hard to the pinned sha by deploy/install-orchestrata.sh, with its own .venv on python3.14t, VERSION staged as .VERSION.deploying and promoted last, .env installed 0600, and an identity verifier (scripts/verify_deployment_identity.py) that refuses success unless the running units are that checkout (HEAD == VERSION.commit == expected sha; every unit's cwd/cmdline/exe under /opt/orchestrata; no unit path under /home/*/workspace/). Checkout rather than archive so promote_env's tree/drift/rollback stages are reused and rollback is a reset, not a re-install; the container keeps its archive copy as the one documented divergence. Units stay user scope this round with scope resolved per unit, so system units with User=mohamed are a later unit-file change. Rejected: keeping ~/prod (three shapes across dev/test/prod, a mutable HEAD as the only record) and an archive copy for prod (forks the promotion path). Supersedes RUL-027's path clause only; also records R3 (deploy assets cherry-picked from architecture/domain-model, never merged) and R4 (the rehearsal HA cluster re-IPs onto 10.53.2.x) without separate ADR ids.
Full record: docs/adr/0062-prod-is-a-pinned-checkout-under-opt-orchestrata.md
ADR-0063: The Read tool is allowed on a module the session already read through the AST toolkit
Date: 2026-09-23 · Status: Accepted (ruling R9, Mohamed, 2026-09-23)
The 2026-08-02 mandate ("ZERO hand-reading of Python source — the AST toolkit serves everything") is enforced by .claude/hooks/force-ast-toolkit.sh, which denies the Read tool and every text tool on this repo's src/, scripts/ and tests/ Python. Edit and Write refuse a file the session has not Read, so measured 2026-09-23 on two swarm tasks (U-T3, U-T8a) an agent that had pulled the exact bytes through source could not land its patch by any route a sibling guard permitted; the method that grew up instead (build in the scratchpad, diff against git show HEAD:<path>, copy over) was a route around a guard — the failure mode the guard's own header warns about. Ruling R9: the Read tool is permitted on a Python file whose module the same session has already read through serialize_codebase_ast.py source <name>. Mechanism (RUL-014, orchestrata-ultimate 1395825c4, shipped 7b6ecb0f0): the hook's Bash branch records every source <name> argument into a per-session memo (${XDG_RUNTIME_DIR:-/tmp}/orchestrata-project-hooks/<session>.ast-read-allow, keyed by the payload's session_id); the Read branch allows a .py whose stem is a dotted segment of a recorded name; text tools stay denied; a payload without a session id records and unlocks nothing (47 guard tests unchanged, 8 new). Rejected: exempting tests/ — it would have restored hand-reading for the largest part of the tree and left src//scripts/ deadlocked. Consequence recorded: the plugin copy goes live only with a ship, so a hook change is sequenced before a swarm wave, never during one. Wiki ruling page: RUL-033.
Full record: docs/adr/0063-read-allowed-after-ast-toolkit-source.md
ADR-0064: The deployed orchgraph package carries MAGE so the ratified pagerank SLO is measurable
Date: 2026-09-23 · Status: Accepted (ruling R13, Mohamed, 2026-09-23: "r13, we had our own orchgraph build")
The migration plan's Step 3.4 SLO "whole-graph MAGE analytical pass < 1 s" is measured with CALL pagerank.get() against the production coordinator, but the package deployed on every node (3.11.0-40-g83b722c-dirty, 2026-07-21) ships no MAGE pagerank module, so tests/scripts/test_benchmark_code_intel.py is red and every full-suite ship is refused. Proposed: rebuild the orchgraph package from the MAGE-bundling commit (fdf6dd8 or later) and deploy it to the test node and orchgraph-coord-1; the test is neither skipped nor weakened meanwhile.
Full record: docs/adr/0064-orchgraph-package-carries-mage-for-the-pagerank-slo.md
ADR-0065: The production gateway's WS control plane binds loopback through the committed prod profile
Date: 2026-09-24 · Status: Accepted (ruling R15, Mohamed, 2026-09-24: "1, proceed with that")
Ship attempt 5's promote failed at services because the gateway's only GATEWAY_HOST was the workspace template's CHANGEME. env/prod.env now sets GATEWAY_HOST=127.0.0.1 explicitly, mirroring env/test.env; the HTTP observability surface keeps its own GATEWAY_HTTP_HOST. A test pins the prod value.
Full record: docs/adr/0065-prod-gateway-binds-loopback-through-the-committed-profile.md
ADR-0066: /opt/orchestrata secrets live in ~/.config/orchestrata/.env and the installer never defaults to the workspace template
Date: 2026-09-24 · Status: Accepted (ruling R16, Mohamed, 2026-09-24: "1, proceed with that")
The installer's --secrets-env defaulted to the 56-placeholder workspace .env, so the first bootstrap shipped placeholders and U-BOOT3 now refuses them. The secrets file is ${XDG_CONFIG_HOME:-$HOME/.config}/orchestrata/.env (0600, secrets only, no config keys, no placeholders); the installer defaults to it and refuses an absent file. The conversation-graph endpoint keys live there per the consumer's contract; their value is still an open operational parameter (R16b).
Full record: docs/adr/0066-opt-orchestrata-secrets-live-in-the-user-config-env.md
ADR-0067: Developer daemons are deployed code; the checkout they serve is a runtime parameter
Date: 2026-10-05 · Status: Accepted (rulings R1/R2 of plans/ast-daemon-deployment-plan.md, Mohamed, 2026-10-05)
The AST daemon and the codemod daemon ran as plain user units executing a developer checkout's code and venv, outside the repo, with no recorded commit, no installer, and no identity check — deploy-in-place in three forms. Both are now deployed code under /opt/orchestrata-tools (own venv, VERSION commit, install-tools.sh from a pinned ref); the served checkout is the systemd instance %I / --repo-root parameter, i.e. data. No daemon process executes from a workspace checkout under any condition; the session hook replaces one that does, and without the deployment the client takes the slow path. Identity (code_root, deployed_commit) is asserted by the installer and the hook; RUL-027's gate extends to ~/.config/systemd/user. The U-T2 "runtime-only" compromise is retired: an enabled instance for the primary checkout is permitted. The codemod daemon rides the same wave (R2).
Full record: docs/adr/0067-dev-daemons-are-deployed-code-serving-checkouts-as-data.md
ADR-0068: SPEC-110 in-memory review checks run on the ctok lossless layer and CPython ast, not LibCST
Date: 2026-10-06 · Status: Accepted (ruling, Mohamed, 2026-10-06: "go with recommendatio . build s3'" — option 1; REQ-110-034 amended in the spec the same commit)
SPEC-110 REQ-110-034 requires the 40 in-memory review checks to run on LibCST trees as CSTVisitor subclasses, but LibCST is gone from the tree (0 hits in pyproject.toml/uv.lock, 0 imports) since the ctok migration superseded REQ-110-107, so FR-012 is unmeetable as worded and unbuilt. Proposed amendment: checks operate on the engine's in-memory lossless layer (ctok token array + the CPython ast overlay the forest holds), never re-parse from disk, never subprocess or call external linters; structural checks are ast.NodeVisitor subclasses over the MUTATED forest, lexical checks (line length, whitespace, tabs, final newline, comment/pattern scans) are token-array scanners, and the 22 existing verification/review/check_* functions are reused where they match. Rejected: re-adding LibCST, dropping FR-012, shelling out to linters.
Full record: docs/adr/0068-spec-110-review-checks-run-on-ctok-and-cpython-ast-not-libcst.md
ADR-0069: Every spec's Definition of Done includes @traces annotations, RTM regeneration, documentation, wiki update and ≥90% coverage
Date: 2026-10-06 · Status: Accepted (ruling, Mohamed, 2026-10-06: "do not forget annotations, rtm, documentation, wiki update and 90% code coverage" then "it should br the definition of done for all specs")
The written DoD named only ≥90% coverage; the other four obligations were scattered or unstated, and SPEC-110 measured the result on 2026-10-06 (uncited working code, code citing undefined ids, a checklist row marked complete for absent code, a design built but never documented as wired). Ruling: every packet of every spec is DONE only when, in the same ship as the code, (1) every added/changed symbol and test carries @traces with spec-defined ids, (2) the RTM is regenerated and the packet's ids move to an implemented-and-tested proof class, (3) the spec checklist, docs/ page and plans/ design status are updated in the code commit, (4) the knowledge-wiki subsystem page gains the packet summary and is shipped, (5) ≥90% line coverage per touched source file. Builder briefs list the five under DONE WHEN; reviewer GO needs evidence for each. Wiki ruling RUL-034; AGENTS.md § Definition of Done extended. Enforcement: judgment until a gate exists (candidates named in the record).
Full record: docs/adr/0069-definition-of-done-for-every-spec-traces-rtm-docs-wiki-90-coverage.md
ADR-0070: Hooks live at the user level only and scope themselves to the project in cwd, so they apply to every project
Date: 2026-10-06 · Status: Accepted (ruling, Mohamed, 2026-10-06: "hooks at user level only but apply to the project in cwd,soo they apply to all projects, add it to the rules")
Claude Code reads <repo>/.claude/settings.json only when the session's project directory IS that repo; measured 2026-08-23, a $HOME-rooted session never loaded it and all 25 guards registered there were dark. Delivery moved to the orchestrata plugin, whose _dispatch.sh scopes each guard to the repo owning the cwd, but both homes were kept: measured 2026-10-06, .claude/settings.json still registers 31 hooks (PreToolUse 15, SessionStart 8, Stop 5, PostToolUse 2, UserPromptSubmit 1), the plugin's hooks.json 42, and a stand-down branch in the dispatcher decides which copy runs from CLAUDE_PROJECT_DIR. The P4 review of the AST-daemon hook hit the consequence: the same hook registered twice, run location-dependent. Ruling: hooks are registered at the USER level only (~/.claude/settings.json, ~/.claude/hooks/, the plugin's hooks/hooks.json) and resolve the repo from cwd at run time, so one registration governs every project; no repo registers hooks in its own .claude/settings.json. Consequences: retire the 31 project-level registrations and the dispatcher stand-down branch (follow-up packet, ADR-0067 P7 at the earliest); candidate gate: fail when a tracked <repo>/.claude/settings.json carries a hooks key. Wiki ruling RUL-035; AGENTS.md Hard Rule 9; CLAUDE.md Key Rules. Enforcement: judgment until the gate lands.
Full record: docs/adr/0070-hooks-live-at-user-level-only-and-scope-to-the-project-in-cwd.md
ADR-0071: REQ-110-135 amended: codemod daemon lifecycle is delegated to systemd user units
Date: 2026-10-06 · Status: Proposed (recommendation: Accept)
SPEC-110 REQ-110-135 reads "start SHALL fork to background ... stop SHALL send SIGTERM, wait 5s for graceful shutdown, and force kill if necessary". ADR-0067 (Accepted) made the codemod daemon deployed code run as a systemd user unit, and packet P4 of plans/ast-daemon-deployment-plan.md executed it: start() refuses an active instance or a missing /opt/orchestrata-tools launcher, runs systemctl --user start, and records the unit's MainPID in .codemod_daemon/pid; stop() on a loaded unit runs systemctl --user stop and signals no pid, and only with no unit loaded does a pid-file pid get SIGTERM, a 5s wait, then SIGKILL, after argv verification that it is the codemod daemon entry module. The P4 round-3 review found the code correct against ADR-0067 and contradictory to the clause; both @traces(reqs=["REQ-110-135"]) methods now certify text they do not implement. Proposed: amend REQ-110-135 to the systemd-delegated lifecycle (unit-first start/stop, MainPID recorded, legacy pid-file path only when no unit is loaded and only for a verified codemod pid). REQ-110-133/134/136/137 unchanged. Alternatives rejected: leave the clause and let the RTM grade it on contrary tests; restore the forking start (reverses ADR-0067 for one daemon); edit spec.md under the ADR-0067 waiver (ADR-0067 rules on deployment, not SPEC-110's words; ADR-0068 set the Proposed-ADR path for spec text). On Accepted: edit REQ-110-135 in specs/110-codemod-engine/spec.md, keep checklist row 119 [x] citing this ADR, record the ruling in Provenance.
Full record: docs/adr/0071-req-110-135-amended-codemod-daemon-lifecycle-is-delegated-to-systemd-user-units.md
ADR-0072: The conversation graph endpoint is the prod orchgraph cluster, database orchkg
Date: 2026-10-06 · Status: Accepted (ruling, Mohamed, 2026-10-07: "cluster = Memgraph, DB = orchkg (ADR stands)"; docs flip 9efdaa570)
ADR-0066 (R16) left one value unnamed: which server and database host the conversation graph that orchestrata-conversation-consumer.service (SPEC-021 US-021-007) writes to. The consumer reads only CONVERSATION_KG_URI/CONVERSATION_KG_DATABASE and refuses to start without them (REQ-021-069: never resolved through the profile-driven KG keys a dev/test overlay redirects). Measured 2026-10-06: the P5 ship cleared both P5 prod blockers (plain units retired, test-env record GREEN), the first real prod promote ran and failed at verify on the consumer's RuntimeError: CONVERSATION_KG_URI and CONVERSATION_KG_DATABASE not set; no host, dev or test file names an endpoint today. Proposed: CONVERSATION_KG_URI=neo4j://10.53.1.160:7691, CONVERSATION_KG_DATABASE=orchkg in ~/.config/orchestrata/.env — the prod operational KG itself (the unit's own description says "-> orchkg graph", the SPEC-021 conversation labels are in the orchkg canonical schema, the nightly backup already covers it). Rejected: a dedicated conversation database (schema split, second root hub, every reader changes — nothing in SPEC-021 asks for it), the test-node Memgraph (prod never writes to the test fleet), resolving via KG_ROUTING_URI (REQ-021-069). On Accepted the keys are appended to the secrets file and the next promote copies them; remaining prod verify findings are plan PH1 (two failed timer oneshots) and P6 (verifier treatment of tool-tree template instances).
Full record: docs/adr/0072-conversation-graph-endpoint-is-the-prod-orchgraph-cluster-database-orchkg.md
ADR-0073: ERR-110-002 amended: the default-on review engine aborts a commit on findings the change introduced (delta baseline)
Date: 2026-10-06 · Status: Accepted (ruling, Mohamed, 2026-10-07: "accept adr-0073")
Plan decision D6 of SPEC-110 deferred this ADR "until a packet measures a legacy-file abort in practice". Packet S3 wired the InMemoryReviewEngine on by default in CodemodWorkspace.commit() with baseline='absolute', and its reviewer measured the result on this repository: 134 of 3,234 files already carry an error-severity finding (419 errors, most false positives with named, fixable causes), and a codemod --apply touching any of them now exits 1 with nothing written. After fix round 1 removed the four false-positive causes the same probe gives 247 errors in 100 files (2 src, 17 scripts, 81 tests), the tests-side ones being real literal credentials and flushdb calls in shared fixtures. Proposed: the default engine's ERR-110-002 abort applies to findings the change INTRODUCED (delta between the pre-mutation and mutated forest, matched on path/rule/line-free message); 'absolute' stays as the opt-in clean-slate gate. The delta line-shift bug (reviewer finding 2) is fixed in ace6149cb.
Full record: docs/adr/0073-err-110-002-amended-default-review-engine-aborts-on-introduced-findings-delta-baseline.md
Execution note (2026-10-07, Provenance of the record above; the Accepted body is unchanged): executed in orchestrata-ultimate 7db0f25e1 (default 'delta', CLI --baseline {delta,absolute}, ERR-110-002 amended) and refined in ac8188445 / c6946173a. The delta key is (path, rule, line-free message, stripped line text) with multiplicity, so an in-place edit of a line already carrying a content-free finding aborts; legacy findings are reported in CommitReport.legacy_findings, as FINDING_EMITTED with legacy: true and in REVIEW_COMPLETED.legacy_count, never vetoing. Measured on the whole tree at 7397f2c35: absolute 235 errors in 93 files, 12,786 warnings; delta 0 introduced.
ADR-0074: The prod promote re-embeds only changed semantic chunks and probes the embedding provider before the tree moves
Date: 2026-10-07 · Status: Superseded by ADR-0149 (Mohamed, 2026-10-29: "NO MORE rag OR EMBEDDIING"); was Accepted (ruling, Mohamed, 2026-10-07: "adr 0074 agreed"; docs flip 98f80b0df)
The promote's graphs stage runs bootstrap_graphs.py --all, whose Memgraph step re-embeds every semantic chunk through OpenRouter on every promote: chunk ids are positional and no content hash is stored, so 5,740 unchanged chunks cost 570 paid calls and 5 min 20 s per ship. On the c8c4ddd23 promote the account balance ran out mid-run (HTTP 402 on the 58th call); the stage failed after verify had passed and the promoter rolled back a working deploy. Proposed: (1) the ingester stores content_sha256 per chunk and embeds only new or changed chunks, deleting stale ids, so a no-change promote embeds nothing; (2) the infra stage probes the embedding provider (credit endpoint or a one-string embed) before the tree moves and refuses on 401/402/403 with no unit restarted; (3) graphs keeps --all. Rejected: taking the ingest out of the gate (graph lags code), --backend local in prod (different vector space, unmeasured), a --skip-semantic ship flag, topping up and changing nothing.
Full record: docs/adr/0074-prod-promote-embeds-only-changed-chunks-and-probes-the-embedding-provider-at-infra.md
ADR-0075: The promote's infra stage proves the ADR-0066 secrets file holds every required env key before the tree moves
Date: 2026-10-07 · Status: Proposed (recommendation: Accept)
The c8c4ddd23 prod promote's rollback left orchestrata-conversation-consumer.service failed on CONVERSATION_KG_URI and CONVERSATION_KG_DATABASE not set: the secrets file lacked two keys a newly deployed unit requires, and nothing checked before the tree moved. Proposed: (1) the infra stage reads the ADR-0066 secrets file (~/.config/orchestrata/.env, the source of truth, not the installer's copy) and refuses the promote with [FAIL] infra env keys: missing <names> when any required key is absent or blank — no tree move, no restart, no rollback; (2) the required set is the infra_env_rule registry (validate_env.REQUIRED_KEYS + KG_URI + the two CONVERSATION_KG_* keys), imported, never a second list, with a test pinning probe and registry together; (3) every addition is recorded by key name, the probe never writes the file and never proposes values (RUL-003).
Full record: docs/adr/0075-promote-infra-stage-proves-required-env-keys-before-the-tree-moves.md
ADR-0076: The semantic/RAG ingest leaves the prod promote; readers check freshness at runtime and trigger refresh on demand
Date: 2026-10-07 · Status: Superseded by ADR-0149 (Mohamed, 2026-10-29: "NO MORE rag OR EMBEDDIING"); was Accepted (ruling, Mohamed, 2026-10-07: "accept ADR-0076"; ADR-0074 item 3 superseded by this ADR)
Mohamed's ruling (2026-10-07): promotion ships code, not data — "no separate code promotion … when code runs it checks for freshness and triggers refresh if needed", scope semantic/RAG only; amended "we promote successfully and trigger a embedding refresh on successful promotion, we do not hide our heads in sand". Proposed: (1) the promote's graphs stage runs bootstrap_graphs.py --kg; the Memgraph ingest is no longer a promote stage, so no promote pays for, waits on or fails on embeddings; (2) a successful promote triggers the embedding refresh without waiting on or gating on it; (3) the refresh is single-flight and incremental (ADR-0074 item 1's content_sha256 engine) with a periodic safety net; (4) ADR-0074 item 2's provider probe re-homes to the trigger — a 401/402/403 or an unaffordable estimate defers and alerts, never fails a reader or a promote; (5) the ingest records the source commit it derived from; every semantic reader compares it with the deployed commit, triggers the refresh when stale and surfaces the staleness loudly instead of answering as if fresh; (6) ADR-0074 item 3 is superseded, items 1–2 survive re-homed. Rejected: keeping the ingest in the promote, serving stale with an annotation, a timer with no read-time check.
Full record: docs/adr/0076-semantic-rag-ingest-leaves-the-prod-promote-readers-check-freshness-at-runtime.md
ADR-0077: Terraform owns every Incus environment; the live dev, test and prod fleets are imported, not rebuilt
Date: 2026-10-08 · Status: Accepted (ruling, Mohamed, 2026-10-08: "all environments must be imported")
Three IaC layers disagreed about the Incus topology: infra/terraform described prod only, was stale (nic incusbr0 vs live orch-prod-br) and its September tfstate no longer exists anywhere on the host; deploy/provision_environments.sh was the de-facto authority; the drift gate in health_check.py passes silently when the state file is missing and no timer runs it. Decided: (1) terraform (OpenTofu) is the single source of truth for projects, bridges, ACLs, the project default profiles and every container in default (prod), orchestrata-test and orchestrata-dev; shell provisioners keep only the in-container layer and Raft enrollment; (2) everything live is IMPORTED, never recreated — acceptance is tofu plan -detailed-exitcode = 0 after import.sh; (3) state lives outside every checkout at /var/lib/orchestrata/terraform/fleet.tfstate; (4) the drift gate FAILS on a missing state, runs tofu, and runs on a timer; (5) out of scope: rhbr0 + the four stale rh-* containers (retired, Mohamed deletes), the codetools orchestrata project, incusbr0, the rehearsal overlay; (6) first managed change after zero-diff: boot.autostart for every container and the four stopped prod nodes started. Rejected: shell provisioner as authority, repair-only re-import, recreate-from-HCL, state in the repo.
Full record: docs/adr/0077-terraform-owns-every-incus-environment-and-the-live-fleets-are-imported.md
ADR-0078: The deployed codemod daemon reviews with every phase; the tools venv carries the free-threaded janus-swi
Date: 2026-10-08 · Status: Proposed (awaiting Mohamed's ruling)
/opt/orchestrata-tools vendored four hand-listed subtrees; measured 2026-10-08 the codemod daemon's eager import closure is 1 module while its first default review needs 103 (six packages, verification and openspec never vendored — task 580). WI-DEPLOY made the vendor list a computed, gate-pinned all-scopes closure (167 modules, nine packages) and made the deploy smoke run a real review; that smoke prints PrologSemanticReview: janus-swi not available, skipping, so the deployed review runs phases 1–2 only. Proposed: (1) the tools venv installs the same free-threaded janus-swi artifact the project venv uses (never upstream PyPI janus, which re-enables the GIL); (2) the smoke fails on any phase-3 skip marker and asserts the GIL stays off after import janus_swi; (3) the vendor list stays the all-scopes closure; (4) lazy-only third-party packages install when the closure lists them. Rejected: shipping phases 1–2 only, upstream janus-swi, vendoring the eager closure.
Full record: docs/adr/0078-the-deployed-codemod-daemon-reviews-with-every-phase-the-tools-venv-carries-the-free-threaded-janus.md
ADR-0079: The prod interlock requires a green test-env record of the commit being promoted
Date: 2026-10-08 · Status: Superseded by ADR-0087 (2026-10-11)
Promoter.gate_for("prod") admits a prod promote when the test env's latest test record reads PASS — the record carries a timestamp and return code, not the commit it tested, so any latest green run admits any tree. Measured 2026-10-08: the ledger's test=PASS@10:27Z (recorded for 1d5f3e52c) would have admitted the promote of a32b14194, 25 commits later; the honest path was a fresh promote_env.py test test on the new tree before the ship. Proposed: (1) test <env> records the commit and ship's source id with the result; (2) gate_for("prod") refuses unless the record is PASS AND its sha equals the commit being promoted (a sha-less legacy record counts as absent); (3) ship.sh keeps its post-hoc 5a/5b identity checks and gains no test test step — the interlock refuses pre-hoc; (4) run_tests.py prints the freshness guard's cause on an aborted: freshness. Rejected: keeping the gate, a time window on the record, source-id only, folding test test into ship.
Full record: docs/adr/0079-the-prod-interlock-requires-a-green-test-env-record-of-the-commit-being-promoted.md
ADR-0080: Every fleet container's address is pinned as nic ipv4.address
- Date: 2026-10-09 · Status: Accepted
- Every prod and dev container's nic declares
ipv4.address= its current lease (lease==pin census-verified; applies lazily on next restart). Applied 2026-10-09: 13 changed / 0 / 0, post-apply plan clean. - Full record: docs/adr/0080-every-fleet-containers-address-is-pinned-as-nic-ipv4address.md
ADR-0081: Provisioners create topology only by generating Terraform
- Date: 2026-10-09 · Status: Accepted
- Amends ADR-0077's consequence: provisioners may create topology only by generating
.tf;tofuremains the sole applier. Provisioner rewrite pending. - Full record: docs/adr/0081-provisioners-create-topology-only-by-generating-terraform.md
ADR-0082: The mcp-server-test-ssh profile's embedded key stays unrotated
- Date: 2026-10-09 · Status: Accepted
- "do not protect keys now, leeave this" — no rotation, no profile change. Verified at ruling: no container uses the profile; the key opens nothing on the host.
- Full record: docs/adr/0082-the-mcp-server-test-ssh-profiles-embedded-key-stays-unrotated.md
ADR-0083: The retired rehearsal fleet stays parked, not deleted
- Date: 2026-10-09 · Status: Accepted
- rhbr0 + rh-coord-1/2/3 + rh-data-1 and the memgraph_test.tf tombstone STAY. ADR-0077 item 5's retirement stands; retired ≠ deleted.
- Full record: docs/adr/0083-the-retired-rehearsal-fleet-stays-parked-not-deleted.md
ADR-0084: Governance predicate dispositions — wire 14, keep 8, hold 30
- Date: 2026-10-09 · Status: Accepted
- The 54 shipped-but-never-queried predicates, reviewed one by one: G226 cluster (9) and the 5 real laws wire at named sites; the 8 vacuous/superseded stay as documented intent; the 30 islands hold. Execution queue in plans/ruling-2026-10-09-governance-predicates.md.
- Full record: docs/adr/0084-governance-predicate-dispositions-wire-14-keep-8-hold-30.md
ADR-0085: Every ruling is recoverable, logged, and fed back into design
- Date: 2026-10-09 · Status: Accepted
- "the ruling from past must be recoverable and logged an used for feedback and to inform teh design and architecture" — every ruling lands in the ADR register (docs/wiki/graph, ADR-0046) the same turn; READMEs/plans/chat are never the record.
- Full record: docs/adr/0085-every-ruling-is-recoverable-logged-and-fed-back-into-design.md
ADR-0086: Claude sessions get a project-scoped SPEC-106 context pack at SessionStart
- Date: 2026-10-11 · Status: Accepted (Mohamed, 2026-10-29: "agree with our reccomendations", option B)
- Replace flat session-start dumps (and the dead Ctx* context_graph) with a SessionStart hook that injects a 1500-token, project-scoped SPEC-106 pack under a 5 s budget. Requires fixing three measured scale defects first: OR-label full scans in selector (~5.7 s/id), whole-graph name download in linking (14.6 s), and 90-way ambiguity from one code copy per worktree in the shared orchkg.
- Open rulings R1–R4: REQ-106-012 narrowing, mint REQ-106-045 (project-scoped linking), un-ingested worktree fallback, retire session_context_hook.py and re-trace REQ-020-030/031.
- R1 ruled 2026-10-11 (Mohamed: "b"): REQ-106-012 is not narrowed — linkable-node writers store an indexed folded-name property and linking matches against it; the ingest change lands first.
- R2 ruled 2026-10-11 (Mohamed: "a"): REQ-106-045 minted — entity linking is scoped to the request's project when one is known; callers without a project keep cross-project linking.
- R3 ruled 2026-10-11 (Mohamed: "no injest the project C"): an un-ingested project is ingested via the existing per-worktree ingest path (started detached, reported in one line), never substituted by another project.
- Accepted, R4 ruled 2026-10-29 (Mohamed: "agree with our reccomendations", option B):
session_context_hook.pyis retired and REQ-020-030/031 are re-traced to the new hook. Before porting the four branches, D-1..D-3 are re-measured on the current graph, and the R3 ingest runs memory-capped and single-flight. - Full record: docs/adr/0086-session-graph-context-injection.md
ADR-0087: The prod gate is one correctness run of the promoted commit
- Date: 2026-10-11 · Status: Accepted (Mohamed: "yes") · Supersedes ADR-0079
- (1) The prod gate needs one full correctness run, recorded with the commit and source id it tested; a PASS for another commit, or with no sha, does not admit. (2) Benchmark/stress modules move to a non-gating
perflane — still run, never skipped. (3) The deep pollution sweep runs once per suite (nested runners skip it) with regexes behind the cheap predicates they imply (278 s → 112 s measured). (4) Reruns re-run real failures one at a time; never-ran budget casualties are rescheduled, not counted as failures. - Measured 2026-10-11: ship's
test testre-ran 1,680 modules believing it took seconds; 425 modules never ran (273 s batch budget); five nested-runner tests failed on a 5–6 min sweep that found nothing. - Full record: docs/adr/0087-the-prod-gate-is-one-correctness-run-of-the-promoted-commit.md
ADR-0088: One shared green ledger per repo; a test reruns only when its own code or the code it tests changes
- Date: 2026-10-11 · Status: Accepted (Mohamed: "yes. one shared per repo with the test file and hash and test id and only rerun when the code of the test or the code the code tests changes") · R1 ruled (a) 2026-10-11 · Amends RUL-032 mechanism 1
- One SQLite ledger under the git common dir, shared by the primary checkout and every worktree; one row per (test id, content key). The key covers the test + conftest chain, its import closure, the non-Python files it reaches by reference or manifest (today's key misses
deploy/*.shanduv.lock— measured on the two greenlet-ship tests), pluspyproject.toml,uv.lockand the interpreter build. Every observed pytest run records greens via the live observer. - R1 ruled (a) 2026-10-11 (Mohamed: "yes R1 (a), go ahead, that is LAW"): the ADR-0087 prod gate composes executed + key-identical reused greens, bound to the promoted sha; the record lists each reused test id with its source run.
- Full record: docs/adr/0088-one-shared-green-ledger-per-repo-a-test-reruns-only-when-its-code-or-the-code-it-tests-changes.md
- Addendum 1, 2026-10-14 (append-only; ruling, Mohamed, verbatim: "code state only"): the P1 review measured every module keying ~83% of the repo (4,572 of 5,504 files, HEAD median 55) because the harness closure, directory-prefix expansion and gitignored-file admission pulled the harness's own outputs (
reports/*.ndjson, caches, codebase map, AST json,context.json,.env,.venv/bin/*) into every key; one unchanged rerun changed the key; 0 of 38 modules reused. Ruled: a key input is code state only — tracked files plus the declared lock/interpreter inputs; gitignored or untracked files are never inputs; files the harness writes are outputs, not inputs; prefix edges expand over tracked files only; env files stay out. Acceptance: an unchanged rerun keeps every key, input counts return to HEAD's order,--statusreports reuse after one green run. - Addendum 2, 2026-10-14 (append-only; ruling, Mohamed, verbatim: "reach by caller"): the P1 reach guard marked every real module
repo-statebecause the harness plugintests/plugins/git_sentinel.pyrunsgit ls-files --othersat each module's last teardown inside the run window (424 repo-state rows across 42 modules since 2026-10-12), so the observer recorded no greens. Ruled: reach is attributed by caller — a call whose nearest first-party frame is harness code (tests/plugins/, the observer,scripts/run_tests.py, the ledger) is the harness's reach and marks nothing; a call from the test, its fixtures/conftest chain or the code it exercises is the test's. The sentinel stays in place. Rejected: moving the sentinel outside the window; exempting read-only git commands. Acceptance: guard on, the V3 set records greens and no sentinel-caused repo-state rows; a test that calls git itself is still marked. - Addendum 3, 2026-10-14 (append-only; ruling, Mohamed, 22:54, verbatim: "approved addendum 3"): the P2a executor measured that addendum 1 to the letter still leaves 4,486 key inputs per module (4,353 in the D6 harness section) because the harness
_walkfollows the harness roots' reference edges — directory-prefix edges overtests/,scripts/,plans/,src/orchestrata_ultimate/,docs/adr/,specs/,claude/, all tracked — into 3,388 Python files, while the roots' pure import closure is 17 files. Ruled: harness roots contribute their code bytes only; their path and prefix edges are not key inputs; the D6 harness section is keyed by import closure only;KEY_SCHEMA_VERSIONbumps to 3; edge-named files are admitted only when tracked and not generated. Acceptance (P2a-2): an unchanged rerun keeps every key, input count < 200 per V3 module,--statusreports reuse after one green run,--gapsshows 0 sentinel repo-state rows, the six red-first ledger tests green.
ADR-0089: Bare toolchain names fail the gate in Prolog, and the C toolchain is one declared file
- Date: 2026-10-11 · Status: Accepted (Mohamed: "fix bare python make it prolog and atoms"; "make the gcc path a configuration and set it")
- The AST snapshot records every literal program token at a spawn-shaped call (
argv_literals, verbatim, no classification) and declares the captures it performed (captures, part of the warm-start identity, so an old snapshot is fully re-parsed rather than read as "captured, none found"). Prolog decides:argv_literal/5facts, enumeratedtoolchain_program/2atoms,violation(bare_toolchain, …); a rule whose capture is missing makes the gate INVALID, never PASS. Severity (error vs warning while existing sites are fixed) is Mohamed's call. env/toolchain.env(tracked) declaresCC/CXXas absolute paths;.envrc, both installers (deploy/toolchain.sh) and worktree provisioning read it and refuse a missing, bare-named or non-executable declaration — no PATH fallback. Root cause: a stale checkout's.venvand Guix GCC 16.2.0 led PATH (measured 2026-10-11).- Full record: docs/adr/0089-bare-toolchain-names-fail-the-gate-in-prolog-and-the-c-toolchain-is-one-declared-file.md
ADR-0090: The git sentinel fails a test for its own mutations, not for sibling-worktree activity
- Date: 2026-10-11 · Status: Proposed
- The sentinel byte-compares the SHARED
configandpacked-refs, which ordinary sibling-worktree work rewrites (git worktree add -bfrom a remote start point writesbranch.<b>.*— 68 such entries measured; push -u; branch -D on GC; gc/pack-refs). Every such write fails whichever test is in teardown in every other worktree — the false positives B2 waved through. - Proposed: block mutating
gitsubprocesses against the live repo at the source (audit hook, raises before exec, names the call site); keep HEAD and this branch's ref byte-exact; compare shared slots only for this worktree (its packed-refs line; config minus other branches'branch.<b>.*). Undetectable afterwards: a grandchild process writing only another branch's config/packed-refs entry. - Full record: docs/adr/0090-the-git-sentinel-fails-a-test-for-its-own-mutations-not-for-sibling-worktree-activity.md
ADR-0091: The janus fork links the declared SWI-Prolog through a path guix gc keeps alive
- Date: 2026-10-11 · Status: Accepted (Mohamed, 2026-10-12: "agree with adr"); superseded in part by ADR-0095 (2026-10-12): the SWI source moves from the guix profile to the project-owned prefix; the declaration and its checks stand
- The prod kernel failed at the 13:00 boot (down until 22:51): our janus fork's
setup.pytakesswiplfrom PATH at build time and bakes the RESOLVED store path into the extension's RUNPATH; prod's points at/gnu/store/n0iyzp3…-swi-prolog-9.3.8, which aguix gcafter the 2026-10-08 profile upgrade removed before 13:03 (tier 3 resolves under the systemd PATH whenever the RUNPATH exists). 30 worktree venvs carry the same exposure onss98ppl…. The doc header and the tier-4 fallback still name.so.10; af74b4f43 measured.so.9on 2026-09-22. - Proposed: declare
SWIPL(the Guix profile'sbin/swipl) inenv/toolchain.envunder ADR-0089's refusal rules; the fork takes it from the declaration only and bakes the UNRESOLVED profile library dir as RUNPATH (the profile is a GC root); a version check at start and a RUNPATH check at verify fail loudly; correct the stale docs and fallback; rebuild prod through promote. - Full record: docs/adr/0091-the-janus-fork-links-the-declared-swi-prolog-through-a-path-guix-gc-keeps-alive.md
ADR-0092: The orchestrata plugin is self-contained, pinned by a tag, and installed by one verifying command
- Date: 2026-10-12 · Status: Accepted (ruled 2026-10-12)
- Measured in a clean
CLAUDE_CONFIG_DIRwith an isolated HOME and no checkout.venvon PATH, master762935ab6's plugin fires 18 SessionStart hooks. 13 pass and 5 fail: 3 run$HOME/.claude/hooks/*.py, outside the plugin; 2 run barepython3= system 3.11, which cannot parse the 3.14 code. 21 of 65 hook commands use barepython3and 10 use$HOME/.claude, while the hooks and tools import neo4j, redis and pydantic. Every agent is delivered twice (~/.claude/agentsplus the plugin).marketplace add <url>#<sha>fails, while#<tag>pins. The current installer cannot pin and ignores--prefixfor itsclaudecalls. - Proposed: hooks, skills and commands run only
${CLAUDE_PLUGIN_ROOT}code under${CLAUDE_PLUGIN_DATA}/venv(3.14t, uv, lock-stamped, built by a plugin Setup/SessionStart bootstrap hook), enforced bygen_plugin.py --check. The pin is aplugin-<sha12>tag created by ship, with the version dropped from the manifest so the cache keys on the commit. Onedeploy/install-plugin.sh --ref <tag> [--config-dir] [--verify]stages, installs, runsclaude --init-only --debug hooks, and refuses success unless the sha, bytes, venv and every SessionStart hook's status 0 match. A live test observes all 7 hook events with a credential from the environment. The~/.claudecopies retire only after an inventory of outside references. - Ruling 2026-10-12, Mohamed: "agreed, take into account if other plugins or skills or hooks exist". It added decision 7: the installer writes only orchestrata's own entries (its marketplace key, its installed-plugins key, its
enabledPluginskey, its cache and data dirs), and asserts that every other plugin, skill, command, agent and hook is byte-unchanged. Rollback restores only those entries. Verify judges onlyorchestrata@orchestratahooks. Overlaps, such as duplicate hooks or name collisions, are reported and never changed. Retirement removes only manifest entries. A coexistence test runs with a second real plugin present. Measured neighbours:tmux-status,turn-navigator,skills/synced/, and the user Stop hookunshipped_guard.py. - Full record: docs/adr/0092-the-orchestrata-plugin-is-self-contained-pinned-by-a-tag-and-installed-by-one-verifying-command.md
ADR-0093: Promotion, test and gate work re-injects the process, test and gate memories, and compaction resets the injector
- Date: 2026-10-12 · Status: Accepted (Mohamed's directive)
- Mohamed, verbatim: "now configure those memories to be triggered every time we work on the promotion so you never forget especially the tests and gates". Only MEMORY.md loads by default. The workflow injector
claude/hooks/workflow_context_inject.pydedups per session id, which survives compaction, so after a compaction it prints "already in context" while the text is gone. - Decision: the injector gains a
promotiondomain (promote, deploy, ship, prod, rollback, test env, run_tests, gate...). The domain injects a tests-and-gates checklist kept in the repo, plus each memory file's description, its How-to-apply line and its absolute path. A PreToolUse(Bash) hook re-injects the same on every promote_env / ship / install / provision / run_tests / gate / tofu / git push command, with no dedup. Seen-state resets on SessionStart compact, resume and clear. The memory dir comes from the git common dir. Delivery to a real headless session is measured in a test. - Full record: docs/adr/0093-promotion-test-and-gate-work-re-injects-the-process-test-and-gate-memories-and-compaction-resets-the-injector.md
ADR-0094: Graph queries run with the ontology structure memory injected first
-
Date: 2026-10-12 · Status: Accepted (Mohamed's directive)
-
Mohamed, verbatim: "ontology structure should be memory we inject before queries". The project queries two graph databases: the operational
orchkg(about 39 labels) and the shared semanticmemgraph, whose labels co-mingle and whose chunk labels are split. Level 0 names the databases but not the labels, relationship patterns or keys, so Cypher written from memory guesses labels, and a wrong label returns empty rows that read like an empty graph. -
Decision: a project memory
kg-ontology-structureopens with a## Query cardof at most 60 lines. Per database the card gives the endpoint and env key, every label with its key properties, every(:From)-[:TYPE]->(:To)pattern, and the constraints and indexes. The card is built by the verification swarm fromcanonical_schema.pyand read-only introspection of the TEST fleet graph. The ADR-0093 injector gains anontologydomain:- Prompt trigger: cypher, graph query, KG, ontology, orchkg, memgraph,
MATCH (. - Command trigger, every match, no dedup: graph_query, graph_browser, semantic_search, kg_schema, check_kg_connectivity, bootstrap/sync/ingest to graph, cypher-shell, mgconsole, wiki-rag-query, or any command carrying Cypher or the neo4j driver.
The hook injects the card verbatim, plus the file path. A missing card is named as missing.
- Prompt trigger: cypher, graph query, KG, ontology, orchkg, memgraph,
-
Full record: docs/adr/0094-graph-queries-run-with-the-ontology-structure-memory-injected-first.md
ADR-0095: SWI-Prolog is built from pinned source into a fixed project-owned prefix, and the runtime path leaves guix
- Date: 2026-10-12 · Status: Accepted (Mohamed, 2026-10-12: "agreed")
- Mohamed, verbatim: "why do we need guix, assignment, research installing it guix free". A read-only measurement found that guix supplies four things: SWI-Prolog 9.3.8 with its closure (janus, the prod kernel, 13 bare-swipl call sites, the test container's store mounts), age, direnv and incusd. A garbage-collected store path already took prod governance down on 2026-10-11, despite ADR-0091's GC roots.
- Measured guix-free: SWI 9.3.8 builds from the pinned
V9.3.8tag plus 7 submodule commits in 42 s with distro gcc and no sudo; it loads the same 26 libraries as guix's build; janus rebuilt with 0 guix paths passes the real tests (28 + 3, plus 38 guard tests). age and direnv are static upstream binaries whose sha256 matches GitHub. incusd has no guix-free source. Also measured: janus takes its SWI home from the firstswiplon PATH, and a 10.0.2swiplaborts a 9.3.8 janus (rc 134). - Proposed: build SWI into
/opt/orchestrata-toolchain/swipl-9.3.8(fixed, versioned, read-only, outside deploy roots), with a pin file, a mirrored source archive and an installer check. Pointenv/toolchain.envSWIPL at it and rebuild janus. Put the prefixbinfirst on PATH everywhere, and setSWI_HOME_DIRfor the kernel. Take age and direnv from pinned upstream binaries. Give the test container the same prefix and drop its guix mounts. incusd stays on guix as a scoped exception until its own ADR. If accepted, this supersedes ADR-0091 for SWI. - Full record: docs/adr/0095-swi-prolog-is-built-from-pinned-source-into-a-fixed-project-owned-prefix-and-the-runtime-path-leaves-guix.md
ADR-0096: ADR body-drift dispositions — the graph sync treats wiki pointer entries as pointers, the three-way check compares bodies, ADR-0073's graph body is re-baselined once to its docs body, and the post-acceptance edits of ADR-0004 and ADR-0019 are ratified as their accepted text
- Date: 2026-10-13 · Status: Accepted (Mohamed, 2026-10-13, verbatim: "fix the sync and the check, re-baseline 0073, ratify 0004 and 0019")
- The 2026-10-13 ADR graph sync refused 36
BODY_DRIFTwrites while the three-way check said "no drift". A read-only audit of all 36 against the live graph found: 35 are a two-writer artefact (the graph holds the full docs body with a hash equal to docs HEAD; the wiki entry is a one-paragraph pointer added once on 2026-08-29 that the wiki pass re-hashes); ADR-0073 is the one stale graph body (captured from the wiki summary while Proposed, context/alternatives/consequences empty, bareStatus:line read as empty); ADR-0004 and ADR-0019 were edited in docs after acceptance but before first graph capture, so the graph already holds the edited text; the check compares titles and presence only. - Decision: the sync's wiki pass treats pointer entries as pointers and writes factory ids from docs only, and reads a bare
Status:line; the check comparesbody_shafor every factory id and reportsBODY_DRIFT; ADR-0073 is re-baselined once from its docs body through an explicit single-id path naming this ADR as authority; ADR-0004 and ADR-0019 are ratified as they stand (retrospective, closed; in-place edits of Accepted bodies remain forbidden); the hash still covers Provenance (no ruling on excluding it). - Builder brief: plans/briefs/2026-10-13-adr-sync-pointers-check-bodies-rebaseline-0073.md (DESIGN_WAIVER: ADR-0096).
- Full record: docs/adr/0096-adr-body-drift-dispositions-sync-skips-wiki-pointers-check-compares-bodies-0073-rebaselined-0004-and-0019-ratified.md
ADR-0097: The resident AST daemon publishes the snapshot file from its warm cache — the post-edit hooks stop re-serializing the tree on every write, and the duplicate home-directory refresh hook is retired
- Date: 2026-10-13 · Status: Accepted (Mohamed, 2026-10-13: "mint the adr and brief the builder", after asking whether to replace the hook now to save hook time)
- Measured: two refresh paths fire on every Edit (the plugin's
ast-incremental-refresh.shand~/.claude/hooks/ast_sync_hook.py), each detaching an incremental re-serialize of the tree; the hook's own header records 64 s warm, and one edit on 2026-10-13 had two serializers running over two minutes. The residentast-daemon(P1/P2 merged, running per checkout) already holds the parsed tree warm and invalidates changed files via inotify, but does not writeplans/ast-<name>.json, which the freshness pin, code_intel, gates.py and the Prolog KB read. - Decision: the daemon gains a
publishcommand and a watcher-driven republish that writes the snapshot atomically from its cache, byte-compatible with the serializer; the post-edit hook asks a live daemon to publish and only falls back to a serialize when no daemon is live; the duplicate home hook stops serializing and reindexes the Prolog KB only on snapshot hash change;gates.pystays the explicit full refresh; snapshot schema, serializer CLI, code_intel and codetools untouched. Acceptance is the measured edit-to-fresh wall in seconds. - Builder brief: plans/briefs/2026-10-13-ast-daemon-publishes-snapshot.md (DESIGN_WAIVER: ADR-0097).
- Full record: docs/adr/0097-the-resident-ast-daemon-publishes-the-snapshot-the-post-edit-hooks-stop-re-serializing-and-the-duplicate-hook-is-retired.md
ADR-0098: First-docs-capture rule — a graph ADR node whose body was never captured from docs/adr/ (it holds a wiki summary or is empty) is baselined once from its docs body; immutability binds from that first docs capture onward
- Date: 2026-10-13 · Status: Accepted (Mohamed, 2026-10-13: "agreed on 0098, rerun the sync fix when hooks land")
- The body comparison added to
check_adr_graph_sync.pyunder ADR-0096 reports 35 BODY_DRIFT rows over 122 ids (70 docs ids compared), a different set from the audit's 35 pointer ids: ADR-0034…0036, 0054…0058, 0062…0079 hold the wiki-captured summary or an empty Proposed body (the wiki parser reads the- **Status:**bullet as blank); ADR-0080…0085, 0087, 0089, 0096 carry the empty-body hash. The guard protects wiki summaries and empty bodies as if they were decisions; under ADR-0096 (one id) the check cannot reach OK. - Proposed: a docs body is the only body immutability protects; a node whose body is empty or equals the wiki entry's parsed body is baselined once from docs as a first capture (computed set, not a list; refuses any node whose body already came from docs and differs); one logged run
--rebaseline-first-capture --authority ADR-0098; the wiki pass never writes bodies for docs-owned ids and--repo-adrsruns in every sync; check must print 0 BODY_DRIFT after. ADR-0004/0019 unaffected. - Full record: docs/adr/0098-first-docs-capture-rule-a-graph-adr-node-whose-body-was-never-captured-from-docs-is-baselined-once-from-docs.md
ADR-0099: The gateway's MCP transport is a WebSocket JSON-RPC client of the running gateway, over one declarative method table that both transports read, and every method is approval-gated unless marked read-only
- Date: 2026-10-13 · Status: Accepted (Mohamed, 2026-10-13, one rule at a time, verbatim each: "agreed, next")
- The ADR-0038 brief assumed an HTTP verb surface with mutation markers; the gateway dispatches only over WebSocket JSON-RPC, its method table is a dict built inside
RPCHandler, and none of its ~80 methods carries a mutates marker (builder stop report, 2026-10-13 17:31). - Proposed: (1)
orchestrata-mcpis a WebSocket JSON-RPC client of the deployed gateway, authenticating with the deployedGATEWAY_TOKEN, so every call passes the gateway's own scope guard, auth and audit; (2) the registry becomes an importable declarativeMETHODStable read by both the RPC handler and the MCP transport; (3)mutatesdefaults to true, read-only methods are marked explicitly after reading each handler, streaming methods, legacy aliases andauth.authenticateare not exposed as tools. - Full record: docs/adr/0099-the-gateway-mcp-transport-is-a-websocket-json-rpc-client-of-the-running-gateway-over-one-declarative-method-table-and-every-method-is-gated-unless-marked-read-only.md
ADR-0100: Model review and the Prolog gate run once per slice at hand-back and once per batch, not on every commit; the deterministic gates stay per commit
- Date: 2026-10-13 · Status: Superseded by ADR-0101 (accepted 2026-10-13 by ruling "agreed, next"; superseded the same evening, its Context described a hook chain
git commitdoes not run) - Fifteen pre-commit hooks run per commit; thirteen are deterministic and fast, two are not:
unified-code-review(three model reviews per commit) andprolog-gate(gate.py run, killed at the 280 s limit by a builder on 2026-10-13). Every builder checkpointed with--no-verifyand paid both once at the end of the slice. - Proposed: deterministic gates, the sentinel and RUL-007 stay per commit with
--no-verifyforbidden; model review runs once per slice at hand-back on the full diff before ship; the Prolog gate runs once per batch on master after each fast-forward; the judged state stays committed state (ADR-0001), only the moment changes. - Full record: docs/adr/0100-model-review-and-the-prolog-gate-run-once-per-slice-at-hand-back-and-once-per-batch-not-on-every-commit-the-deterministic-gates-stay-per-commit.md
ADR-0101: ADR-0100 rested on a wrong reading of the commit hook; the real hook's stages are measured first and placement is decided on the numbers; none of ADR-0100's rules execute
- Date: 2026-10-13 · Status: Accepted (Mohamed, 2026-10-13: "agreed, next")
- Premise check after ADR-0100's acceptance (master ee4c5ba17):
git commitrunsscripts/hooks/pre-commit(secrets check,ruff --fix,scripts/gate.py review-stagedwith two deterministic per-file reviewers under a 600 s timeout and a Prolog verdict), not the 19-hook.pre-commit-config.yaml;scripts/code-review.pyruns zero model reviews;gate.py runis not in the hook; RUL-007 is the commit-msg hook; the ship script runs no hooks. ADR-0100's costs were measured on the wrong mechanism. - Proposed: ADR-0100 superseded in full, none of its rules execute; the real hook records per-stage and per-file walls to
reports/precommit-walls.ndjson; placement is decided by a new ADR after ten measured builder commits; every ADR Context cites its mechanisms by path and sha before it is ruled on. - Full record: docs/adr/0101-adr-0100-rested-on-a-wrong-reading-of-the-commit-hook-the-real-hook-stages-are-measured-first-and-placement-is-decided-on-the-numbers-none-of-adr-0100-s-rules-execute.md
ADR-0102: Three graph ADR nodes overwritten by a mis-targeted codetools sync are re-baselined from docs, and the codetools register gets its own graph key before any further sync
- Date: 2026-10-13 · Status: Accepted (Mohamed, 2026-10-13: "agreed, next")
- At 20:21 the coordinating session synced the codetools register (wiki page and docs/adr) with
sync_architecture_to_graph.py, which has no project key and writes every id underorchestrata-ultimate:; ADR-0039/0040 were refused by the immutability guard, ADR-0034 was restored by the corrective run, but ADR-0035 and ADR-0036 now hold codetools' Accepted bodies and ADR-0041 was created from codetools' wiki entry.check_adr_graph_sync.pyreports 3 title DRIFTs over 126 ids. - Proposed: re-baseline ADR-0035, ADR-0036 (from docs) and ADR-0041 (from this wiki page) with
--rebaseline-id … --authority ADR-0102; no codetools sync into this graph until the project key (ws/adr-sync-project-flag) lands; the sync script refuses a foreign page or directory with a message naming this ADR. - Full record: docs/adr/0102-three-graph-adr-nodes-overwritten-by-a-mis-targeted-codetools-sync-are-re-baselined-from-docs-and-the-codetools-register-gets-its-own-graph-key-before-any-further-sync.md
ADR-0103: The byte-faithful LIFT is the parity oracle, not the deliverable; the clean target is the deliverable
- Date: 2026-10-15 · Status: Rejected, 2026-11-02, by Mohamed's ruling of option A on ADR-0132. Was Accepted (probationary until POC — Mohamed, 2026-10-15: "RATIFY … BUT ON PROBATIONARY BASIS UNTIL PROVEN WITH POC")
- The components refactor's byte-faithful LIFT (AD-7/RULES-15) is demoted from deliverable to an ephemeral, never-committed parity ORACLE regenerated from frozen by a deterministic filler — proven: a ~150-line pure-stdlib script reproduces 25/34 committed contracts wave-1 files byte-identically from the brief alone (the other 9 differ only in mechanical assembly), and frozen
core/base/error.py:7–66== C-02's ORIGINALS. The shipped component is a separate clean copy designed totarget.mmd(6-layer, contracts-first, debt removed), gated by behavioural parity vs the oracle + conformance, so frozen's mess (zero-implementer protocols, the 4,131-LOCclaude/tools/sessionfork, theContextManagerdefect) is not repackaged into nine new packages. Supersedes AD-7; amends RULES-15; builds on ADR-0008 (transcription ≠ correctness). Probation lifts only when a five-criterion POC passes (oracle 34/34 + generalizes to an un-briefed component + one clean reshape preserves behaviour + the parity gate deterministically FAILS a hand mutant + target.mmd conformance); else this ADR is Superseded and byte-faithful AD-7 stands. - Full record: docs/adr/0103-lift-oracle-clean-target.md
ADR-0104: Git worktrees are not graph projects — orchkg holds one current, provenance-stamped copy per codebase
- Date: 2026-10-16 · Status: Accepted (Mohamed, 2026-10-16: "do the cleanup now")
- Measured: 88% of orchkg's code nodes (~545K of ~616K) were per-worktree copies of one repo, written under the worktree's name. Ids never deduplicated, copies outlived their worktrees, and 13 of 14 Project nodes had no provenance. Meanwhile the ADR-0103 component code and the frozen source were absent.
- Decision: no worktree is ingested as its own graph project. The graph holds one current copy per codebase
(master, components, frozen, codetools), each recording
root_path,commitandsynced_at. Branch-local structure comes from each worktree's AST daemon. - Executed: 12 copies deleted (953,076 nodes, 0 left, no new orphans).
- Incident: the re-sync of
orchestrata-ultimatedeleted 249 non-code nodes, because no dry run was made.- The ADR and architecture nodes were restored from master's docs.
- The 66 test-fleet Thread nodes were not restored, by choice; the pre-delete snapshot is preserved.
- The deleter is now code-labels-only and requires a dry run first.
- Owed: pin the writer, a project-registry gate in
GraphSyncer, and project-scoped queries. - Full record: docs/adr/0104-worktrees-are-not-graph-projects.md
ADR-0105: The components branch pushes only after the affected-test gate learns the packages/** layout — a 345-commit push is not shipped past the gate with --no-verify
- Date: 2026-10-18 · Status: Accepted (Mohamed, 2026-10-18, verbatim: "Remediate the gate, then push")
- Cleanup of session 271 done (5 commits, tree clean); the
componentsbranch is 345 commits ahead of origin/master and was never pushed. The pre-push affected gate stops the push:run_tests.py --affectedreturns 1 on 7,176 UNTESTED changed files (6,505.pyunderpackages/**), anddeploy/ship.shhits the same wall (7,178 from the last verified tree8a413deed). Root cause: the affected model knows only top-levelsrc//tests/; the lifted packages carry parity suites in per-package venvs the gate cannot see, and no pushed commit ever touchedpackages/**. - Decision: the branch pushes only after the affected gate learns the
packages/**layout (resolution source + per-package runner seam + manifest rules for the inert artifact classes, then verify via ship.sh). No 345-commit push ships past the gate with--no-verify; until the packet is green the branch stays unpushed. - Full record: docs/adr/0105-the-components-branch-pushes-only-after-the-affected-test-gate-learns-the-packages-layout-a-345-commit-push-is-not-shipped-past-the-gate-with-no-verify.md
ADR-0106: Ruling-gated lift increments are not claimed by their package's parity suite until their ruling lands — the affected gate certifies the landed surface, and the held REQs stay UNLINKED in the RTM
- Date: 2026-10-18 · Status: Accepted (Mohamed, 2026-10-18, verbatim: "Hold the claims, gate green now")
- The ADR-0105 remediation is green for session (57/57), kg (143/143) and governance (242/242); volks sits at 234 passed / 38 failed, of which 31 are parity claims on the lander's deliberately STOPPED importers (tool_executor, architect.volk, codedirector.volk/implementation, spec_completeness — the N-V3/N-V4 tools knot, increments marked "ruling-gated", re-points
##UNRESOLVED##; AD-65 R3/AD-91) and 7 are the in-flight contracts rename. The parity suite claims REQs the documented design says are not implemented, and the__init__re-export shims import the stopped modules, breaking ~27 non-gated tests as cascades. - Decision (proposed): a package's parity suite claims only LANDED units — ruling-gated REQs are HELD (no test emitted, generator records the hold, RTM grades UNLINKED until the ruling re-claims them), and landed modules never import held units (the stopped-class re-export shims are removed). The affected gate then certifies exactly the landed surface. Rejected: waiting for N-V3/N-V4 (unbounded), implementing the tools family now (that is the ruling; self-rulings are forbidden), and skip/xfail (no-skips doctrine; a hold is not a skip).
- Full record: docs/adr/0106-ruling-gated-lift-increments-are-not-claimed-by-their-parity-suite-until-their-ruling-lands.md
ADR-0107: The frozen tools family lands as its own component package orchestrata-tools, and volks consumes it as a declared dependency (N-V3)
- Date: 2026-10-18 · Status: Accepted (Mohamed, 2026-10-18, verbatim: "Ratify and implement all of it")
- The N-V3 gate held five volks increments because the tools/codetools family was un-designed. Measured: the frozen tools family is 20 modules / 13,449 LOC with ZERO
orchestrata_ultimateimports (fully self-contained; httpx/psutil/redis/typer/urllib), openspec is 723 LOC, andpackages/toolsis already reserved. Decision: land the tools + openspec families aspackages/tools(orchestrata_tools, SPEC-126) via the existing lift machinery with its own parity suite; volks declaresorchestrata-toolsand retargets tool_registry/spec_completeness imports. Rejected: seam-only (tool_registry would stay un-landed forever) and landing tools inside volks (wrong layering). - Full record: docs/adr/0107-the-frozen-tools-family-lands-as-its-own-component-package-orchestrata-tools-and-volks-consumes-it-as-a-declared-dependency.md
ADR-0108: The AST toolkit stays host-side — the volks codedirector consumes it through a contracts protocol injection seam (N-V4)
- Date: 2026-10-18 · Status: Accepted (Mohamed, 2026-10-18, verbatim: "Ratify and implement all of it")
codedirector.implementationwas stopped overscripts.serialize_codebase_ast(13,797 LOC — the host's code-intelligence engine, not a component) for two functions:gate0_construction_smoke,generate_skeleton_modules. Decision: the toolkit is NOT lifted;orchestrata-contractsgains a smallAstToolkitProtocol(the two signatures), the codedirector lands retargeted to the protocol (TYPE_CHECKING import), and the host wires the real toolkit at runtime. The repo-rootimport orchestrata_ultimatehacks become the landed packages' own root resolution (ORCHESTRATA_ROOT pattern). Rejected: lifting the 13.8K-LOC toolkit for two call sites, and copying the two functions (forked truth).- Full record: docs/adr/0108-the-ast-toolkit-stays-host-side-the-volks-codedirector-consumes-it-through-a-contracts-protocol-injection-seam.md
ADR-0109: Semantic-graph bulk writes run in transactions of at most 500 rows; replication stays SYNC
- Date: 2026-10-19 · Status: Accepted (Mohamed, 2026-10-19, verbatim: "Batch the big writes")
- The d4037442 prod promote failed at
graphs:MemgraphSemanticGraph.clear()deleted every CodestralChunk in ONE transaction; the SYNC replica needed ~54 s to apply it against a fixed 5 s replication wait. Decision:clear(),clear_prefixed()'s chunk delete andlink_chunks_to_modules()loopLIMIT $batchwrites of at most 500 rows (WRITE_BATCH_SIZE) until a pass touches 0 rows. Replication stays SYNC. Rejected: ASYNC replication / a longer timeout, and a whole-DB reset of the shared semantic DB. - Full record: docs/adr/0109-semantic-graph-bulk-writes-run-in-transactions-of-at-most-500-rows-replication-stays-sync.md
ADR-0110: The idle Prolog containers are retired from every fleet, and SWI-Prolog runs only embedded in the kernel
- Date: 2026-10-19 · Status: Accepted (Mohamed, 2026-10-19, verbatim: "agree to all", then "yes and document the environments thoroughly")
- Measured 2026-10-19:
prolog-node-1/2/3(prod),prolog(test) andprolog(dev) run no service and hold 0 connections, and no source reads an endpoint for them (PROLOG_NODESis written byinfra/gen_env.py, read by nothing). SWI-Prolog executes only in-process: the kernel (runtime_main, prod on the host and test in theappcontainer) mapslibswipl.so.9.3.8through the janus fork. The gateway maps none. - Decision: terraform stops declaring the five containers (
prolog.tfdeleted, theprolog_nodesvariable and output and the fleet-map entries removed) andtofu applydestroys them. The provisioners and theirswipl --versionprobes stop. The raw-machine and rehearsal templates follow. Prolog readiness is the kernel's boot health line plus ADR-0095's libswipl check. An out-of-process Prolog service needs its own ADR. ADR-0077's terraform-owns rule stands. - Full record: docs/adr/0110-the-idle-prolog-containers-are-retired-from-every-fleet-and-swi-prolog-runs-only-embedded-in-the-kernel.md
ADR-0111: Test runs never create containers; container creation is exercised only by explicit rehearsals
- Date: 2026-10-19 · Status: Accepted (Mohamed, 2026-10-19, verbatim: "do not test container creation in test runds")
- Measured 2026-10-19:
- Container-creating tests leaked six instances.
- Their deletes on the quota-enabled
t9-btrfspool blocked test Redis for up to 99 s, which failed thebd7aa0dfeship gate. - One codetools test spent minutes per run re-provisioning a whole container.
- Decision: no test run (developer run,
pytest-live,run_tests.py, ship gate, pre-commit, CI) creates, copies, launches or inits a container or VM. Tests may read and exec into the standing fleet containers.- Container-creating code is exercised by explicit rehearsals under
rehearsals/, outside the test tree, run deliberately when that code changes. - A static policy gate in each repo's test run fails any test module that calls an instance-creating API.
- Container-creating code is exercised by explicit rehearsals under
- Rejected:
- a live-only lane run when Incus code changes;
- pool-only fixes;
- a fake Incus server (RUL-002).
- Ruling: RUL-038.
- Full record: docs/adr/0111-test-runs-never-create-containers-container-creation-is-exercised-only-by-explicit-rehearsals.md
ADR-0112: Tools reaches Redis and embeddings through contracts protocols, not kernel imports
- Date: 2026-10-19 · Status: Accepted (Mohamed, 2026-10-19, verbatim: "all" — ratifying R2 option B of reports/project-analysis-2026-10-19.md)
- Measured 2026-10-19: the landed tools package imports kernel at two module-level sites (
schedule_cron_tool.py:76create_async_redis,search.py:52EmbeddingService) — retarget artifacts (D-T2) exceeding the drawn DAG, which boundaries.json draws as contracts + knowledge_graph + governance only. - Decision: both sites go through contracts protocols —
RedisFactory(TYPE_CHECKING-only redis types, no new runtime dep) andEmbeddingPort(pure-Python surface). Tools dropsorchestrata-kernelfrom its dependencies and keeps real local default constructions, so it stays bootable standalone; kernel/integration supplies the canonical implementations at boot. Parity tests droporchestrata_kernelfrom_ALLOWED_ROOTSas the negative check. - Full record: docs/adr/0112-tools-reaches-redis-and-embeddings-through-contracts-protocols-not-kernel-imports.md
ADR-0113: Integration L1 builds now, and the master push is recorded as backup-and-deploy, not cut-over
- Date: 2026-10-19 · Status: Superseded in part by ADR-0132 (2026-11-02: the cut-over merge condition that removes
src/orchestrata_ultimate/*no longer holds). Was Accepted (Mohamed, 2026-10-19, verbatim: "all" — ratifying R3 option A and R4 option A of reports/project-analysis-2026-10-19.md) - Measured 2026-10-19: integration (the composed release) has a full phase-D design and zero Python files; every injection seam of the eight landed packages is unwired until its composition root exists.
components=origin/master=0faca7a7and prod deploys that commit — but the old tree was not removed and integration was not built, so the push is not the cut-over. - Decision: integration L1 builds now (composition root, five boot adapters, env profiles, service start/verify, fleet-tier smoke importing the clean packages), fenced to landed code only; lift residue stays per-package briefs. The
0faca7a7master push is recorded in the components design and LEVEL0 as backup-and-deploy of the in-progress tree; the cut-over remains the future merge condition (fleet-tier green + oldsrc/orchestrata_ultimate/*removed + one merge to master). - Full record: docs/adr/0113-integration-l1-builds-now-and-the-master-push-is-recorded-as-backup-and-deploy-not-cut-over.md
ADR-0114: orchkg sees the component packages, and Spec and Requirement nodes are in scope
- Date: 2026-10-19 · Status: Accepted (Mohamed, 2026-10-19, verbatim: "all" — ratifying R5 option A of reports/project-analysis-2026-10-19.md)
- Measured 2026-10-19: orchkg was synced from the monolith root only — zero component-package paths, 0 Spec nodes, 0 Requirement nodes; the repo holds 72 specs/*/spec.md and 58 requirements checklists. The AST walker skips nested distributions (pyproject markers), so
packagescannot be one root. - Decision:
env/ast-roots.envgains one root per component package'ssrcdir (integration's joins when it lands); Spec and Requirement nodes are in scope, sourced from specs/* and @traces through the existing sync tooling pointed at orchkg, verified by count queries > 0. A worker brief fills any tooling gap. - Full record: docs/adr/0114-orchkg-sees-the-component-packages-and-spec-and-requirement-nodes-are-in-scope.md
ADR-0115: E-1 ruled: the component DAG gains integration → knowledge_graph, governance, tools (27 → 30 edges)
- Date: 2026-10-20 · Status: Accepted (Mohamed, 2026-10-20, verbatim: "Clear E-1 per the standing recommendation")
- Measured: 20 frozen-bytes outbound rows (kg 7, governance 3, tools 10) target components outside the drawn 27-edge DAG; 18 of 25 INT increments carried
<PENDING Mohamed E-1>and dispatched last; the prod orchkg seed and clean-target table already carry the 30-edge shape (declared edges, not violations) while the design registers waited on the ruling. - Decision: E-1 is cleared per the standing D-I6 recommendation — integration → knowledge_graph, governance, tools are DECLARED DAG edges (27 → 30); the 18 gated increments unblock and keep their frozen-bytes consumer lines byte-faithful (no port-invention, no kernel pass-through); per-line markers flip to an ADR-0115 citation at each dispatch; N4 (INT-11) and N-I1 (INT-04) stay gated as separate rulings.
- Full record: docs/adr/0115-e-1-ruled-the-component-dag-gains-integration-knowledge-graph-governance-tools.md
ADR-0116: Staged source .py files enter the T21 env-key review at commit time
- Date: 2026-10-20 · Status: Accepted (Mohamed, 2026-10-20, verbatim: "Approve as follow-up packet (Recommended)")
- Measured: commit_review.pl's reviewer_tree routes only tests/ + end2end/ to test_review, so the T21 env-key rule — founded on a SOURCE-file incident (run_tests.py::_child_run_env fabricated ORCH_TEST_DEADLINE) — never gates the file class that founded it; wiring would surface exact-key WARNINGs in 29 test files plus the 6 run_tests.py sites (fixed in T17 run-2's fence).
- Decision: staged source .py files enter the T21 review; the wiring is a follow-up packet after T21 lands (minimally
reviewer_tree(test_review, 'scripts/'), src/ scope measured in-packet) and must make the 29-file exact-key worklist visible (burn-down or explicit ratchet baseline); the warnings-block pre-commit policy stays uniform — no source-file exception. - Full record: docs/adr/0116-staged-source-py-files-enter-the-t21-env-key-review-at-commit-time.md
ADR-0117: SPEC-109 rename engine moves to codetools, and orchestrata consumes the deployed CLI
- Date: 2026-10-20 · Status: Accepted (Mohamed, 2026-10-20, verbatim: "A" — option A of the coordinator's location prompt over plans/refactor-convergence-design.md)
- Measured: codetools already hosts the cross-project lane (24 packages incl. refactor/ with import-level primitives, zero pyright/LSP presence, deployed /opt/codetools/bin); the SPEC-109 basedpyright JSON-RPC engine is built + 13-module-tested but stranded (CLI seam shipped, volk registration declared-deferred, 0 production importers); its coupling = exactly 5 external orchestrata imports across 29 modules; the components refactor assigns refactor/ NO landing (0 mentions in clean-target + boundary-census).
- Decision: engine + CLI + tests + spec bookkeeping move to codetools (the spec travels with the code; orchestrata's RTM records the move, never dangling REQ-109); orchestrata consumes the deployed CLI, with a contracts seam only on a concrete volk consumer (ADR-0108 shape); two-copy parity (owed sanctioned green proof BEFORE, byte-faithful move, codetools green AFTER); the toolkit's KB-native rename stays; module_move overlap ruled in-packet; codemod/ctok location explicitly NOT in this ruling.
- Full record: docs/adr/0117-spec-109-rename-engine-moves-to-codetools-and-orchestrata-consumes-the-deployed-cli.md
ADR-0118: Tier-1 host-side scripts transfer to codetools in two batches; review and RTM lane merges wait for their own rulings
- Date: 2026-10-20 · Status: Accepted (Mohamed, 2026-10-20, verbatim: "great, why not run workers to do that now" — approving the transfer-audit recommendation cut); item 5's suite machinery superseded in part by ADR-0171
- Measured: 182 scripts scored by coupling; a backcompat bridge already exposes the review/diagram verbs cross-project from the codetools venv; codetools.rtm/testing are RICHER siblings (adoption questions, not transfers); the first two moves have zero hard consumers (docstring lineage references only); codetools' review lane is dirty with an active REV-3 workstream and is avoided.
- Decision: MOVE-109 + Batch 1 (pyright_autofix/pyright_ratchet + 6 test modules → codetools refactor lane) dispatch now with the ADR-0117 template (byte-faithful, green proofs both sides, recorded disposition; the zero-consumer volk tool shell is deleted, not moved); Batch 2 (12 more Tier-1 scripts, rts.py conditioned on P-RUL040's consumer contract) packetizes behind a cross-vendor audit panel; workers land NEW FILES only in codetools — pyproject/console-scripts/deploy are one serialized coordinator step; Tier 2 (review trio after T21, RTM adoption, ADR-graph gate, deployed-from-here family) held for per-lane rulings.
- Full record: docs/adr/0118-tier-1-host-side-scripts-transfer-to-codetools-in-two-batches.md
ADR-0119: dr-hannah connects only to the prod orchgraph memgraph cluster; data stranded in another memgraph moves to prod
- Date: 2026-10-20 · Status: Accepted (Mohamed, 2026-10-20, verbatim: "dr-hannah should connect to the ssme memgraph as prodcustion orchestrata and code tools, if it has data in the other memgraph it must be moved to the prof memgraph cluster")
- Verified at ruling time: dr-hannah already stores its data on the prod orchgraph cluster's
memgraphdatabase (19,887 nodes incl. Hannah/CodestralChunk/Entity labels) viaDR_HANNAH_MEMGRAPH_HOSTS; the other memgraph (codetools-memgraph, orchestrata project) holds 0 nodes, so nothing needed moving; a stray ambientMEMGRAPH_URIin dr-hannah's process env is inert (no code reads it). - Decision: dr-hannah connects only to the prod memgraph cluster; any dr-hannah data found in another memgraph is migrated to prod. Codetools repointing
CODETOOLS_MEMGRAPH_URIto the prod cluster is the tracked follow-up that completes the "same memgraph as code tools" clause. - Full record: docs/adr/0119-dr-hannah-prod-memgraph-cluster-only.md
ADR-0120: Test-spawned subprocesses import from the installed environment, never the source tree
- Date: 2026-10-20 · Status: Accepted (Mohamed, 2026-10-20, verbatim: "yes and create an adr that applies this to all future cases like this")
- Context: the §5.4 coupling measurement found three tests whose kill-child subprocesses run
sys.path.insert(0, repo/src)before importing codetools.deploy (atomic_promote:51, install:35, steps_spec012_fixtures:22) — the child imports the working tree instead of the installed artifact; L26's class at the subprocess boundary. Mohamed's forcing question: "What is the professional best practice, not the band aid, what is the RIGHT way?" - Decision: a test-spawned subprocess resolves the packages under test from the installed environment like production, never via
sys.path/PYTHONPATHinto the source tree; freshness is the runner-level staleness guard (the §5.3 ruling), not per-test path surgery; production-entrypoint invocation wherever a child simulates production,-cshims only where the PID is structurally needed. The three sites repoint in a packet whose acceptance pairs them with the §5.3 staleness guard — never alone. - Full record: docs/adr/0120-test-subprocesses-import-from-installed-environment-never-source-tree.md
ADR-0121: The deployed Hermes subagent keeps persistent curated memory across runs; the user profile stays off
- Date: 2026-10-20 · Status: Accepted (Mohamed, 2026-10-20, verbatim: "you must allow hermes memory")
- Context: the Hermes subagent deployment (/opt/hermes, source ~/workspace/hermes) was deliberately stateless (memory_enabled: false; comment "A subagent keeps no personal state"). The agent ships a bounded self-managed memory mechanism (MEMORY.md 2,200 chars, USER.md 1,375 chars in $HERMES_HOME/memories, frozen-snapshot injected at session start). The coordinator reported this and Mohamed ruled.
- Decision: memory_enabled: true in the deployment's home config; the subagent keeps MEMORY.md across runs (bounded, self-managed via the memory tool); user_profile_enabled stays false. Ships through the repo → deploy/install.sh → immutable release → hermes-task verify pipeline, never by editing the deployed home in place.
- Full record: docs/adr/0121-hermes-subagent-keeps-persistent-curated-memory-user-profile-off.md
ADR-0122: One coordinator per worktree at a time; the shared handoff board is the coordination channel
- Date: 2026-10-20 · Status: Accepted (Mohamed, 2026-10-20, verbatim: "a" — option (a) of the §5.6 prompt)
- Context: two concurrent coordinator sessions operated one worktree with no channel; one reverted the other's sanctioned pyproject/uv.lock work, and push gates ran against a tree in flux — three wasted push attempts and a worker mis-attribution before the de-facto channel (handoff board + mtime check, one coordinator commits) ended it.
- Decision: one coordinator per worktree at a time; a second session is read-only until the shared handoff board marks the worktree free; board + mtime check before touching any fence; one coordinator commits. The rule rides every handoff as a standard clause.
- Full record: docs/adr/0122-one-coordinator-per-worktree-handoff-board-is-the-channel.md
ADR-0123: Every host service is registered in the runtime view with its purpose and architecture component, or it is retired
- Date: 2026-10-22 · Status: Proposed
- Context: ten systemd units ran out of /opt/orchestrata. Seven are in no architecture doc (component-dag.md is the code view, architecture-ontology.md the design schema), and install.sh enabled and started every one on each deploy, overriding the operator's load cuts of 2026-10-22.
- Decision: the wiki page projects/orchestrata-ultimate/runtime-services.md is the register of host services. A deploy never enables or starts a unit; enablement is the operator's. A unit without a purpose and an architecture home is retired. Each unit's disposition (keep enabled, on demand, or retire) is ruled one at a time.
- Full record: docs/adr/0123-every-host-service-is-registered-in-the-runtime-view-or-retired.md
ADR-0124: Orchestrata runs in a test container and a prod container built from one OpenMandriva image, over always-on memgraph and Redis
- Date: 2026-10-22 · Status: Proposed (two containers and the image ruled by Mohamed 2026-10-22)
- Context: the host's ten service units cost CPU on a CPU-bound host and each deploy re-enabled them (ADR-0123). The volks write code in the workspace, and a read-only mount already broke
session.openonce. No OpenMandriva image is published. - Decision: a test container to test and a prod container that runs ("we need a test container to test and prod is what is running"), both from our own OpenMandriva image. Memgraph and Redis stay up 24/7; the containers are off at boot, limited, mount the workspace read-write, and run from a pinned install. Claude starts and stops prod, and prod stops itself when idle.
- Full record: docs/adr/0124-orchestrata-runs-in-a-test-container-and-a-prod-container-over-always-on-infrastructure.md
- Addendum 1, 2026-10-23 (append-only; ruling, Mohamed, 2026-10-22, verbatim: "cutover first"): asked whether the containers come first, running the current package, or the components cut-over first. Ruled: the cut-over (the integration package, INT-02 to INT-25) lands before either container is built; the containers only ever run the components build, never the old single package. Until then the workers build on the host in worktrees. Recorded as item 10 of the docs record.
ADR-0125: The host Claude writes no orchestrata code; it perfects the briefs, checks and workflow and measures the worker models
- Date: 2026-10-22 · Status: Accepted (Mohamed's ruling, RUL-041)
- Context: the host Claude already wrote briefs, dispatched Sonnet builders, checked results and logged brief defects (D1 to D21). Qwen and DeepSeek are available through Hermes and pi-task.
- Decision: "this claude here remains production claude but it writes no code when working on my project just perfects the briefs checks and workflow with the flagship model while measuring sonnect and qwen and deepseek performance and workflow improvements". Workers write the code; each dispatch is measured.
- Full record: docs/adr/0125-the-host-claude-writes-no-orchestrata-code-it-perfects-briefs-checks-and-workflow.md
ADR-0126: The kg is branch-aware: each synced branch is its own project, serialized from a clean commit and checked after every sync
- Date: 2026-10-22 · Status: Proposed (direction ruled by Mohamed: "make the tree branch aware option b")
- Context: every checkout syncs into one project id; the kg lost 1,064 modules in two contiguous sorted ranges; a snapshot taken with a different root set would have deleted the 1,771 package modules.
- Decision: each synced branch is its own project (default branch keeps the bare id, others
<project>@<branch>, proposed); syncs come from a clean commit export with the commit's root set; every sync is followed by a snapshot-vs-kg comparison and re-sync of any gap. The components branch is synced first. - Full record: docs/adr/0126-the-kg-is-branch-aware-each-synced-branch-is-its-own-project.md
ADR-0127: The test runner stays a repo dev tool outside the integration release until codetools has an affected-set oracle
- Date: 2026-10-23 · Status: Accepted (Mohamed, 2026-10-22: "ok agree")
- Context: INT-12..22 would move
run_tests.pyand its siblings into the integration package, which is 3,741 of the release's 7,136 LOC (52%), lifted from a frozen commit that is already 11 commits behind the runner. - Decision: INT-12..21 and the
start_test_serviceshalf of INT-22 leave the release (ha_controller_clistays).scripts/run_tests.pystays a repo dev tool until codetools has an affected-set oracle that can replace--affected. The AST roots fix and cross-package affected-set dependents join the cut-over. - Full record: docs/adr/0127-the-test-runner-stays-a-repo-dev-tool-outside-the-integration-release-until-codetools-has-an-affected-set-oracle.md
ADR-0128: The tracked codebase maps are regenerated at commit, by per-file delta, through the codetools ast map verb
- Date: 2026-10-23 · Status: Accepted (Mohamed, 2026-10-22: "A", "maps should use delta", "do item 3 in orchestrata")
- Context: the tracked maps (RUL-030) were refreshed only at SessionStart, and new worktrees copied the primary's map, so commits carried maps of another tree. A full regeneration costs 13.5 s; the codetools verb costs 0.495 s warm.
- Decision: pre-commit regenerates and stages both maps whenever Python is staged; regeneration re-parses only changed modules and must equal a full run byte for byte; it goes through
codetools ast map <tree> --with-full, withscripts/gen_codebase_map.pyas the fallback. Shipped as a4cbc2bdf on master. - Full record: docs/adr/0128-the-tracked-codebase-maps-are-regenerated-at-commit-by-per-file-delta-through-the-codetools-ast-map-verb.md
ADR-0129: Installer tests that only read the install share one read-only install, an enforced exception to function-scoped fixtures
- Date: 2026-10-23 · Status: Accepted (Mohamed, 2026-10-22: "1,2 and 3")
- Context: eight of the eleven orchestrata-tools installer tests each ran the full installer (about 22 s), so worker VERIFY runs hit the 300 s cap on that suite alone.
- Decision: the read-only tests share one install, enforced read-only (a write fails the test); the installer prints its phase times (venv reuse measured at only 3 s); only the affected tests run for a change. Implemented as orchestrata-tools 7309a3488.
- Full record: docs/adr/0129-installer-tests-that-only-read-the-install-share-one-read-only-install-an-enforced-exception-to-function-scoped-fixtures.md
ADR-0130: ADRs are a publish step — rulings live in the scratch notes while a thread is open and are minted together at its end
- Date: 2026-10-23 · Status: Accepted (Mohamed, 2026-10-22: "adr becomes a publish step for you")
- Context: minting every ruling the same turn (ADR-0046/0047) produced ADRs written mid-research and superseded soon after, plus repo writes during a discussion.
- Decision: "when we have finished understanding, researchimg and agreeing adr becomes a publish step for you". While a thread is open, rulings are recorded verbatim in the session scratch notes. At its end, or on "publish", each is reviewed against the register: covered → addendum or noted; operational → status doc; the rest minted in all three homes; still-open questions minted Proposed with one recommendation.
- Full record: docs/adr/0130-adrs-are-a-publish-step-rulings-live-in-the-scratch-notes-while-a-thread-is-open-and-are-minted-together-at-its-end.md
ADR-0131: Deploys the classifier blocks run through approved user-level launchers that refuse unsafe states and verify or roll back
- Date: 2026-10-23 · Status: Accepted (Mohamed: ct-install 2026-10-07 "option 2"; tools-deploy and hermes-deploy 2026-10-22, "yes")
- Context: the auto-mode classifier blocks every raw installer as a production deploy, so each deploy needed a paste; running an installer from a live worker tree also let a commit change what was approved.
- Decision: each blocked deploy gets a launcher in
~/bin(ct-install,tools-deploy,hermes-deploy), outside any repo, pre-approved by an allow rule. A launcher refuses a dirty tree, unknown commit, non-fast-forward, held lock or missing credentials, runs the committed installer from a pinned ref, verifies identity and smoke, and rolls back on failure. A new or changed launcher needs Mohamed's approval again. - Full record: docs/adr/0131-deploys-the-classifier-blocks-run-through-approved-user-level-launchers-that-refuse-unsafe-states-and-verify-or-roll-back.md
ADR-0132: One tree is edited and the deployment copy is built from it at a pinned version — which tree is edited at the cut-over
- Date: 2026-10-23 · Status: Accepted, option A (Mohamed, 2026-11-02, verbatim: "2 A"; his recorded position was "paackages is not for editing")
- Context: master holds
src/orchestrata_ultimate/(902 modules) andpackages/*/src(4,227 modules). Mohamed: "paackages is not for editing, uit is a copy of the code from src at a specific trime and version for deployment in that structure". ADR-0103/0113 and PyPA practice makepackages/<c>/srcthe single edited source after the cut-over and the built wheel the deployment copy. - Decision (proposed): A —
src/stays the edited source andpackages/is generated from it at a pinned commit; B — the same until the cut-over, thenpackages/<c>/srcis the only edited source and each package's built wheel at a pinned version is what deploys. Recommendation: B, the only option in which the cut-over completes. - Full record: docs/adr/0132-one-tree-is-edited-and-the-deployment-copy-is-built-from-it-at-a-pinned-version-which-tree-is-edited-at-the-cut-over.md
ADR-0133: orchestrata-mcp mints its own short-lived gateway token from the deployed JWT secret, and an exported GATEWAY_TOKEN still wins
- Date: 2026-10-23 · Status: Proposed (amends ADR-0099 rule 1's token wording if accepted)
- Context: every
mcp__orchestrata__*call fails withGATEWAY_TOKEN is not set. The deployed environment layer holdsJWT_SECRETand noGATEWAY_TOKEN; the gateway runs JWT auth with one-hour tokens, so a fixed token in a secrets file expires within the hour. - Decision (proposed): A — mint one token into the secrets file (works for an hour); B — the transport mints its own short-lived JWT from
JWT_SECRETat connect, subjectorchestrata-mcp, and an exportedGATEWAY_TOKENstill wins; C — static auth with a non-expiring token for every client. Recommendation: B. - Full record: docs/adr/0133-orchestrata-mcp-mints-its-own-short-lived-gateway-token-from-the-deployed-jwt-secret-and-an-exported-gateway-token-still-wins.md
ADR-0134: Dispatched Hermes workers get the read-only codetools verbs, the deployed Claude skills, the memory tool and the ast tool by default; no run writes skills
- Date: 2026-10-24 · Status: Proposed (deployed under Mohamed's instruction as Hermes release
da684a3; the limits await his ratification) - Context: every dispatched run got only
terminal, file, todo, so no worker had theasttool, the ADR-0121 memory tool, skills or MCP tools;/opt/orchestrata-tools/bin/asthad been dropped by every ship from master; the agent venv lacked itsmcpextra. - Decision (proposed): codetools MCP limited to its six approval-
noneread verbs;~/.claude/skillsreadable,skill_managerefused in every run; default toolsetsterminal, file, todo, ast, codetools, skills, memory; agent built with--extra mcpand verify refuses a home whose MCP servers cannot connect;SOUL.mdstates the harness rules; memory seeded by the agent's own tool;bin/aston master (9724ea1a6). - Full record: docs/adr/0134-dispatched-hermes-workers-get-read-only-codetools-claude-skills-memory-and-ast-by-default.md
ADR-0135: Nothing starts at boot; one approved command starts the whole system and verifies it
- Date: 2026-10-25 · Status: Accepted (Mohamed, 2026-10-25: "deliberate, keep off, do wiki entry but create a command that starts everything with one command"); item 3's dev sentence superseded by ADR-0170
- Context: after the T9 drive fell off USB (2026-10-24 18:33:40) and four reboots, nothing ran. All 30 orchestrata containers have
boot.autostart=falseand the user units were disabled at 2026-10-24 11:34:03. Restoring prod by hand took 7 container starts, a readiness wait, 5 unit starts and identity checks, and the rawincus startwas a classifier denial. - Decision: autostart stays off everywhere, prod included, and
infra/terraformdeclaresfalse(supersedes ADR-0077 item 6).orch-up, an ADR-0131 launcher, execs the deployed/opt/orchestrata-tools/bin/system-up:- it refuses unless the T9 filesystems are mounted;
- it starts prod (containers, Redis
PING, routed graph read, the 5 units with an interpreter/cwd identity check), then test (all_required_ok), then the wiki container; --with-dev,--only,--check;- exit 0 only when every part is verified.
- Full record: docs/adr/0135-nothing-starts-at-boot-and-one-approved-command-starts-the-whole-system-and-verifies-it.md
ADR-0136: Secrets are not encrypted; a worktree's .env is the primary checkout's plaintext, refused when it carries placeholders
- Date: 2026-10-25 · Status: Accepted (Mohamed, 2026-10-25: "i do not want api key security" · "just ship working code")
- Context: the SOPS + age store (
secrets/env.sops.env, d08aaf603) was encrypted to an age key this host no longer has (recipientage15v0hkn…, installedage1rj6ssa…since 2026-08-28), and its hook never fails, so no worktree got a decrypted.env. Plaintext copies came from whichever checkout was current, and git's main worktree held 56CHANGEMEplaceholders. - Decision: no encryption; the store,
.sops.yaml,secrets_cli.pyand thesops/agetool declarations are removed. A worktree's.envis the plaintext.envof git's main worktree, copied by one implementation (provision_worktree_env.py, used byworktrees.pyand the post-checkout/post-merge hooks); a source with aKEY=…CHANGEMEline is refused, naming the keys only. - Full record: docs/adr/0136-secrets-are-not-encrypted-a-worktree-env-is-the-primary-plaintext-refused-when-it-carries-placeholders.md
ADR-0137: The pre-commit framework config is removed; a gate counts only where an automatic runner executes it, and each orphaned gate is its own task
- Date: 2026-10-25 · Status: Accepted (Mohamed, 2026-10-25: "ignore secrets for now but agree with next 2 lines")
- Context:
core.hooksPathisscripts/hooksand nothing runspre-commit run, so none of the 19 hooks in.pre-commit-config.yamlever executed; two named scripts that do not exist, a test asserted the dead registration, and the docs called its gates "blocking". - Decision: delete the file. A gate counts as enforced only where the commit hook, ship, GitLab CI or the suite runs it on the real tree. Nine gates with no such point (check-deps,
gate.py run, generated-artifacts, feature-loss, untyped, check-traces, both pyright ratchets, doc-lint) each get a task. - Full record: docs/adr/0137-the-pre-commit-framework-config-is-removed-a-gate-counts-only-where-an-automatic-runner-executes-it.md
ADR-0138: Files are deleted through delete_file, which refuses directories; the Bash guard allows it
- Date: 2026-10-25 · Status: Accepted (Mohamed, 2026-10-25: "ignore secrets for now but agree with next 2 lines")
- Context:
intercept_bash.pyblocksrm/unlink/rmdir/shredwith no remedy, and the workspace rules name adelete_filefunction that never existed. - Decision:
python3 ~/.claude/tools/delete_file.py <file>...deletes regular files and symlinks (never following a link), refuses directories and missing paths, validates every argument before deleting any; the guard allows exactly that one simple command, with no shell control or substitution, and names it in its refusal. A directory still needs Mohamed's permission. - Full record: docs/adr/0138-files-are-deleted-through-delete-file-which-refuses-directories-the-bash-guard-allows-it.md
ADR-0139: The prod gate's full-suite run gets a 900-second test-phase cap; interactive runs keep 300
- Date: 2026-10-27 · Status: Rejected (Mohamed, 2026-10-27; asked on a false premise, see ADR-0140)
- Context:
scripts/run_tests.py_test_cap_seconds()caps the test phase at 300 s (ORCH_TEST_CAP_SECONDS); the recorded full-suite test phase is 458-489 s, so every low-reuse full run is cut: 524, 732 and 408 modules killed (rc=-9) on 26-27 Oct, including a quiet run. The prod gate needs a GREEN fullpromote_env.py test test, so seven ships since 22 Oct merged and then failed to promote. - Decision:
promote_env.py test testruns withORCH_TEST_CAP_SECONDS=900for that one run; every other run keeps 300; the cap's docstring stops citing RUL-038 (the container ruling). - Full record: docs/adr/0139-the-prod-gate-full-suite-runs-with-a-900-second-test-phase-cap-interactive-runs-keep-300.md
ADR-0140: The prod gate keeps RUL-038's 300-second test-phase cap; capped runs resume from the green ledger until one finishes
- Date: 2026-10-27 · Status: Accepted (Mohamed, 2026-10-27: "go" on the coordinator's recommendation) · Rejects ADR-0139
- Context: ADR-0139 (900 s for the prod gate) was asked on a false premise. The 300 s cap is RUL-038's own 2026-10-19/20 amendment, Mohamed verbatim: "a HARD 300 s cap on the test phase … record the modules that passed into the green ledger, and exit non-zero with
CAPPED" (landed4a470482c); the seven RUL-038 citations inscripts/run_tests.pyare correct. - Decision: every run, the prod gate's
promote_env.py test testincluded, keeps 300 s; a capped run's passed modules go into the RUL-032 ledger and the next run resumes; the gate is green when a run finishes inside the cap. Stop rule: a second consecutive capped gate run that adds no passed modules sends the 900 s question back to Mohamed. - Full record: docs/adr/0140-the-prod-gate-keeps-rul-038-s-300-second-test-phase-cap-capped-runs-resume-from-the-green-ledger-until-one-finishes.md
ADR-0141: Test selection and the green-ledger key read one edge set
- Date: 2026-10-29 · Status: Accepted (Mohamed, 2026-10-29: "1 yes")
- Context: selection reads the import closure, the manifest and reference edges, but the RUL-032 key reads only a
src/-only closure and manifest files. Data files read through a local path variable or ascripts.*import stayed outside the key, so two stale greens shipped (2026-10-25, 2026-10-29). Part (a), three manifest rules, shipped as P18e59f77efd. - Decision: the extractor follows local path variables;
key_inputshashes the reference-edge targets of the test and its key closure from the same fresh edge file; the key'sfrommatching admits the test's directscripts.*imports. About 65 modules re-key once. - Full record: docs/adr/0141-test-selection-and-the-green-ledger-key-read-one-edge-set.md
ADR-0142: Prod installs orchestrata non-editable, like dev
- Date: 2026-10-29 · Status: Accepted (Mohamed, 2026-10-29: "2 yes")
- Context:
/opt/orchestrata's venv is editable (direct_url.jsoneditable: true, a.pth->/opt/orchestrata/src), while dev has been non-editable since 2026-10-20. The ship skipsdepswhileuv.lockis unchanged, so prod never converged. - Decision: every promote runs
uv sync --no-editable --frozenand reinstalls the project package, and verify runscheck_source_provenance.pyagainst the prod tree, refusing unless site-packages holds the deployed bytes (about 2 s measured in dev). - Full record: docs/adr/0142-prod-installs-orchestrata-non-editable-like-dev.md
ADR-0143: The green-ledger key hashes what tests run on, not all of pyproject.toml
- Date: 2026-10-29 · Status: Accepted (Mohamed, 2026-10-29: "B")
- Context:
key_inputshashes the wholepyproject.toml, so the provenance change's[tool.uv] cache-keysedit re-keyed ~1,700 modules and took 10 capped ship passes. Meanwhileuv.lockis in no key; a manifest rule maps it to two modules, so a lock-only upgrade keeps every other green. - Decision: the key hashes the resolved
uv.lockpackage set (name, version, source),[project]dependencies and optional-dependencies,[build-system], and[tool.pytest.ini_options], as canonical JSON. Other sections leave the key and reach their readers through manifest rules. Ships with ADR-0141(b) so the one-time re-key is paid once. - Full record: docs/adr/0143-the-green-ledger-key-hashes-what-tests-run-on-not-all-of-pyproject.md
ADR-0144: knowledge-wiki tests run in their own test database, never on MEMGRAPH_URI
- Date: 2026-10-29 · Status: Accepted (Mohamed, 2026-10-29: "b")
- Context: each wiki test's label-less cleanup scans the whole shared
memgraphdatabase (667,135 nodes, 6.2-8.7 s per scan), so the suite reached ~81 of ~237 tests in 900 s against ship's 300 s. Its URI fixture readsMEMGRAPH_URI, which in a wiki shell is the prod orchgraph coordinator (port open). - Decision: the tests read only
TEST_MEMGRAPH_URI(no default, prod subnet refused) and run in awikitestdatabase (MemgraphClient(database=);driver()sessions default to it). Ship's wiki arm supplies the URI from the deployedenv/test.env;wikitestis provisioned besideorchkg. - Full record: docs/adr/0144-knowledge-wiki-tests-run-in-their-own-test-database-never-on-memgraph-uri.md
ADR-0145: Test-created host resources use the orchtest- namespace, and the runner reaps them
- Date: 2026-10-29 · Status: Accepted (Mohamed, 2026-10-29: "A"); item 1 amended by ADR-0146
- Context: a capped run's SIGKILL reaches only the test process group; a systemd user unit a test started (
orchestrata-p6b…loop.service,Restart=always) survived, and prod verify refused the promote ofbc3af3358. - Decision: test-created host resources are named
orchtest-…; test unit files start with# orch-run-id=<ORCH_RUN_ID>; after every run, capped or not,run_tests.pystops and deletes only this run's tagged units. No SIGTERM grace period. - Full record: docs/adr/0145-test-created-host-resources-use-the-orchtest-namespace-and-the-runner-reaps-them.md
ADR-0146: The run tag decides test-unit ownership; tests judged by prod verify keep their prod-scope names
- Date: 2026-10-29 · Status: Accepted (Mohamed, 2026-10-29: "A") · Amends ADR-0145 item 1
- Context: renaming
test_verify_settle_hold.py's units toorchtest-broke 2 of 27 tests: they drive the real prodverify, whose identity check discovers onlyorchestrata-*/orchgraph-*services. - Decision: the run tag decides ownership; the reaper takes this run's tagged files under
orchtest-*,orchestrata-*andorchgraph-*. Tests useorchtest-except those prod verify must judge, which keep prod-scope names viawrite_test_unit(..., in_prod_scope=True). Prod identity code is unchanged. - Full record: docs/adr/0146-the-run-tag-decides-test-unit-ownership-prod-scope-tests-keep-their-names.md
ADR-0147: An agent's done status is stored before it is announced; the listener reads it after subscribing
- Date: 2026-10-29 · Status: Accepted (Mohamed, 2026-10-29: "b")
- Context:
agent_notify.pypublishesagent:done:<task>before it setsagent:status:<task>, andagent_check --listenaccepts only messages, so a done published before the subscription is lost and the listener waits 3600 s. Retiring both scripts was offered and declined. - Decision: SETEX then PUBLISH in one MULTI/EXEC; the listener GETs the status key after the subscribe acknowledgement; each dispatch uses a fresh task id, 3600 s expiry unchanged.
- Full record: docs/adr/0147-agent-done-is-stored-before-it-is-announced.md
ADR-0148: Every Hermes run declares its worker environment; the harness composes it, validates it before the model starts, and runs the agent and every check in it
- Date: 2026-10-29 · Status: Accepted (Mohamed, 2026-10-29: "AGREECD GO"); items 2 and 5 amended by ADR-0151 (Mohamed, 2026-10-29: "2, agree 3,agree")
- Context: Hermes checks inherit the launcher shell's whole environment (no
env=); over 2,668 saved check executions none ran clean, nine were changed by a missed hand strip (one false red), and no run recorded its environment. - Decision: a compulsory ```env block (
venv,files,set,secretsby name only,import); the harness composes the environment from it alone, runs a preflight before the model starts, runs the agent and every check in it, refuses checks that edit their own environment, writesenv.json; phase 2 validates names against a newast env-reads. SPEC-117 is retargeted to this. - Full record: docs/adr/0148-every-hermes-run-declares-its-worker-environment.md
ADR-0149: No RAG and no embedding; the knowledge graph answers
- Date: 2026-10-29 · Status: Accepted (Mohamed, 2026-10-29: "NO MORE rag OR EMBEDDIING THAT WAS ALREADY DECIDED"); item 4 answered by ADR-0150 (Mohamed, 2026-10-29: "1, no")
- Context: Mohamed's 2026-10-14 goal is that the knowledge graph (AST + history) answers project questions; the 2026-10-19 audit found 1 confirmed RAG retrieval in 33 days and no production reader, and he ruled "drop injest" (the promote has been KG-only since). The ADR promised then was never written, so stale guidance kept describing an embedding stage.
- Decision: orchestrata has no RAG and makes no embedding call; questions are answered from
orchkgand the code-intelligence KB. ADR-0074 and ADR-0076 are superseded. The remaining RAG/embedding code is retired through Hermes runs after an AST inventory; the knowledge-wiki's own RAG waits for Mohamed's answer on scope. - Full record: docs/adr/0149-no-rag-and-no-embedding-the-knowledge-graph-answers.md
ADR-0150: The knowledge-wiki keeps its own RAG; ADR-0149 binds orchestrata only
- Date: 2026-10-29 · Status: Accepted (Mohamed, 2026-10-29: "1, no")
- Context: ADR-0149 item 4 left open whether "no more RAG" covers the knowledge-wiki's own RAG (
rag-store,wiki-rag-query, the ship's "wire (re-index RAG)" step, which embedded 86,194 chunks locally with SentenceTransformers, dim 384, on the 2026-10-29 ship). - Decision: it does not. The wiki keeps its RAG; the ADR-0149 clean-up touches orchestrata's tree only. An orchestrata call into the wiki RAG is still an orchestrata RAG use and is retired.
- Full record: docs/adr/0150-the-knowledge-wiki-keeps-its-own-rag-adr-0149-binds-orchestrata-only.md
ADR-0151: The Hermes agent keeps every provider key for the fallback, and env-reads refuses only unread set names
- Date: 2026-10-29 · Status: Accepted (Mohamed, 2026-10-29: "2, agree 3,agree")
- Context: ADR-0148 item 2 gave the agent only the active provider's key (deployed phase 1 gives every provider key, for the R7 fallback); item 5 refused any unread
setname or env-file key, but profiles carry keys read outside the tree. - Decision: the agent gets every provider key the launcher holds; checks get none. Phase 2 refuses an unread
setname and lists unread env-file keys as findings inenv.jsonand the verdict, without refusing. - Full record: docs/adr/0151-the-hermes-agent-keeps-every-provider-key-for-the-fallback-and-env-reads-refuses-only-unread-set-names.md
ADR-0152: ADR-0149 retirement scope: volk semantic roots move to the KG, EmbeddingService stays with a stable hash, the rag_context hook is retired
- Date: 2026-10-30 · Status: Accepted (Mohamed, 2026-10-30: "1 agree, 2 agree, 3 agree")
- Context: the ADR-0149 inventory found a production volk-context reader of the frozen RAG chunk nodes (
semantic_bridge.search_semantic_roots), a local hash vectorizer (memory/embedding.EmbeddingService) whose builtinhash()is salted per process so stored memory vectors never match across processes, and a hook tool (claude/tools/rag_context.py) querying the wiki's RAG. - Decision: semantic roots are rebuilt on
orchkgcode nodes and the chunk read is deleted;EmbeddingServicestays outside ADR-0149 with a stable hash and a two-process test;rag_context.pyis retired; the rest of the inventory's retire list goes as ADR-0149 item 3 says. - Full record: docs/adr/0152-adr-0149-retirement-scope-volk-semantic-roots-move-to-the-kg-embeddingservice-stays-with-a-stable-hash-the-rag-context-hook-is-retired.md
ADR-0153: Volk semantic roots look up KG symbols through an indexed name token, not a scan
- Date: 2026-10-30 · Status: Accepted (Mohamed, 2026-10-30: "ADR-153 approved")
- Context: ADR-0152 item 1 moves the volk semantic-roots reader to
orchkgcode nodes. A substring match over the ~63KFunction/Class/Methodnodes of the project has no index: 1.35 s wall when terms match, 5.7-6.6 s when they miss (test graph, 2026-10-30), on every volk context assembly. There are noFilenodes;Modulecarriesfile_pathandline_count. - Decision (proposed): the AST-to-graph sync writes
NameToken {token}nodes (lowercase words of symbol names, split on_and CamelCase) withHAS_TOKENedges and an index on:NameToken(token); the reader matches terms by index lookup and maps hits toModule. Alternatives: a time-budgeted scan; the AST snapshot; Memgraph's experimental text index. - Full record: docs/adr/0153-volk-semantic-roots-look-up-kg-symbols-through-an-indexed-name-token-not-a-scan.md
ADR-0154: delete_file also removes empty directories; a non-empty directory still needs Mohamed's permission
- Date: 2026-10-30 · Status: Accepted (Mohamed, 2026-10-30: "fix the guard to allow empty dirs") · Amends ADR-0138
- Context: after a merged worktree was force-removed, an empty
wt-MEM-TRIGGER/plans/remained; with Mohamed's go,rmdirwas blocked by the Bash guard anddelete_file.pyrefused it as a directory, so no sanctioned path existed. - Decision:
delete_file.pyremoves a directory that is empty, or whose every entry is also named on the command line, withos.rmdirdeepest first after the files (the kernel refuses a non-emptyrmdir); any other directory is refused as before. The guard's text patterns are unchanged; its block reason names empty directories. - Full record: docs/adr/0154-delete-file-also-removes-empty-directories-a-non-empty-directory-still-needs-mohameds-permission.md
ADR-0155: ADR-0140's graph body is re-baselined from its docs record
- Date: 2026-10-30 · Status: Accepted (Mohamed, 2026-10-30: "ADR-0140 re-baseline agree") · Authorises
--rebaseline-idfor ADR-0140 (ADR-0096 mechanism) - Context:
check_adr_graph_sync.pyfailed on every sync withADR-0140 BODY_DRIFTon the test graph; the docs record has one commit (8460248ba) and never changed, and the prod graph reported no drift. - Decision: re-baseline the test-graph node's body from
docs/adr/0140-...mdwith--rebaseline-id ADR-0140 --authority ADR-0155; ADR-0140's decision is unchanged. - Full record: docs/adr/0155-adr-0140s-graph-body-is-re-baselined-from-its-docs-record.md
ADR-0156: The prod graphs stage stops enriching the semantic database with specs; nothing reads them
- Date: 2026-10-30 · Status: Rejected (Mohamed 2026-11-01; superseded by ADR-0159)
- Context:
enrich_semantic_graph_specs.py, the last step of every prod graphs stage, took 7:03 and 4:59 on 2026-10-30 and writes ~43K spec/trace nodes and relationships into the semanticmemgraphdatabase; no code in orchestrata, the knowledge-wiki or~/.claudereads those labels. The testorchkgholds 0Requirement/Specnodes and 0TRACES_TOedges, so the semantic database is the only graph copy of the specs. - Decision (proposed): drop the step from
bootstrap_graphs._kg_stepsand delete the script,spec_trace_enricher,spec_trace_parserand their tests; populatingorchkgwith requirements stays with the open P1 requirement-tier item. - Full record: docs/adr/0156-the-prod-graphs-stage-stops-enriching-the-semantic-database-with-specs-nothing-reads-them.md
ADR-0157: A failed promotion is fixed forward and re-shipped; the runner never rolls back
- Date: 2026-10-31 · Status: Accepted
- Mohamed, verbatim: "focus on shipping spin up a swarm if needed to fix and never roll back, make the ship happen"; "failure must be fixed and reshipped not rolled back".
- Decision:
promotion/runner.pydeploy()stops at the first failed stage and leaves the target on the sha it advanced to; it never calls_rollback. The fix ships forward (Hermes runs, commit, re-rundeploy/ship.sh).DeployReport.rolled_backstays in the record, alwaysFalse. - Full record: docs/adr/0157-a-failed-promotion-is-fixed-forward-and-re-shipped-the-runner-never-rolls-back.md
ADR-0158: Reads are balanced across main and a synced replica
- Date: 2026-11-01 · Status: Accepted
- Mohamed, verbatim: "can we do load balancing on memgraph to query 2 insttances at the same time or when one is under pressure?", "after we confirm they are synced", then "yes".
- Decision: every orchgraph HA cluster sets
enabled_reads_on_main = trueandmax_replica_read_lag = 0(unit: transactions), applied byinfra/rehearsal/enroll_orchgraph.py; reads go to main and replica, least-connected first, and the replica serves reads only while fully synced. Writes stay on main. - Full record: docs/adr/0158-reads-are-balanced-across-main-and-a-synced-replica.md
ADR-0159: RTM data lives in the operational graph, built from the @traces the AST carries from code
- Date: 2026-11-01 · Status: Accepted
- Mohamed, verbatim: "why retire spec trace? we need it for rtm"; "will the code remain marked with @trace? it must be so we can regenerate"; "rtm data beloiongs in the graph, but the ast will contain it from code"; "move rtm data to graph, but @traces remain".
- Decision:
@tracesstays on the code as the RTM's source. The graphs stage builds the requirement tier (Spec, Requirement, trace edges from code and tests) inorchkgfrom the@tracesthe AST snapshot carries plusspecs/. The semantic-database writer (enrich_semantic_graph_specs) stays untilorchkgholds the RTM and a gate proves the counts match the files. Rejects ADR-0156. - Full record: docs/adr/0159-rtm-data-lives-in-the-operational-graph-built-from-the-traces-the-ast-carries-from-code.md
ADR-0160: Every push is recorded in full, with its exit code and its landing
- Date: 2026-11-01 · Status: Accepted
- Mohamed, verbatim: "i want the exit codes and data from pushes to be logged in full for futue reference automatically".
- Decision:
scripts/record_push.pywraps the whole pre-push hook, streaming its output unchanged and keeping a full per-push log in~/.local/state/orchestrata/pushes/.pushes.ndjsongets astartrow, agaterow with the exit code, and alandedrow written by thereference-transactionhook when git moves the remote-tracking ref, the only local proof that the remote accepted the push. - Full record: docs/adr/0160-every-push-is-recorded-in-full-with-its-exit-code-and-its-landing.md
ADR-0161: Ship's verified pushes go through the push record
- Date: 2026-11-01 · Status: Accepted
- Executes Mohamed's ADR-0160 directive, verbatim: "i want the exit codes and data from pushes to be logged in full for futue reference automatically".
- Decision: ship pushes with
--no-verifyat two sites after running the gate's work itself, which skips pre-push, so the first ship carrying the recorder recorded nothing. Both sites now run throughscripts/record_push.py push, which keeps the output and writes apushrow with the real exit code ofgit push. This supersedes the ADR-0160 consequence that called--no-verifybanned. - Full record: docs/adr/0161-ships-verified-pushes-go-through-the-push-record.md
ADR-0162: Worktree code nodes leave prod orchkg after their writer is closed
- Date: 2026-11-02 · Status: Accepted (delegated ruling)
- Mohamed, verbatim: "i delelgate you to rule on the rulings but document your choices and rule in kine with my policies".
- Prod
orchkgholds 23,032wt-*nodes across 13 projects and the count is growing, including 18 realConversationTurnnodes under the same project values. Close the writer first, then delete code-intelligence labels only;ConversationTurnstays and its count is checked before and after. - Full record: docs/adr/0162-worktree-code-nodes-leave-prod-orchkg-after-their-writer-is-closed.md
ADR-0163: A deployed tree is never rewritten after deploy
- Date: 2026-11-02 · Status: Accepted (delegated ruling)
- The
/opt/orchestrataAST daemon runs with--publishand rewrites the deployedplans/ast-*.json, so the release differs from its recorded commit. The daemon refuses--publishunder/opt; readers needing a fresher snapshot read one outside the release tree. - Full record: docs/adr/0163-a-deployed-tree-is-never-rewritten-after-deploy.md
ADR-0164: Skipped verification and rollback get no exception; only ship's recorded push stays
- Date: 2026-11-02 · Status: Accepted (delegated ruling)
- Hand-typed
--no-verifyon commit or push and theship.sh --no-verifyflag are not used, and get a mechanical guard. Ship's internal recorded push stays per ADR-0161. A failed promote is fixed forward;deploy_rollbackruns only when Mohamed names it. - Full record: docs/adr/0164-skipped-verification-and-rollback-get-no-exception-only-ships-recorded-push-stays.md
ADR-0165: sync_traces_to_graph is retired
- Date: 2026-11-02 · Status: Accepted (delegated ruling)
- The script has 0 importers and no deploy reference, while the graphs stage already holds 16,443/16,443
TRACES_TOedges without it. Retire the script, its test module and its hook lines;TRACES_TOkeeps one writer. - Full record: docs/adr/0165-sync-traces-to-graph-is-retired.md
ADR-0166: python-dev is merged selectively and its test advice is rejected
- Date: 2026-11-02 · Status: Accepted (delegated ruling)
- Installed project skills stay the authority. Memory, algorithms, bytecode, C-extension, free-threading, language, debugging and GoF references are adopted with a project-skills-win banner; pytest parts 1-2 are rejected; uv-packaging is adopted with its
uv pipand editable passages cut; python-dev itself is not installed. - Full record: docs/adr/0166-python-dev-is-merged-selectively-and-its-test-advice-is-rejected.md
ADR-0167: The IoC design is ruled for the new project only, and the old tree keeps its rules
- Date: 2026-11-02 · Status: Accepted (delegated ruling)
- R1-R19 of the IoC design take its recommendations, with R10, R11 and R18 scoped to the new project only. The repo is
orchestrata.platform. RetiringPipelineKernelClientneeds its own ADR. ADR-0132 stays Proposed and returns to Mohamed, because its recommended option conflicts with his own words and the harvest does not depend on it. - Full record: docs/adr/0167-the-ioc-design-is-ruled-for-the-new-project-only-and-the-old-tree-keeps-its-rules.md
ADR-0168: ADR-0162 is rebaselined to drop a time not taken from a clock read
- Date: 2026-11-02 · Status: Accepted (delegated ruling)
- ADR-0162's provenance gave a 03:30 that was never read from a clock. Dropping it changed a frozen body, so this ADR authorises one ADR-0096 re-baseline of ADR-0162. Its decision text is unchanged.
- Full record: docs/adr/0168-adr-0162-is-rebaselined-to-drop-a-time-not-taken-from-a-clock-read.md
ADR-0169: The growth claim in ADR-0162 is withdrawn
- Date: 2026-11-02 · Status: Accepted (delegated ruling)
- ADR-0162's "growing" had no valid basis: the count was still 23,032 about 45 minutes later, code nodes sit in 8 projects rather than 13, and the nodes carry no timestamp. ADR-0162's decision stands; its first step becomes establishing whether any writer is still active.
- Full record: docs/adr/0169-the-growth-claim-in-adr-0162-is-withdrawn.md
ADR-0170: The dev fleet starts in the default system-up run
- Date: 2026-11-02 · Status: Accepted (delegated ruling)
start_test_servicesrequiresdev-fleetand the drift unit plans the dev containers, so a default cold start exited 1 after 300 s and the failed drift unit blocked prod verify. The default groups becomeprod test wiki devand--with-devis removed. This supersedes the dev sentence of ADR-0135 item 3; the cost is the dev fleet's CPU load.- Full record: docs/adr/0170-the-dev-fleet-starts-in-the-default-system-up-run.md
ADR-0171: Test selection, the green ledger and batching move to codetools
- Date: 2026-11-02 · Status: Accepted (Mohamed, 2026-11-02, verbatim: "yes to AKK"); batching superseded by ADR-0172
- Selection, the RUL-032 ledger and batching hold no orchestrata knowledge and move to codetools;
start_test_servicesand kernel verification stay, and Hard Rule 7 is unchanged. The codetools release is installed first, then an orchestrata ship removes the originals. Supersedes ADR-0118 item 5 for these three parts only. The first step compares them with the selection and ledger verbs codetools already has. - Full record: docs/adr/0171-test-selection-the-green-ledger-and-batching-move-to-codetools.md
ADR-0172: Test batching stays in orchestrata
- Date: 2026-11-02 · Status: Accepted (Mohamed, 2026-11-02, verbatim: "yes, yes")
- ADR-0171's recommendation missed codetools ADR-0060 (orchestrata keeps its runner; codetools decides what runs). Batching stays in orchestrata; selection and the ledger still move, after the P3 replay.
codetools-memgraphholds the replay's rows as a stopgap; gates plan P13 stays open. - Full record: docs/adr/0172-test-batching-stays-in-orchestrata.md
ADR-0173: CI's test job verifies the ship's recorded authoritative run instead of re-running the suite
- Date: 2026-11-04 · Status: Accepted (Mohamed, 2026-11-04: chose "Verify ship's record (Recommended)")
- The first self-hosted CI pipelines (3025522050, 3025588891) showed the
testjob is a pre-ruling design: full suite with--skip-infra(no kernel, non-authoritative), retired Neo4j service, and a second run of a code state the ship already tested (RUL-032); it also exits 1 silently. Proposed: CI verifies a PASS authoritative record atCI_COMMIT_SHAand fails without one; coverage-gate reads that run's coverage. - Full record: docs/adr/0173-ci-test-job-verifies-the-ships-recorded-run.md
ADR-0174: A static gate whose inputs are content-identical to a passing run is reused, not re-run
- Date: 2026-11-05 · Status: Accepted (Mohamed, 2026-11-05: "agree go")
- A ship ran the five safety gates twice on the same commit (verify 34.6 s, prod gate 34.7 s; ship 1105-h). Parallel safety, test purity and Prolog boundary record a PASS keyed by the content of every file they read; rulings lint keys on the wiki rulings store; a matching PASS is reused. Source provenance always runs. FAILs are never reused; keys never use file age or tree.
- Full record: docs/adr/0174-a-static-gate-whose-inputs-are-content-identical-to-a-passing-run-is-reused.md
ADR-0175: An orchestrata ship over 300 seconds finishes every stage, then fails
- Date: 2026-11-05 · Status: Accepted (Mohamed, 2026-11-05: "Fail after, never kill")
- Mohamed: "reduce timeout to 5 mins, longer than that, you failed". An orchestrata ship has a 300 s budget from start to the end of its last stage. The budget never interrupts a stage (a kill mid-promote can leave prod half-deployed); the ship finishes, prints
BUDGET <s>/300 s OKorOVER BUDGET <s>/300 s, and exits 3 when over. The number is a constant with no override. Measured: ship 1105-k 281 s, 1105-j 477 s. - Full record: docs/adr/0175-an-orchestrata-ship-over-300-seconds-finishes-every-stage-then-fails.md
ADR-0176: The test preflight leaves the trace_intel KBs to their consumers
- Date: 2026-11-06 · Status: Rejected (Mohamed, 2026-11-06: "Keep it, find the 83.8 s")
- The freshness guard's prolog-kbs check refreshes
.cache/astand the trace_intel KBs before every test run: 104.2 s on ship 1105-l (snapshot-refresh 83.8 s), which went OVER BUDGET at 403 s. Its only readers (trace_intel, derive_rtm, history_intel and their tests) callensure_kb()themselves; no test reads them unrefreshed. Proposed: run_tests runs the guard without that check; a hand-run guard keeps all six. - Full record: docs/adr/0176-the-test-preflight-leaves-the-trace-intel-kbs-to-their-consumers.md
ADR-0177: The prod gate accepts the ship's verify run across its evidence-only commit
- Date: 2026-11-06 · Status: Accepted (Mohamed, 2026-11-06: "A")
- A ship ran the suite twice: verify on commit T, then
promote_env.py test teston the evidence commit P (record the authoritative run for T, which changes onlyreports/), because the prod gate wanted a record of exactly P. About 43 s of ship 1106-b's 120 s promote. Now the gate also accepts a GREEN record of T when T is an ancestor of P and every path changed since T is underreports/; the ship writes that record from its verify report withpromote_env.py record-verified test. - Full record: docs/adr/0177-the-prod-gate-accepts-the-ship-verify-run-across-the-evidence-commit.md
ADR-0178: Infra capabilities are tested once each against a small real fixture codebase, never the whole repo
- Date: 2026-11-06 · Status: Superseded by ADR-0179 (Mohamed, 2026-11-06: "it is not about the whole repo, or amount of tests it is about test case coverage"); was Accepted (Mohamed, 2026-11-06: "adr 0178 approveed")
- Mohamed: "we make sure we can read and write, we cover ever case we need but only once, like can write a node, can i write an edge, if I have a class with methods, will the code produce the right calls to write them, if i have a module, same thing etc? but we do not need a huge codebase for that". Each capability (node write, edge write, one per node and relationship type, class + methods, module + imports, calls) gets one test against a small committed fixture package of real code, parsed by the real code into the real graph/Redis (codetools ADR-0047: real, never simulated). A test that reads the whole repo's AST snapshot has every file in its ledger key (ADR-0088), so it re-runs on every commit; a fixture test re-runs only when its own code changes. Whole-repo checks stay ship gates, run once.
scripts/test_input_inventory.pymeasures the conversion. - Full record: docs/adr/0178-infra-capabilities-are-tested-once-each-against-a-small-real-fixture-codebase-never-the-whole-repo.md
ADR-0179: Tests are organised by a case catalogue, each case listed once and proved by exactly one test
- Date: 2026-11-06 · Status: Accepted, implementation deferred (Mohamed, 2026-11-06: "yes, write ADR-0179 but not yet on the implementation, i want to know what the ship takes in terms of time and how much we improved it, and examine the gates and make them efficient first")
- Supersedes ADR-0178, which centred on whole-repo inputs and ledger re-runs; Mohamed: "it is not about the whole repo, or amount of tests it is about test case coverage". For each capability a catalogue lists every distinct case it must handle (node of each type, edge of each relationship type, class with 0/1/N methods, module with/without imports, call from function/method, edge to a missing endpoint, duplicate write, empty module, unparseable file, ...). Each case is proved by exactly one test on the smallest real input that exhibits it, asserting the exact result, against the real backend (codetools ADR-0047). Coverage is reported as covered / listed cases, with gaps (no test) and redundancy (many tests) named; line coverage does not substitute. Implementation waits until the ship's gates are made efficient.
- Full record: docs/adr/0179-tests-are-organised-by-a-case-catalogue-each-case-listed-once-and-proved-by-exactly-one-test.md
ADR-0180: A per-file static gate re-checks only the files whose content changed
- Date: 2026-11-06 · Status: Accepted (Mohamed, 2026-11-06: "approved")
- Amends ADR-0174, whose whole-input key never matches on a ship: one changed test file re-scans all 2,010 (parallel safety 17.7 s, test purity 15.1 s, Prolog boundary 3.6 s standalone; 27.7 s together on ship 1106-j, the last thing the test phase waited for). Proposed: gates whose verdict is a union of per-file verdicts (parallel-safety 0.1/0.2/0.4/0.5/0.6, test purity, Prolog boundary) record a PASS per file keyed by the gate's code, its shared inputs and the file's content; a run checks only unmatched files. FAIL never recorded; content keys only; a gate that follows other files keys them too or keeps the whole-input key.
- Full record: docs/adr/0180-a-per-file-static-gate-re-checks-only-the-files-whose-content-changed.md
ADR-0181: REQ-109-042 to 044 retire with the RefactorRenameTool ADR-0117 deleted
- Date: 2026-11-07 · Status: Accepted (Mohamed, 2026-11-07: "Retire with the tool (Recommended)")
- Executes ADR-0117 decision 1. The three ids specify the SPEC-072 BaseTool adapter (
refactor/rename/tool.py) that the move to codetools deleted on purpose; codetools has no counterpart.specs/109-rename-engine/MOVED.jsonlists them underretirednaming this ADR, andcheck_feature_loss.pyaccepts a retired id only with an existing ADR file. The other 59 ids are transferred to codetools SPEC-020. - Full record: docs/adr/0181-req-109-042-to-044-retire-with-the-refactorrenametool-adr-0117-deleted.md