Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
454a12e
docs(s013): G1 plan with the renderer measured, and the slice opens
HackTuah Sep 20, 2026
482b9e6
chore(s013): add mdex ~> 0.13
HackTuah Sep 20, 2026
ce4b1f4
test(s013): a model set between turns is not the next turn's model (red)
HackTuah Sep 20, 2026
57aca61
fix(s012): the Session reads its row at the start of every turn
HackTuah Sep 20, 2026
50f35db
feat(s013): the chat: two LiveViews, the component vocabulary, the re…
HackTuah Sep 20, 2026
e33f5bc
fix(s013): a user bubble keeps its own whitespace only
HackTuah Sep 20, 2026
84a4aba
test(s013): the kill test waits for the rehydrate broadcast only
HackTuah Sep 20, 2026
080c543
fix(s013): the inline sobelow skip names its reason the way the enfor…
HackTuah Sep 20, 2026
8cc90b7
feat(s013): complete slice 013 (LiveView chat UI with streaming)
HackTuah Sep 20, 2026
50d6630
docs(s013): PROOF.md names the closing commit's sha
HackTuah Sep 20, 2026
68c00a6
fix(s001): the serve step prints the body and serve.log when GET / is…
HackTuah Sep 20, 2026
1f677b0
fix(s010): the packaged binary runs its migrations at boot
HackTuah Sep 20, 2026
55999e0
docs(s013): the package workflow's three findings and the open render…
HackTuah Sep 20, 2026
22cd58c
feat(s013): the linux package builds the markdown NIF for musl, and t…
HackTuah Sep 20, 2026
6a94e31
docs(s013): NOTES finding 15 and the PROOF correction for the package…
HackTuah Sep 20, 2026
96baed2
fix(s001): the serve step prints serve.log when no endpoint line appears
HackTuah Sep 20, 2026
b62ef62
fix(s010): the data-directory lock reads a pid's liveness on macOS an…
HackTuah Sep 20, 2026
ff2b316
docs(s013): finding 16 and the green package run in PROOF.md
HackTuah Sep 20, 2026
c2663ad
docs(s013): closing correction names the pull request and the commits…
HackTuah Sep 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,19 @@ jobs:

# --overwrite is not optional. Without it, and with a release directory already
# present, mix release prompts, gets no stdin, and exits 0 having built nothing.
# Slice 013: on Linux, mdex's NIF is built from source for Burrito's musl ERTS, linked
# through Zig (scripts/zig-cc-musl; config/config.exs names the three settings and
# NOTES.md finding 14 the measurement). rustup installed the musl standard library from
# rust-toolchain.toml above. macOS and Windows load the precompiled artifact for their
# native ERTS; the smoke step below is what says whether it loaded.
- name: Build the Burrito binary
shell: bash
env:
BURRITO_TARGET: ${{ matrix.target }}
MIX_ENV: prod
MDEX_NATIVE_BUILD: ${{ runner.os == 'Linux' && '1' || '' }}
TRINITY_NIF_TARGET: ${{ runner.os == 'Linux' && 'x86_64-unknown-linux-musl' || '' }}
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: ${{ runner.os == 'Linux' && format('{0}/scripts/zig-cc-musl', github.workspace) || '' }}
run: |
set -euo pipefail
# `mix deps.get` above runs in the default env. On the Windows runner the prod build
Expand Down Expand Up @@ -188,6 +196,8 @@ jobs:
sleep 3
ours > ps-after.txt
grep -q '^TRINITY_SMOKE_PORT=[0-9][0-9]*$' smoke.log
# Slice 013: the renderer's NIF loaded and rendered in this binary (exit 3 otherwise).
grep -q '^TRINITY_SMOKE_MARKDOWN=ok$' smoke.log
echo "--- ours, before ---"; cat ps-before.txt
echo "--- ours, after ----"; cat ps-after.txt
diff ps-before.txt ps-after.txt
Expand All @@ -202,6 +212,9 @@ jobs:
if (-not (Select-String -Path smoke.log -Pattern '^TRINITY_SMOKE_PORT=\d+$')) {
throw "no port line"
}
if (-not (Select-String -Path smoke.log -Pattern '^TRINITY_SMOKE_MARKDOWN=ok$')) {
throw "the markdown renderer did not render in this binary (slice 013)"
}

