Skip to content

Commit 16d61ec

Browse files
committed
feat(s022): complete slice 022 (core tools: fs, web, shell)
PROOF.md with every criterion's evidence: AC1 to AC5, AC7 to AC9 and AC11 from the tests, AC6's fake half in the suite and its live half in the owner's queue, AC10 as a GIF and three stills from the scripted run (scripts/dev_chat_list_and_summarise.sh). NOTES.md carries the nine findings and the follow-ups. coverage.tsv: 022 at 74.85% from 72.45%. ROADMAP row 022 to done. Gate exit 0, 255 tests. Signed-off-by: Ayla Croft <aylacroft@proton.me>
1 parent 1fb1372 commit 16d61ec

10 files changed

Lines changed: 249 additions & 1 deletion

‎ROADMAP.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ standards register names the rows that ask for them.
3939
| 013 | LiveView chat UI with streaming | 1 Core loop | M | 012 | approved |
4040
| 020 | Tool protocol + registry | 2 Tools | M | 012 | approved |
4141
| 021 | Permission gate + approval UI (M2 fingerprint-bound, M7) | 2 Tools | M | 020, 013 | approved |
42-
| 022 | Core tools: filesystem, web fetch/search, shell (MuonTrap) | 2 Tools | L | 021 | in_progress |
42+
| 022 | Core tools: filesystem, web fetch/search, shell (MuonTrap) | 2 Tools | L | 021 | done |
4343
| 023 | Context compaction + session lineage | 2 Tools | M | 012 | planned |
4444
| 024 | Effect catalog, authority selection (`TRINITY_AUTHORITY`), local receipts | 2 Tools | L | 021, 022 | planned |
4545
| 025 | Encryption at rest, and the key-custody seam | 2 Tools | M | 010, 024 | planned |

‎coverage.tsv‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ slice_id percent sha date
77
013 64.41 080c543 2026-09-20
88
020 67.18 8a5b7ae 2026-09-20
99
021 72.45 1f3727f 2026-09-20
10+
022 74.85 1fb1372 2026-09-20
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env bash
2+
# SPDX-FileCopyrightText: Sudo Apt Holdings LLC
3+
# SPDX-License-Identifier: Apache-2.0
4+
# AC10: the chat on the test registry with the fake provider scripting "list the files and summarise the README".
5+
cd "$(dirname "$0")/.."
6+
export MIX_ENV=test
7+
exec systemd-run --user --scope -p MemoryMax=32G --quiet -- mix run --no-start --no-halt -e '
8+
repo = Application.get_env(:trinity, Trinity.Repo) |> Keyword.delete(:pool) |> Keyword.put(:database, "trinity_screenshots.db")
9+
Application.put_env(:trinity, Trinity.Repo, repo)
10+
endpoint = Application.get_env(:trinity, TrinityWeb.Endpoint) |> Keyword.put(:check_origin, false)
11+
Application.put_env(:trinity, TrinityWeb.Endpoint, endpoint)
12+
Application.put_env(:trinity, :permissions, expiry_ms: 600_000, session_grant_ms: 3_600_000)
13+
{:ok, _} = Application.ensure_all_started(:trinity)
14+
Ecto.Migrator.run(Trinity.Repo, :up, all: true)
15+
project = File.cwd!()
16+
say = fn text -> Enum.flat_map(String.split(text), &[{:text_delta, &1 <> " "}, {:sleep, 35}]) end
17+
Trinity.LLM.Providers.Fake.scripts([
18+
say.("I will list the project first.") ++ [{:tool_call_start, "c1", "fs_list"}, {:tool_call_end, "c1", %{"path" => project}}, {:usage, %{input_tokens: 10, output_tokens: 8}}, {:done, :tool_calls}],
19+
say.("Now the README.") ++ [{:tool_call_start, "c2", "fs_read"}, {:tool_call_end, "c2", %{"path" => project <> "/README.md", "limit" => 60}}, {:usage, %{input_tokens: 10, output_tokens: 6}}, {:done, :tool_calls}],
20+
say.("The project is **Trinity**, a personal AI agent on Elixir and Phoenix LiveView packaged as a desktop app. The README explains the slice process, the quality gate, and how to connect it to the platform. The tree holds the app, the docs, the slices and the tests.") ++ [{:usage, %{input_tokens: 400, output_tokens: 60}}, {:done, :stop}]
21+
])
22+
{:ok, {_, port}} = TrinityWeb.Endpoint.server_info(:http)
23+
IO.puts("PORT=#{port}")
24+
'

