| R1 |
Desktop packaging chain (Burrito/ex_tauri) lags OTP or lacks Windows |
High |
High |
Slice 001 spike fails on a target OS |
Spike first; fallback matrix: ex_tauri → plain Tauri sidecar with hand-rolled Rust → elixir-desktop → local-server+tray |
001 |
| R2 |
macOS notarization / Windows HSM signing friction |
High |
Med |
101 blocked on certs |
Start cert procurement at M0; Windows 460-day cert cap (from Mar 2026) noted; unsigned dev builds allowed until 101 |
101 |
| R3 |
EXLA/Bumblebee binary size and RAM on user machines |
Med |
Med |
032 measurement > 300 MB or embed > 500 ms |
Behaviour lets us switch to hosted embeddings; or Ortex despite stall |
032 |
| R4 |
sqlite_vec extension not loadable inside Burrito bundle |
Med |
Med |
032 on packaged build |
Vendor the extension in priv/; fallback Nx brute-force over an ETS cache |
032 |
| R5 |
req_llm breaking changes / provider drift |
Med |
Med |
Live tests fail after bump |
Behaviour isolates it; LangChain-Elixir fallback |
011 |
| R6 |
LiveView streaming performance (token spam) |
Med |
Low |
UI lag at > 20 msg/s |
Coalescing broadcaster; phoenix_streamdown |
013 |
| R6 |
Closed 2026-09-20 at slice 013: the Session coalesces to at most 20 broadcasts a second (012) and the page renders once per broadcast, measured at 6 renders for 1,000 deltas (013 PROOF.md, AC7). The renderer is mdex, not phoenix_streamdown, which never left beta (013 NOTES.md). The row above stands as written |
none |
none |
none |
none |
013 |
| R7 |
Context compaction loses critical information |
Med |
High |
Eval set regression |
Always-on tier preserved verbatim; compaction eval harness in 023 |
023 |
| R8 |
Prompt injection via tool output / skills / web |
High |
High |
none |
Untrusted-content framing; scanner on skills; permission gate on side effects; see 07-security-model |
021, 041 |
| R9 |
Oban on SQLite limitations (no Pro workflows) |
Low |
Med |
Need for multi-step durable graphs |
Postgres path kept alive in CI matrix |
050 |
| R10 |
Scope creep beyond a change's stated specification |
Med |
Med |
The engineering record shows work outside the specification |
The conventions in docs/03; recorded follow-ups; maintainer review |
all |
| R11 |
Stale or single-maintainer libs (boundary, hnswlib, ex_tauri, sqlite_vec, nostrum, telegex) |
High |
Med–High |
No release in 6 months. Measured 2026-09-05: boundary 2024-09-25, sqlite_vec 2024-11-19, telegex 1.9.0-rc.0 2024-09-18, nostrum 2025-03-02, the trigger already fires for four of them |
All behind behaviours; vendor if needed. boundary is the highest-consequence one: ADR-0001, docs/01 and the engineering rules in docs/03 all rest on it, and it is unverified on Elixir 1.20. Probe it before anything is built on it |
000, 032, 072 |
| R12 |
Secrets leak into logs/DB/commits |
Low |
High |
grep hits in CI |
mix gate includes a secret scan (gitleaks-style regex) from 000; Secrets module from 100 |
000, 100 |
| R13 |
Scope drifts toward matching other agents' breadth instead of shipping depth |
Med |
Low |
Slice scope grows during a phase |
Non-goals in docs/00 are binding; breadth is a later decision, not a default |
none |
| R14 |
beam_mcp's will-not-implement page keeps MRTR, the client role and OAuth out of the core; each is Trinity's to build above it (re-scoped 2026-09-20, was library lag) |
High |
Med |
059 FINDINGS names a gap 060 to 062 did not plan for |
The layering rule in ADR-0007 decision 6; the :server seam asked for, with the fork as recorded fallback; boundaries around Trinity.MCP.* |
059 |
| R15 |
Closed 2026-09-20: the server core is beam_mcp, Apache-2.0 (ADR-0007 decision 5); no LGPL candidate remains |
none |
none |
none |
none |
059 |
| R20 |
Foundation donation may require transferring assets or marks the project intends to keep |
Med |
Med |
Proposal drafting (122) |
Unverified: the requirement is asserted from an announcement, not from the charter text. Read the charter, then decide what is offered and what is retained. Legal review before any proposal leaves the tree |
122 |
| R21 |
The tree carries IP that is not this project's to publish |
Med |
High |
Any design in Trinity that reproduces a third party's protected mechanism |
Trinity's receipt and policy design is its own. Policy identifiers stay out of signed_payload until legal review clears them; 024's field set is reviewed before that slice starts |
024, 120, 122 |
| R21 |
Answered 2026-09-24 for the receipt field set, which was the open half. Slice 024 proposed the signed_payload field set for review and built "the default until cleared"; nothing cleared it until now, and slice 120's AC3 could not be met while that was true. Two questions, both now answered from published standards rather than from instinct. Does the field set reproduce a third party's protected mechanism? No, because it contains no mechanism anyone owns: prev_hash with seq is a forward-integrity hash chain (Bellare and Yee 1997; Schneier and Kelsey 1998), key_id is COSE kid, scheme occupies COSE alg's role, at is RFC 3339, subject is in-toto's Statement subject, chain_scope is Certificate Transparency's per-log scoping, fingerprint is an RFC 8785 canonical-digest commitment and decision is XACML's PDP outcome. IETF RFC 9943 (Standards Track, June 2026) standardises this whole object class as COSE_Sign1 Signed Statements and Receipts, which settles it: an open standard for the thing means the mechanism is nobody's to protect. May policy identifiers enter signed_payload? No, and the existing placement in unsigned meta is confirmed rather than merely tolerated. SLSA's attestation model separates externalParameters (untrusted, MUST be included and verified downstream) from internalParameters (platform-set, trusted, OPTIONAL, not verified downstream); a policy identifier is the second kind. Field-tier minimisation (arXiv 2608.25474) gives the governing property: the ledger commits to a canonical digest of the full parameters computed before minimisation, so "reclassifying a field changes what is disclosed without invalidating a historical entry, reopening a hash, or altering what an offline verifier checks" - Trinity already holds that commitment as the fingerprint, and moving policy identifiers into the signed bytes would convert a revisable disclosure decision into an immutable one for no gain, since a downstream verifier cannot evaluate Trinity's policy anyway and decision.basis already names the layer that decided inside the signed bytes. The original hazard also holds: if policy identifiers ever come from the external authority plane, embedding another party's namespace in immutable signed bytes is exactly what this row warns about, and in meta a namespace change is a metadata change rather than a scheme bump. The rule, so this is not re-litigated: the signed payload carries the commitment and the decision facts; anything platform-set, revisable or another party's namespace goes in meta. The row above stands for everything other than the receipt field set |
Low (was Med: the named open question is closed) |
High |
The lift condition slice 024 named, met |
docs/06-risk-register.md R21; slice 120 AC3 |
024, 120 |
| R21 |
Amended 2026-09-26, appended on the owner's instruction after slice 026. Chain-integrity fields generated by this node (seq, prev_hash, clock) are commitments. They stay in the signed payload so a merge cannot be reordered by rewriting unsigned meta. "Platform-set" in the original sentence means revisable transport and operator annotations, not the values that make two chains one. |
Low |
High |
The lift condition slice 024 named, met |
docs/06-risk-register.md R21; slice 026 NOTES |
024, 026, 120 |
| R22 |
Single maintainer; Growth needs two unaffiliated production users and commits from two orgs |
High |
Med |
6-month Sandbox checkpoint |
Sandbox tolerates it; GOVERNANCE.md documents intent to grow; recruit co-maintainers via the Elixir community once public |
120, 122 |
| R22 |
Partly mitigated 2026-09-23: two further maintainers, Zach Mead and Mike Hostetler, joined with review, merge, tag and release rights (MAINTAINERS.md; GOVERNANCE.md "Continuity"). What that closes is availability, which is what the OpenSSF criterion access_continuity asks for: the project can accept a change and publish a release without any one person. What it does not close is knowledge or independence. Every commit in the tree to date has one author, so a "bus factor" of two or more in the sense the criteria mean it (the number who would have to disappear before the work stalls for lack of competent personnel) is being established rather than claimed, and the badge answer says so; and organisation administration, the domain and the security mailbox still rest with the author alone, which GOVERNANCE.md names as the first thing a reader should ask about. Lift condition, checkable by a stranger: reviews and merged changes from a maintainer other than the author in the git history, and the administrative access documented as recoverable. The row above stands as written |
Med |
Med |
The lift condition above, unmet |
GOVERNANCE.md "Continuity"; MAINTAINERS.md |
120, 122 |
| R24 |
A native NIF in the desktop bundle: mdex_native (the markdown renderer's Rust core, added at 013) is the first, and slice 001 proved the packaging chain with a pure-BEAM release |
Med |
Med |
The package workflow fails on one of its three operating systems after 013, or 100's cross-target build finds no artifact for a target |
rustler_precompiled ships artifacts for every target the chain builds for (013 NOTES.md, measured against the checksum file); the package run on 013's mix.lock change is the first proof on three runners; lift condition: that run green on all three, cited in 013's PROOF.md; a fallback is MDEX_NATIVE_BUILD=1 with the pinned Rust toolchain already in the tree |
013, 100 |
| R24 |
Fired 2026-09-20, in slice 013: neither precompiled mdex_native artifact loads in Burrito's musl ERTS (both need glibc's libgcc_s; _dl_find_object: symbol not found). Lifted the same day by the owner's decision: the linux package builds the NIF from source for musl with Zig as the linker (scripts/zig-cc-musl, config/config.exs, rustler at build time), measured on a fresh install of the packaged binary rendering the real provider's answer; the --smoke path now prints TRINITY_SMOKE_MARKDOWN=ok and exits 3 otherwise, and the package workflow reads that line on every target, so a dead NIF cannot pass again. Residual: macOS and Windows use the precompiled artifact, and the package workflow has yet to run green there since the slice/011 tag (an older failure at "Serves HTTP 200", now diagnosed by the step's log). The row above stands as written |
none |
none |
none |
none |
013 |
| R24 |
Residual closed 2026-09-24. The row above left one open: macOS and Windows use the precompiled mdex_native artifact and the package workflow had not run green there since the slice/011 tag. It has now, six times consecutively, on the runs for slice/090.1, 027, 028, 029, 120 and main, each with linux x86_64, macOS aarch64 and windows x86_64 all passing (gh run list --workflow=package.yml). The lift condition the row named is met. What is not closed by this, and is worth saying so nobody reads more into it than it carries: these runs package the tree, they do not install the artifact on a mac or a Windows machine and watch it render, which is what finally caught the musl defect on linux. The rows above stand as written |
none |
none |
none |
none |
013, 100 |
| R23 |
EMA / ID-JAG is beta everywhere (vendors label it so); IETF draft still moving |
Low (re-scoped 2026-09-22) |
Med |
Spec revision breaks the exchange at the external authorization server, which is where it happens: Trinity redeems no assertion since the owner's decision of 2026-09-22 (slice 062 NOTES) |
The resource server validates whatever audience-bound token that server issues, so a draft revision is that server's upgrade, not Trinity's; the row stays until the deferral lifts |
062 |
| R25 |
glib 0.18.5 carries GHSA-wrw7-89jp-8q8g (medium, unsoundness in Iterator and DoubleEndedIterator impls); first patched in 0.20.0 |
Low |
Med |
The advisory is open against src-tauri/Cargo.lock; measured 2026-09-23 |
It is transitive through the GTK 0.18 stack (gtk 0.18.2, webkit2gtk 2.0.2) that Tauri 2.11.5 requires on Linux, so it is fixed by a Tauri release built on gtk-rs 0.20 and not by a direct bump. Scope: the desktop shell only. The headless release is steps: [:assemble] with no Burrito and no src-tauri, so a server deployment does not ship this dependency. Owner: the maintainer. Lift condition: a Tauri release whose Linux stack resolves glib >= 0.20, adopted by the desktop shell work |
100 |
| R26 |
An intermittent Exqlite.Error: Database busy fails the test suite on loaded CI runners, in a different test each time, always on a write to the main repository |
Med |
Low (test-only; no production path) |
Failed the gate on pull requests 67, 70 and again on the commit that attempted to fix it; the same commit passed on its other run, so it is seed- and load-dependent. Not once reproduced locally across repeated runs |
Ruled out: busy_timeout, raised from the shipped 5 s to 30 s in the suite, did not stop it. SQLite returns BUSY immediately, without the busy handler, when a read transaction upgrades to a write while another connection holds the write lock; no timeout affects that path. The suite runs two connections (one for the sandbox owner, one for Oban's boot check) and deliberately queues fifty concurrent writers at slice 070 AC7, which is the load that provokes it. Candidate fixes, not yet taken: drain the stress test's sessions before the suite continues; give that test its own tag and CI step; or remove the second connection's need. Owner: the maintainer. Lift condition: ten consecutive green CI runs of the full suite with the stress test in the default run |
070 |
| R26 |
Occurrence recorded 2026-09-23: the same class of failure appeared on the FIPS leg rather than the ordinary gate, which the row above does not cover. Trinity.Gateways.ApprovalsTest AC4 failed in its setup, with the crash in Trinity.Receipts.ChainWriter.terminate/2 calling insert_checkpoint/2 after the sandbox owner had gone: an ownership error at termination rather than a Database busy on a write. Re-running the identical commit passed all four checks, which is the evidence that matters: the code did not change between the red run and the green one, so the failure is in the environment or the ordering and not in the change under test. What this adds to the row above: the mechanism is not only write contention, it also reaches process termination racing sandbox teardown, and it is not confined to the ordinary gate's runners. A candidate fix specific to this shape: ChainWriter.terminate/2 writes a checkpoint on the way out, and a terminate callback that touches the database after the test that owns the connection has finished is a race by construction; it should skip the write when the repo is in sandbox mode and has no owner, rather than attempting it and crashing. Second occurrence the same day, and it changes the severity. The very next pull request, which changed only this file, failed the FIPS leg again: Trinity.Gateways.RouterTest AC6, Exqlite.Error: Database busy on an INSERT INTO gateway_identities - the row's original signature, on the leg the row did not cover. A change that cannot affect behaviour failing a required check twice in a row means this is no longer an intermittent annoyance: it blocks work, and the honest reading is that the FIPS leg provokes it more reliably than the ordinary gate does, most likely because that leg is slower (163 s against about 110 s locally) and every contention window is correspondingly wider. The row above stands as written |
High (was Med: it now blocks merges) |
Low (test-only; no production path) |
The lift condition in the row above, still unmet; two failures on the FIPS leg on 2026-09-23, one of them on a documentation-only change |
docs/06-risk-register.md R26 |
070 |
| R26 |
Cause removed 2026-09-24, slice 005; the row stays open until its lift condition is met. The mechanism this row diagnosed on 2026-09-23 is the documented behaviour of a DEFERRED transaction: no lock at BEGIN, a reader until its first write, and at that upgrade SQLite returns SQLITE_BUSY without invoking the busy handler because blocking there risks deadlock. The documented fix is BEGIN IMMEDIATE, which takes the write lock at BEGIN where the handler is allowed to wait, and ecto_sqlite3 0.24.1 exposes it as :default_transaction_mode. Both SQLite repos are now configured :immediate, guarded on the adapter so it cannot reach the Postgres build. The three candidate fixes this row recorded were all workarounds, and saying so is the useful part: draining the stress test's sessions, giving it its own CI step and removing the second connection's need each reduce how often two connections contend and leave the upgrade path exactly where it is. The row had the mechanism right and then proposed three ways around it. Reproduced deterministically for the first time, against this row's own words that it was "not once reproduced locally": test/trinity/repo/sqlite_transaction_mode_test.exs drives two connections through both sequences and asserts that under DEFERRED the upgrade is refused in under a second despite a five-second busy_timeout, and that under IMMEDIATE the same sequence waits and succeeds, in a tenth of a second. Not closed: the lift condition is ten consecutive green runs of the full suite with the stress test in the default run, which this can begin earning and cannot assert on the day it lands. A fix for an intermittent fault that declares victory on one green suite is how the fault comes back. The rows above stand as written |
Low (was High: the upgrade path is gone, pending the ten runs) |
Low (test-only; no production path) |
Ten consecutive green runs from 2026-09-24 |
docs/05-data-model.md, "Transactions begin IMMEDIATE on SQLite" |
005, 070 |
| R26 |
Occurrence recorded 2026-09-24, and this one is R26 rather than something wearing its signature. Trinity.Telemetry.CostsTest, "an unconfigured budget is not a budget of zero", failed with ** (Exqlite.Error) Database busy on the ordinary gate leg of pull request 96, while the sibling run of the identical commit passed. The test is untouched by that change, the error is the row's original wording, and the failure is on a write: three of the three things the row names. Recorded separately from the counter-example above so that the register shows both kinds, because the counter-example's whole point is that one cannot be assumed from the signature and this one was checked before being filed. What it adds: the row above says it blocks work, and it blocked a merge again here, costing a full CI cycle on a branch whose own tests were green. The three candidate fixes in the original row remain untaken, which is now a decision rather than an oversight: it has cost four cycles across 2026-09-23 and 2026-09-24. Recommend taking it as a slice of its own rather than continuing to re-run. The rows above stand as written |
High, unchanged |
Low (test-only; no production path) |
The lift condition in the original row, still unmet; a fourth blocking occurrence |
docs/06-risk-register.md R26 |
070 |
| R26 |
Counter-example recorded 2026-09-24, in slice 090, and it corrects a heuristic the rows above state. At commit b1bca1f the pull_request run passed all four checks and the push run of the same commit failed postgres (runs 36001064067 and 36001057879). By the reading the 2026-09-23 entry offers - the code did not change between the green run and the red one, so the fault is the environment - this was R26 again. It was not. It was a real defect in slice 080's code, on a genuinely racy path: every call into a child session is a gen_statem.call, and a call into a process that dies exits the caller, so killing a child killed whoever called delegate/3. Whether the kill landed before or after the call returned decided the outcome, which is why the same commit could go either way. Fixed at 1f33f12, after which both runs of the same commit passed. What this changes: "the same commit passed on its other run" is evidence that the failure is timing-dependent, which is not the same as evidence that it is environmental. A real race in library code produces exactly the signature R26 is recognised by, so the signature alone cannot be the diagnosis - the stack trace has to be read before a failure is filed here. This slice nearly filed one. The rows above stand as written |
none (not an occurrence) |
none |
none |
none |
080, 090 |