# AC1's property on a runner, and AC6's per-OS cold start, which used to be skipped.
# `bash` on every OS, Windows included: the job that never curled was the one gap in
Expand Down Expand Up @@ -233,7 +246,14 @@ jobs:
[ -n "${PORT:-}" ] && break
sleep 0.5
done
test -n "${PORT:-}"
# No endpoint line in two minutes says nothing on its own either: runs 35512430836
# (slice/011), 35515556177 (slice/012) and 35521749862 all failed here on macOS and
# Windows with an empty step log. The log the binary wrote goes to stderr first.
if [ -z "${PORT:-}" ]; then
echo "no 'Running TrinityWeb.Endpoint' line in serve.log after 120 s" >&2
echo "--- serve.log ---" >&2; tail -60 serve.log >&2
exit 1
fi
# Bounded. The first version of this loop had no limit and the Windows runner sat in
# it until the run was cancelled by hand: a step that hangs reports nothing and
# fails nothing, which is the same shape as a workflow that never fires.
Expand All @@ -252,6 +272,13 @@ jobs:
echo "HTTP $code on port $PORT"
echo "COLD_START_MS=$elapsed" | tee -a "$GITHUB_ENV"
echo "### ${{ matrix.name }}: cold start to first HTTP 200: **${elapsed} ms**" >> "$GITHUB_STEP_SUMMARY"
# A non-200 says nothing on its own: run 35518054546 (slice 013) printed "HTTP 500"
# and nothing else, and the log that would have named the exception was never
# read. The body and the tail of serve.log go to stderr before the step fails.
if [ "$code" != "200" ]; then
echo "--- body of GET / ---" >&2; curl -sS "http://127.0.0.1:$PORT/" | head -c 2000 >&2; echo >&2
echo "--- serve.log ---" >&2; tail -60 serve.log >&2
fi
test "$code" = "200"
# Stopping it, and Windows needs its own verb. `kill` from Git-bash does not stop a
# native Windows process, so the backgrounded .exe outlived the step and the job hung
Expand Down
10 changes: 5 additions & 5 deletions .sobelow-skips
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Config.CSP: Missing Content-Security-Policy,lib/trinity_web/router.ex:12,2DBC9F9
Traversal.FileModule: Directory Traversal in `File.mkdir_p`,lib/trinity/data_dir/lock.ex:57,35AB9EC
Traversal.FileModule: Directory Traversal in `File.read`,lib/trinity/data_dir/lock.ex:143,55D5ADB
Traversal.FileModule: Directory Traversal in `File.rm`,lib/trinity/data_dir/lock.ex:82,21D88E2
Traversal.FileModule: Directory Traversal in `File.write`,lib/trinity/data_dir/lock.ex:132,1C42814
Config.CSP: Missing Content-Security-Policy,lib/trinity_web/router.ex:14,1DC4768
Traversal.FileModule: Directory Traversal in `File.mkdir_p`,lib/trinity/data_dir/lock.ex:60,4ECF02C
Traversal.FileModule: Directory Traversal in `File.read`,lib/trinity/data_dir/lock.ex:146,5EFB666
Traversal.FileModule: Directory Traversal in `File.rm`,lib/trinity/data_dir/lock.ex:85,7D2B826
Traversal.FileModule: Directory Traversal in `File.write`,lib/trinity/data_dir/lock.ex:135,42D2817
10 changes: 5 additions & 5 deletions .sobelow-skips.reasons
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
# exactly that. Re-run mix sobelow --mark-skip-all and rekey this file when it happens.
#
# format: <fingerprint><TAB><reason>
2DBC9F9 Missing CSP on the browser pipeline. The scaffold ships no Content-Security-Policy, and setting one needs LiveView nonce handling, which belongs with the UI rather than the toolchain. Slice 013 owns it. Skipped rather than fixed blind: a CSP written without the UI it protects is a guess.
35AB9EC Slice 010, Trinity.DataDir.Lock.acquire/2: the directory is the application's own data directory from config or Trinity.Paths, never a request or a model output; the lock has to create it before the Repo does. Low confidence traversal by the tool's own label.
55D5ADB Slice 010, Trinity.DataDir.Lock read/1: reads <data_dir>/LOCK, a path this module composes from the data directory and a constant file name; no caller supplies it.
21D88E2 Slice 010, Trinity.DataDir.Lock.release/2: removes <data_dir>/LOCK only when the file carries this process's own token; same composed path as the read.
1C42814 Slice 010, Trinity.DataDir.Lock take_over/2: rewrites <data_dir>/LOCK after the recorded holder's pid is found dead; same composed path, same constant name.
1DC4768 Slice 013, the browser pipeline: the policy IS set, by TrinityWeb.Plugs.ContentSecurityPolicy with a nonce per request, placed before put_secure_browser_headers. sobelow recognises only a header map given to put_secure_browser_headers, so it keeps reporting the pipeline. test/trinity_web/plugs/content_security_policy_test.exs asserts the header and the nonce on every browser response. Rekeyed from 2DBC9F9 (router.ex:12, slice 000, "slice 013 owns it") when the plug moved the line to 14.
4ECF02C Rekeyed at slice 013 (the moduledoc grew three lines for the macOS and Windows liveness check). Slice 010, Trinity.DataDir.Lock.acquire/2: the directory is the application's own data directory from config or Trinity.Paths, never a request or a model output; the lock has to create it before the Repo does. Low confidence traversal by the tool's own label.
5EFB666 Rekeyed at slice 013 (the moduledoc grew three lines for the macOS and Windows liveness check). Slice 010, Trinity.DataDir.Lock read/1: reads <data_dir>/LOCK, a path this module composes from the data directory and a constant file name; no caller supplies it.
7D2B826 Rekeyed at slice 013 (the moduledoc grew three lines for the macOS and Windows liveness check). Slice 010, Trinity.DataDir.Lock.release/2: removes <data_dir>/LOCK only when the file carries this process's own token; same composed path as the read.
42D2817 Rekeyed at slice 013 (the moduledoc grew three lines for the macOS and Windows liveness check). Slice 010, Trinity.DataDir.Lock take_over/2: rewrites <data_dir>/LOCK after the recorded holder's pid is found dead; same composed path, same constant name.
2 changes: 1 addition & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ SPDX-PackageDownloadLocation = "https://github.com/ScriptKittyOS/Trinity"
# the gap is recorded rather than glossed.