‎scripts/dev_chat_on_test_registry.sh‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ endpoint = Application.get_env(:trinity, TrinityWeb.Endpoint) |> Keyword.put(:ch
1717
Application.put_env(:trinity, TrinityWeb.Endpoint, endpoint)
1818
Application.put_env(:trinity, :permissions, expiry_ms: 600_000, session_grant_ms: 3_600_000)
1919
{:ok, _} = Application.ensure_all_started(:trinity)
20+
Ecto.Migrator.run(Trinity.Repo, :up, all: true)
2021
call = [{:text_delta, "I will save that as a note. "}, {:sleep, 300}, {:tool_call_start, "c1", "write_note"}, {:tool_call_end, "c1", %{"path" => "/home/me/notes/today.md", "text" => "Buy oat milk, call the dentist, finish the slice."}}, {:usage, %{input_tokens: 12, output_tokens: 20}}, {:done, :tool_calls}]
2122
final = fn text -> Enum.flat_map(String.split(text), &[{:text_delta, &1 <> " "}, {:sleep, 40}]) ++ [{:usage, %{input_tokens: 40, output_tokens: 30}}, {:done, :stop}] end
2223
Trinity.LLM.Providers.Fake.scripts([

‎slices/022-core-tools/NOTES.md‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,52 @@ Deviations from SLICE.md, stated before building: tool names are the flat `fs_re
7272
or namespaced core name would read as dynamic); `<untrusted>` wrapping is the Part's rendering, not a string
7373
the tool returns (the M1 alignment says so); the search provider is Brave unless the owner names another;
7474
the shell is POSIX-only (above); `Web.Fetch` does no JavaScript and says so in its description.
75+
76+
## Lines 1 to 9, 2026-09-20: what was built, and what building it found
77+
78+
**Built.** As planned: `Content.Part`, `Tools.Untrusted`, `Result.parts`, the turn's taint on the Session and the
79+
assistant row, the prompt's `<untrusted>` rendering and rule; `Tool.escalate/2` and `available?/0`,
80+
`Permissions.effective_tier/2`; `Tools.FS` with the six tools and `FS.Placeholders`; `Web.Fetch`,
81+
`SearchProvider` (Brave, Fake), `Web.Search`; `Shell.Run` and `Shell.Dangerous`; the catalog's first entry
82+
(`shell`, `:exec`); the toolsets in config; the card's platform note; two scripts under `scripts/` that serve the
83+
chat on the test registry with scripted turns (the second is AC10's).
84+
85+
**Found while building, each recorded rather than smoothed.**
86+
87+
1. **A port's `env:` adds to the environment; it does not replace it.** The first shell version handed MuonTrap
88+
the seven kept names and the child still saw every key (AC9 red: `TRINITY_TEST_SECRET_KEY=sk-…` in the
89+
output). Every other name in Trinity's environment is now passed as `nil`, which unsets it.
90+
2. **`Req` retries a 500 three times by default**, seven seconds for a page that says no; `retry: false` on the
91+
fetch, a tool call being one attempt.
92+
3. **`config/runtime.exs` runs after `config/test.exs`**, so the Brave default there overrode the test config's
93+
fake and AC6's fake test hit "no search key". The runtime default is set for every environment but test.
94+
4. **The web tests use `Req`'s `plug:` option**, not a Bandit server on the loopback as SLICE.md said: a Plug
95+
answers in-process and no socket is opened, which is stronger for "tests must not hit the network"; the
96+
`NetworkGuard` chokepoint cannot see Req in any case (its stated limit).
97+
5. **sobelow's traversal check fires on every filesystem tool by construction**: a path that is the model's
98+
argument is the tool's whole purpose. Each function carries a scoped skip whose reason names the actual
99+
control (the roots after symlink resolution, the escalation, the gate).
100+
6. **Playwright's bundled ffmpeg has no GIF muxer and no filter graph**: the frames were extracted with it and
101+
assembled with ImageMagick's `convert`; the GIF is 724 KB, 31 frames at four a second.
102+
7. **A fresh screenshot database has no tables under Mix**: `skip_migrations?/0` is true wherever Mix is loaded
103+
(the 013 fix), so the serving scripts migrate by hand after the application starts.
104+
8. **`fs_list` on the project directory asks** in the AC10 run, because the session has no working directory
105+
and the project is not a configured root: the approvals in the GIF are the design working, and the follow-up
106+
is 033's project context, which gives a session a cwd.
107+
9. **Credo's nesting and `with` rules** reshaped `fs_read`, `fs_list` and `fs_grep` (a `case` and a helper each).
108+
109+
```
110+
$ mix test test/trinity/tools/fs test/trinity/tools/web test/trinity/tools/shell test/trinity/tools/provenance_test.exs → 23 passed, 1 excluded (live)
111+
$ mix gate → exit 0; 255 passed, 11 excluded; plan_check: PASS
112+
$ mix test --cover → 74.85% total (Shell.Run 100%, FS 91.67%, Fetch 81.94%, Part 80%)
113+
$ mix credo --strict --all → 1059 mods/funs, found no issues
114+
```
115+
116+
## Follow-ups
117+
- A Windows shell runtime with a kill guarantee (job objects) is a slice of its own; until then the shell is
118+
absent there and the card says so.
119+
- 033 gives a session a working directory (the project's), so `fs_list` and `fs_read` on it run without asking.
120+
- `blocked` parts: nothing writes one; 024's receipts and the sentinel decide when a part is blocked.
121+
- The live search test needs `BRAVE_SEARCH_API_KEY` in `.env`; the owner's manual queue.
122+
- `Web.Fetch` reads no PDF; a text extractor for it is a small later addition to the same tool.
123+
- `FS.restore/2` has no tool or UI; 034 (export, import, restore) is its natural surface.

0 commit comments

Comments
 (0)