[[annotations]]
path = ["mix.lock", "src-tauri/Cargo.lock", "**/*.md", "**/*.json", "**/*.tsv", "**/*.cff", "**/*.toml", ".tool-versions", ".gitignore", ".sobelow-skips", ".sobelow-skips.reasons", "priv/name_digests.txt", ".formatter.exs"]
path = ["mix.lock", "src-tauri/Cargo.lock", "**/*.md", "**/*.json", "**/*.tsv", "**/*.cff", "**/*.toml", ".tool-versions", ".gitignore", ".sobelow-skips", ".sobelow-skips.reasons", "priv/name_digests.txt", ".formatter.exs", "slices/**/proof/**"]
precedence = "aggregate"
SPDX-FileCopyrightText = "Sudo Apt Holdings LLC"
SPDX-License-Identifier = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ standards register names the rows that ask for them.
| 010 | Core domain + persistence (Ecto/SQLite, schemas, Repo owner) | 1 Core loop | M | 000 | approved |
| 011 | LLM provider layer (req_llm behind `Trinity.LLM` behaviour) | 1 Core loop | M | 010 | approved |
| 012 | Session process + agent loop (gen_statem, DynamicSupervisor, rehydration) | 1 Core loop | L | 010, 011 | approved |
| 013 | LiveView chat UI with streaming | 1 Core loop | M | 012 | planned |
| 013 | LiveView chat UI with streaming | 1 Core loop | M | 012 | done |
| 020 | Tool protocol + registry | 2 Tools | M | 012 | planned |
| 021 | Permission gate + approval UI (M2 fingerprint-bound, M7) | 2 Tools | M | 020, 013 | planned |
| 022 | Core tools: filesystem, web fetch/search, shell (MuonTrap) | 2 Tools | L | 021 | planned |
Expand Down
4 changes: 3 additions & 1 deletion VERSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ never pin a version hex marks as retired or vulnerable.
| `ex_tauri` | ~> 0.2 | ✅ in `mix.lock` | ⚠️ Declares `otp_release: "~> 27.0"`, and Slice 000's probe refutes the reason it gives: OTP 28 macOS universal returns 200 and OTP 27 returns 404. Whether it runs on the pinned OTP is Slice 001's first measurement. ⚠️ 439 downloads all-time, so the ADR-0004 fallback matrix carries real weight. |
| `nostrum` | ~> 0.10 | 🔍 not yet a dependency | Discord. ⚠️ No release in roughly 18 months. R11's trigger already fires. Check intents and components against the current gateway before Slice 072. |
| `telegex` | **not pinned** | 🔍 not a single package | Telegram. ⚠️ The latest release on hex is a release candidate, roughly two years old, and this file's own rule forbids pinning an `-rc`. Alternative: ex_gram. Slice 071 decides with the measurement. |
| `phoenix_streamdown` | **not pinned** | 🔍 not a single package | Streaming markdown renderer for LiveView. ⚠️ Pre-release, and this file's own rule forbids pinning an `-rc`; a beta is the same category. Verify at Slice 013; fallback: earmark or mdex with chunk buffering. |
| `phoenix_streamdown` | not used (measured at Slice 013, 2026-09-20) | 🔍 not a single package | Streaming markdown renderer for LiveView. Measured at Slice 013: 1.0.0-beta.4 (2026-05-03) was still the latest release with no stable behind it, and this file's own rule forbids a pre-release. It is 801 lines over `mdex`, whose own `streaming: true` option completes fragments; the rest is a rendering rule Slice 013 keeps anyway. The row stays so the decision is visible where a reader would look for the package. |
| `mdex` | ~> 0.13 | ✅ in `mix.lock` | Markdown renderer for the chat (`TrinityWeb.Markdown`), streaming fragments completed by its `streaming: true` option, raw HTML omitted (`unsafe: false`) and the default sanitizer on top. Added at Slice 013 after the measurement in its NOTES.md: `earmark` 1.4.49 is retired on hex with an open XSS advisory (EEF-CVE-2026-48591), which the gate refuses. ⚠️ A Rust NIF (`mdex_native`): the first in the bundle. Measured at Slice 013 (NOTES finding 13): neither precompiled artifact loads in Burrito's musl ERTS on Linux, so the linux package builds it from source for musl with Zig as the linker (`rustler` below, `scripts/zig-cc-musl`, the three settings in config/config.exs); macOS and Windows load the precompiled artifact. The `--smoke` path prints whether it rendered, and the `package` workflow reads that line on every target. |
| `rustler` | ~> 0.38 | ✅ in `mix.lock` | Build time only (`runtime: false`): what `rustler_precompiled` needs to compile `mdex_native` from source when `MDEX_NATIVE_BUILD=1`, which the linux package sets (owner decision 2026-09-20, Slice 013 NOTES finding 14). Nothing in the tree calls it. |

### Dev and quality

Expand Down
Loading
Loading