@synovitec/trellis 0.0.0-stage → 0.6.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +393 -0
- package/LICENSE +190 -0
- package/README.md +206 -2
- package/SECURITY.md +104 -0
- package/bin/trellis.mjs +5 -0
- package/docs/attestation/v0.1.md +96 -0
- package/package.json +78 -4
- package/schema/trellis.config.schema.json +349 -0
- package/src/agents/adapters.mjs +155 -0
- package/src/agents/git.mjs +241 -0
- package/src/agents/glob.mjs +41 -0
- package/src/agents/hooks.mjs +780 -0
- package/src/agents/install-adapters.mjs +172 -0
- package/src/agents/install.mjs +302 -0
- package/src/agents/manifests.mjs +127 -0
- package/src/agents/rules.mjs +330 -0
- package/src/agents/shell.mjs +253 -0
- package/src/agents/state.mjs +233 -0
- package/src/agents/transcript.mjs +120 -0
- package/src/cli/main.mjs +191 -0
- package/src/commands/attest.mjs +207 -0
- package/src/commands/checkpoint-state.mjs +62 -0
- package/src/commands/doctor.mjs +98 -0
- package/src/commands/guide.mjs +62 -0
- package/src/commands/init.mjs +129 -0
- package/src/commands/key.mjs +31 -0
- package/src/commands/scan.mjs +51 -0
- package/src/commands/uninstall.mjs +36 -0
- package/src/commands/verify.mjs +509 -0
- package/src/core/abatty.mjs +25 -0
- package/src/core/agents-block.mjs +65 -0
- package/src/core/attestation.mjs +213 -0
- package/src/core/config.mjs +135 -0
- package/src/core/context.mjs +280 -0
- package/src/core/diff.mjs +189 -0
- package/src/core/evidence.mjs +167 -0
- package/src/core/mcp.mjs +51 -0
- package/src/index.mjs +11 -0
- package/src/mcp/client.mjs +164 -0
- package/src/mcp/launch.mjs +57 -0
- package/src/mcp/relay.mjs +111 -0
- package/src/mcp/server.mjs +210 -0
- package/src/mcp/trust.mjs +142 -0
- package/src/tools/registry.mjs +370 -0
- package/src/tools/roles.mjs +17 -0
- package/src/verify/deps.mjs +382 -0
- package/src/verify/gitleaks-default.toml +3211 -0
- package/src/verify/rules.mjs +163 -0
- package/src/verify/secrets.mjs +169 -0
- package/src/verify/test-commands.mjs +116 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this package. The format follows Keep a Changelog; versions follow SemVer.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.6.0-rc.2] - 2026-10-30
|
|
8
|
+
|
|
9
|
+
The tenth review, on the code the ninth round added, found the secret scan's own reading of team
|
|
10
|
+
allowlists unsound, and three false refusals. All fixed.
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
|
|
14
|
+
- **The team's gitleaks allowlist is read by gitleaks itself.** Trellis parsed it, and a pattern
|
|
15
|
+
with a character class (`[^/]+\.lock$`) became an empty pattern that allowed every path: a
|
|
16
|
+
secret passed CI. Files are now scanned at their real paths with the team's `.gitleaks.toml`
|
|
17
|
+
and `.gitleaksignore` as gitleaks reads them (`condition = "AND"`, `targetRules`, rule
|
|
18
|
+
allowlists, `[extend] path`), and only gitleaks's built-in global path allowlist is dropped:
|
|
19
|
+
Trellis ships gitleaks 8.30.1's default rules without it (`src/verify/gitleaks-default.toml`,
|
|
20
|
+
MIT, regenerated and checksum-checked by `scripts/gitleaks-rules.mjs`).
|
|
21
|
+
- **A test run's environment.** `COMSPEC` is pinned to the system's cmd.exe, and Cargo, Go, Yarn,
|
|
22
|
+
Python, shell (`BASH_ENV`) and JVM settings that steer a runner are cleared; a test command with
|
|
23
|
+
a line break never counts, and the gateway's `run_tests` runs the accepted command, never the
|
|
24
|
+
raw text; an ignored `.pytest.ini`, `.yarnrc` or `.cargo/config` refuses the record.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **The net change is what lands**: what merging the head into the base gives (`git merge-tree`,
|
|
29
|
+
git 2.38 or later), so criss-cross history no longer reads a change the base made again as the
|
|
30
|
+
pull request reverting it.
|
|
31
|
+
- **A person's test edit is judged as a person's**, file by file: one commit with no attestation
|
|
32
|
+
(the host's "Update branch", a bot) no longer makes every file of the pull request an agent's.
|
|
33
|
+
|
|
34
|
+
## [0.6.0-rc.1] - 2026-10-30
|
|
35
|
+
|
|
36
|
+
A release candidate: nine independent reviews, the last four adversarial, each fixed in full; the
|
|
37
|
+
tenth is under way. Verify it on a repository of your own before you rely on it.
|
|
38
|
+
|
|
39
|
+
Evidence v2 (design: docs/design/0001-evidence-v2.md). Five independent reviews kept finding the
|
|
40
|
+
same weak point, deciding what a session did from git history; and only Claude Code was held.
|
|
41
|
+
Trellis now holds any agent, and any person, at the commit, and has CI verify every commit again.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- **The commit checkpoint.** `trellis verify` (the pre-commit hook) judges the staged change by
|
|
46
|
+
content: Trellis's own secret scan, each added or moved dependency checked on its registry and
|
|
47
|
+
against OSV at its exact version, and the rules, in two presets (`recommended`, `strict`) grounded
|
|
48
|
+
in published evidence. Blocking rules are the deterministic ones: secrets, dependencies,
|
|
49
|
+
protected tests (an agent may add tests, not change existing ones without a person's approval),
|
|
50
|
+
an unanswered escalation. The rest warn.
|
|
51
|
+
- **The evidence log v2.** One append-only log per repository, each line signed and chained; content
|
|
52
|
+
checks hold the git blob of every file they ran on.
|
|
53
|
+
- **`trellis check tests -- <cmd>`, `trellis escalate`, `trellis approve`** (refused without a
|
|
54
|
+
terminal), **`trellis checkpoint`**.
|
|
55
|
+
- **Attestations.** The post-commit signs an in-toto Statement v1 about each commit and its tree
|
|
56
|
+
(DSSE, ed25519), carried in `refs/notes/trellis` and pushed by the pre-push hook. Format:
|
|
57
|
+
docs/attestation/v0.1.md. **`trellis key [--add]`** commits a developer's public key.
|
|
58
|
+
- **`trellis verify --ci`**, the authority: every commit after a base, its attestation checked, the
|
|
59
|
+
deterministic checks run again, the rules judged again from the change and the recorded
|
|
60
|
+
evidence; a SLSA `verification_summary/v1` and commit checksums for signing. **A GitHub Action**
|
|
61
|
+
(`action.yml`) runs it, and can sign the summary with GitHub artifact attestations.
|
|
62
|
+
- **`trellis mcp`**, the MCP gateway: the checks as tools for any agent (graph_query, run_tests,
|
|
63
|
+
escalate, check_change), and every stdio server in `.mcp.json` relayed through it, with Trellis
|
|
64
|
+
recording each check itself. Dual-era: MCP 2026-07-28 stateless requests and the `initialize`
|
|
65
|
+
handshake of earlier versions. Declared by init in `.mcp.json`, `.cursor/mcp.json`,
|
|
66
|
+
`.codex/config.toml` and `.vscode/mcp.json`.
|
|
67
|
+
- **Cursor, Codex and Copilot hooks**, on the same guard, gates and evidence as Claude Code, each
|
|
68
|
+
in its own format, failing closed.
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- **doctor** shows the checkpoint first; before init, one diagnosis and one command; a tool with
|
|
73
|
+
one known step left warns with that step. init leaves OSV-Scanner off (the checkpoint checks each
|
|
74
|
+
added dependency against OSV).
|
|
75
|
+
- README and SECURITY.md describe v2: the machine is a seatbelt, CI is the authority.
|
|
76
|
+
|
|
77
|
+
### Security
|
|
78
|
+
|
|
79
|
+
- A commit the session pushed is the session's again. 0.5 left out every commit a remote had,
|
|
80
|
+
so committing and pushing made the work invisible to every rule, a pushed secret included.
|
|
81
|
+
Now only what the remotes held at the start, and what arrived since by a fetch or a pull (read
|
|
82
|
+
from the remote-tracking refs' reflogs), is left out. The guard refuses moving a
|
|
83
|
+
remote-tracking ref by hand, a fetch into one, and rewriting a reflog, which would forge that.
|
|
84
|
+
- AgentShield scans the repository. Without `--path` it scanned the user's `~/.claude`, so
|
|
85
|
+
`agent-setup-scanned` judged plugin caches (never passing on a busy machine) and never looked
|
|
86
|
+
at the repository's changed setup. The Stop hook now runs `scan --path <repo> --format json`,
|
|
87
|
+
reads the report from a file (it can run to megabytes), counts only critical and high findings
|
|
88
|
+
on the setup files the session changed, and treats a report it cannot read, or one about
|
|
89
|
+
another folder, as not scanned rather than clean. `trellis scan` passes `--path .` too.
|
|
90
|
+
- With Trellis gone, a hook blocks instead of crashing. After `git clean -fdx` or
|
|
91
|
+
`npm ci --omit=dev`, the hook's `node` failed with exit 1, which Claude Code ignores, so the
|
|
92
|
+
session ended unchecked. The hook command now checks that Trellis is there and exits 2 (which
|
|
93
|
+
blocks) when it is not. The guard refuses those commands in the first place: `git clean -x`
|
|
94
|
+
or `-X`, an install or prune without devDependencies, and installing `@synovitec/trellis`
|
|
95
|
+
mid-session (another version, or another package, in its place). Existing installs show the
|
|
96
|
+
hooks as outdated; `trellis init --agents` updates them.
|
|
97
|
+
- A graph query counts only when graphify is all that ran (beside a `cd` before it or a
|
|
98
|
+
`| head` after it). `graphify query x; echo NODE fake` printed what an answer looks like and
|
|
99
|
+
unlocked every edit.
|
|
100
|
+
- The agent cannot allowlist its own secret. The Stop hook's gitleaks run ignores inline
|
|
101
|
+
`gitleaks:allow` comments, and when the session touched `.gitleaks.toml` or `.gitleaksignore`
|
|
102
|
+
it scans with gitleaks's default rules instead of them; `GITLEAKS_CONFIG` is not passed on. A
|
|
103
|
+
team allowlist committed before the session still applies.
|
|
104
|
+
- Quotes and escapes no longer hide a path from the guard. `cat $HOME/.trel''lis/key` read the
|
|
105
|
+
signing key: the key, evidence and index-flag checks ran on the raw command text. They now run
|
|
106
|
+
on the words as the shell reads them too.
|
|
107
|
+
- A deleted session record stays missing. A resume or a compaction after
|
|
108
|
+
`find .git/trellis/sessions -delete` started a fresh record, which made everything done before
|
|
109
|
+
it the baseline. The record is now created only when a session starts (`startup`, `clear`), and
|
|
110
|
+
`find` with `-delete`, `-exec` and the like counts as a write on the evidence; the guard now
|
|
111
|
+
covers the whole transcripts folder, not only paths ending in `.jsonl`.
|
|
112
|
+
- A file hidden from git by an ignore rule is a finding. Pointing `core.excludesFile` at a list,
|
|
113
|
+
or writing a `.gitignore` that ignores itself, hid a new file from every rule. The session
|
|
114
|
+
records a fingerprint of the ignore rules at the start; when they changed, every newly ignored
|
|
115
|
+
file under the code, UI, schema or agent-setup paths is reported. A build output the rules
|
|
116
|
+
already ignored is not.
|
|
117
|
+
- `trellis uninstall` and `trellis init` are refused mid-session, by any spelling
|
|
118
|
+
(`npx @synovitec/trellis@x uninstall` included): the first took the hooks out at once, the
|
|
119
|
+
second could rewrite them. Only `trellis hook` was refused before.
|
|
120
|
+
- The test overrides `TRELLIS_STATE_DIR`, `TRELLIS_KEY_FILE` and `TRELLIS_AGENTSHIELD` hold only
|
|
121
|
+
under Node's test runner and outside Claude Code's project, and the guard refuses a command
|
|
122
|
+
that sets one. A session could set them and point the next one at its own key, record or
|
|
123
|
+
scanner.
|
|
124
|
+
- The Stop hook runs AgentShield's npx from an empty temporary folder. Run from the repository,
|
|
125
|
+
npx would take a `node_modules/ecc-agentshield` the session planted there over the pinned
|
|
126
|
+
version; it now fetches that exact version, with npm's integrity check, and still scans the
|
|
127
|
+
repository by its absolute path.
|
|
128
|
+
- A config broken at the start of a session stays the judge. Stop fell back to the current
|
|
129
|
+
config, so an agent could write a permissive one and be judged by it. It now fails closed
|
|
130
|
+
(`config-invalid`) until the person fixes the config.
|
|
131
|
+
|
|
132
|
+
### Security (reviews six to nine)
|
|
133
|
+
|
|
134
|
+
- **CI judges what lands.** The keys, the rules, the gitleaks allowlist and the test commands are
|
|
135
|
+
read at the base, so a pull request cannot change what judges it. Every file of the net change
|
|
136
|
+
must be what a commit of the range made it, read against every merge base, and the net change
|
|
137
|
+
is judged again as one change against the base: an old ancestor merged in, a criss-cross merge
|
|
138
|
+
keeping an older test, a branch that "adds" a test the base already has or keeps a dependency
|
|
139
|
+
the base upgraded, are all refused. A pull request is judged at its head, never at the merge
|
|
140
|
+
commit a checkout builds; only a checkpoint that passed vouches (`fail` and `unverified` are
|
|
141
|
+
refused).
|
|
142
|
+
- **The workflow is the base's.** The README's workflow runs on `pull_request_target`, so a pull
|
|
143
|
+
request cannot edit the check away; the action runs its own pinned code and a gitleaks checked
|
|
144
|
+
by SHA-256, reads the attestations with the workflow's token (private repositories), fetches a
|
|
145
|
+
fork's, and fails when origin cannot be read. doctor warns about a Trellis step a pull request
|
|
146
|
+
can edit.
|
|
147
|
+
- **The secret scan reads content, not diffs.** Each changed blob is scanned under a neutral name,
|
|
148
|
+
so neither a `.gitattributes` `-diff` line nor gitleaks's built-in path allowlist (`*.pdf`,
|
|
149
|
+
`package-lock.json`, `node_modules/`) hides a secret; a `.gitleaksignore` the change writes is
|
|
150
|
+
never loaded; the team's committed allowlist is applied by Trellis on the real paths; UTF-16 is
|
|
151
|
+
read as text and gzip unpacked; a merge is scanned too.
|
|
152
|
+
- **Dependencies.** Lockfile v1 to v3, aliases by their real name, a `resolved` URL that is not the
|
|
153
|
+
package's own tarball, an integrity that is not npm's, a spec moved to git, pip index, VCS and
|
|
154
|
+
`name @ url` lines, go `replace`, a byte-order mark, and a manifest that does not parse: each is
|
|
155
|
+
checked, or named by the new rule `deps-registry` (warn, block in strict), with registry
|
|
156
|
+
settings, overrides, included requirements files and the manifests and lockfiles Trellis does
|
|
157
|
+
not read yet.
|
|
158
|
+
- **A test run proves a test run.** Only an exact project test command counts (`verify.tests`, or
|
|
159
|
+
read from the project, at the base in CI), run as written, never through the tokens that
|
|
160
|
+
matched it; it counts only for the exact tree it ran on, with an environment cleared of what
|
|
161
|
+
steers a runner and no ignored runner setting present.
|
|
162
|
+
- **tests-protected covers what bends the tests**: runner configs (new ones too), the scripts a
|
|
163
|
+
test script runs, pytest's settings, CI files, the checkpoint's own settings, the agents' hook
|
|
164
|
+
and MCP configs, RSpec specs and test setup files.
|
|
165
|
+
- **Who made a commit is evidence.** Every agent's hooks record its first call of a window; the
|
|
166
|
+
guard refuses clearing `CLAUDECODE` and the person's steps run inside another command.
|
|
167
|
+
- **Merges, rebases and colleagues.** Merge commits are checked and attested; a rebased commit
|
|
168
|
+
keeps its attestation when its change is the same; a commit no pre-commit saw (cherry-pick,
|
|
169
|
+
revert, conflicted rebase) is judged by the post-commit; notes merge line by line on push, so a
|
|
170
|
+
colleague's attestations are never lost.
|
|
171
|
+
- **The MCP gateway starts only what a person trusted** (`trellis mcp trust`, an exact entry and
|
|
172
|
+
the repository files it runs), reads a call's kind from what the server runs, and passes
|
|
173
|
+
arguments intact on Windows.
|
|
174
|
+
- **Hooks fail closed** on every agent when Trellis is missing or crashes; Codex blocks by its
|
|
175
|
+
JSON deny, which survives Windows PowerShell; Codex hooks run on Windows.
|
|
176
|
+
- **Contributors without Trellis**: `verify.unattested: "judge"` judges their commits on their own,
|
|
177
|
+
as an agent's, instead of refusing them.
|
|
178
|
+
|
|
179
|
+
### Fixed
|
|
180
|
+
|
|
181
|
+
- `init --agents` never writes its pre-commit hook outside the repository. With a machine-wide
|
|
182
|
+
`core.hooksPath`, it wrote there, and every other repository on the machine then failed its
|
|
183
|
+
commits on a path that only exists in this one. It now says so and writes nothing.
|
|
184
|
+
- `graph-before-edit` steps aside when no graph is built, as documented. With graphify installed
|
|
185
|
+
and no `graphify-out/graph.json`, every code edit was refused and no query could unlock it.
|
|
186
|
+
- `doctor` warns when the Postgres MCP reads its URI from a variable that is not set: it showed
|
|
187
|
+
"ok" while the server Claude Code starts would have had no database to read.
|
|
188
|
+
- `trellis scan` reports OSV-Scanner's "no package sources found" (exit 128, a repository
|
|
189
|
+
without a lockfile) as "nothing to scan", not as a failure with no reason given.
|
|
190
|
+
|
|
191
|
+
## [0.5.0] - 2026-10-27
|
|
192
|
+
|
|
193
|
+
A third, fresh independent review found that 0.4's evidence could still be produced or
|
|
194
|
+
influenced by the agent: its own hook command could be run from the shell to record a check that
|
|
195
|
+
never happened, a crash or a timeout passed the session, a backdated commit escaped the secret
|
|
196
|
+
scan, and any command that exited 0 counted as a scan. This release ties every piece of evidence
|
|
197
|
+
to something the agent does not control.
|
|
198
|
+
|
|
199
|
+
### Security
|
|
200
|
+
|
|
201
|
+
- Evidence is recorded only for a call Claude Code itself logged: the post-tool hook checks the
|
|
202
|
+
call's id (or, failing that, the same tool and input) in the session's transcript. Running
|
|
203
|
+
`trellis hook` from the shell is refused, and would record nothing anyway.
|
|
204
|
+
- A crash fails closed: the Stop hook sends the agent back once and says the work was not
|
|
205
|
+
checked; the pre-tool hook refuses the call. Before, any exception exited 1, which Claude Code
|
|
206
|
+
treats as a pass.
|
|
207
|
+
- The guard keeps Trellis in place: it refuses uninstalling it, deleting or moving `node_modules`,
|
|
208
|
+
editing its installed code, reading or moving `~/.trellis`, and writing `.git/logs`; matching
|
|
209
|
+
deny rules are added. A change to Trellis's installed code during a session is a finding.
|
|
210
|
+
- AgentShield is run by the Stop hook itself when the agent setup changed, and a clean run is
|
|
211
|
+
recorded with the content it saw; the agent's own `scan || true` proves nothing any more.
|
|
212
|
+
- A graph query counts only when graphify's answer is in the output, and not when the program run
|
|
213
|
+
is a script inside the repository or one put first on the PATH.
|
|
214
|
+
|
|
215
|
+
### Changed
|
|
216
|
+
|
|
217
|
+
- A session's commits are those reachable from local branches and HEAD's new reflog entries but
|
|
218
|
+
not from where things stood at the start, nor from a remote. Commit dates play no part: a
|
|
219
|
+
backdated commit is scanned, work committed on another branch is judged, and a teammate's work
|
|
220
|
+
pulled mid-session is not blamed on the agent.
|
|
221
|
+
- Without gitleaks installed, the session's commits are reported unscanned; a pre-commit hook is
|
|
222
|
+
no longer trusted on its own (it can be skipped in more ways than a flag shows).
|
|
223
|
+
- The evidence is an append-only log, one signed line per event, so post-tool hooks running in
|
|
224
|
+
parallel no longer lose each other's evidence; the snapshot is written atomically.
|
|
225
|
+
- "Wired" is decided by parsing commands where commands run (package scripts, hooks, shell
|
|
226
|
+
scripts, CI steps read by key, Makefile and justfile recipes, Jenkins `sh` steps). A string in a
|
|
227
|
+
JavaScript file, an `echo`, a step's name or a comment no longer counts.
|
|
228
|
+
- The Stop hook reads a manifest at the start revision only when the file is a manifest, so its
|
|
229
|
+
cost no longer grows with the number of changed files; its budget is 180 s. The post-tool hook
|
|
230
|
+
answers calls that cannot be evidence without reading the transcript.
|
|
231
|
+
- Dependency detection: only the dependency tables of `pyproject.toml` are read (not
|
|
232
|
+
`classifiers`, `keywords` or a linter's `select`); `// indirect` go.mod lines, local paths,
|
|
233
|
+
URLs, git sources and installer upgrades (`pip install -U pip`) are not new dependencies.
|
|
234
|
+
- Session files older than 30 days are removed when a session starts.
|
|
235
|
+
- The OSV-Scanner install hint no longer suggests `@latest`.
|
|
236
|
+
|
|
237
|
+
### Fixed
|
|
238
|
+
|
|
239
|
+
- The docs no longer claim what the code does not do: `graph-before-edit` only warns at Stop for
|
|
240
|
+
a write made through the shell; one browser check verifies every changed UI file whose current
|
|
241
|
+
content it was taken over, not only those on the page it showed; `init` enables the tools found
|
|
242
|
+
on the machine that runs it.
|
|
243
|
+
|
|
244
|
+
## [0.4.0] - 2026-10-27
|
|
245
|
+
|
|
246
|
+
A second independent review showed that 0.3's evidence lived where the agent could write it (a
|
|
247
|
+
state file in the temp folder, the transcript, file times, git's index), and that a missing
|
|
248
|
+
record fell back silently to the weak path. This release records evidence as it happens, signs
|
|
249
|
+
it, guards it, and makes the agent look before it writes.
|
|
250
|
+
|
|
251
|
+
### Added
|
|
252
|
+
|
|
253
|
+
- **Look before you write.** A PreToolUse hook refuses an edit before it happens:
|
|
254
|
+
- `graph-before-edit`: a code file cannot change until the code graph has been asked once this
|
|
255
|
+
session (`graphify query`, `path` or `explain`). One query unlocks every edit. Steps aside
|
|
256
|
+
when the repository has no graph to ask.
|
|
257
|
+
- `schema-before-change`: a migration, model or schema file cannot change until the database
|
|
258
|
+
schema has been looked at with the Postgres MCP.
|
|
259
|
+
Both are checked again at Stop from git, so a write through the shell is caught too.
|
|
260
|
+
- **An evidence ledger**, written by the PostToolUse hook the moment a tool call succeeds: the
|
|
261
|
+
content hash of each changed file when it was seen in the browser or scanned by AgentShield,
|
|
262
|
+
that the graph or the schema was asked, which libraries Context7 was asked about. A UI file is
|
|
263
|
+
verified only when its current content is the content the browser check saw: backdating a
|
|
264
|
+
file or forging a transcript line gains nothing.
|
|
265
|
+
- **`evidence-intact`**, blocking by default: an altered or missing session record, a file
|
|
266
|
+
hidden from git (`update-index --assume-unchanged`, `--skip-worktree`) or an edited
|
|
267
|
+
`.git/info/exclude` is a finding. The changes are still counted.
|
|
268
|
+
- An evidence guard: the PreToolUse hook refuses shell commands that would write the session
|
|
269
|
+
record, the transcripts or `info/exclude`, or set an index flag; `init --agents` adds Claude
|
|
270
|
+
Code deny rules for the same paths and for the signing key.
|
|
271
|
+
- Dependency detection beyond npm: requirements files, pyproject.toml, Cargo.toml, go.mod,
|
|
272
|
+
composer.json and Gemfile, at the root and in workspaces (package.json `workspaces`,
|
|
273
|
+
pnpm-workspace.yaml). A dependency written into any of them counts as added.
|
|
274
|
+
- A global install (`npm i -g @synovitec/trellis`) for repositories without node_modules: the
|
|
275
|
+
hooks and the pre-commit hook then call `trellis` on PATH. Without either, `init` writes no hook
|
|
276
|
+
rather than one that fails on every event.
|
|
277
|
+
- SECURITY.md (with the threat model) and CONTRIBUTING.md.
|
|
278
|
+
|
|
279
|
+
### Changed
|
|
280
|
+
|
|
281
|
+
- The session record moves into the repository's git folder (`.git/trellis/sessions`) and is
|
|
282
|
+
signed with a per-user key (`~/.trellis/key`).
|
|
283
|
+
- Changed files are read by content, including files hidden by an index flag, files put away in a
|
|
284
|
+
stash, and deletions (a deletion seen in the browser is verified).
|
|
285
|
+
- Secret scanning covers every commit created during the session, on any branch or only in the
|
|
286
|
+
reflog. Without gitleaks, a pre-commit hook is trusted only if no commit skipped it.
|
|
287
|
+
- A tool call counts only when it succeeded; `--help`, `--version`, `command -v`, a heredoc body,
|
|
288
|
+
a shadowing function or alias, and `false && ...` run nothing. A browser check must be on the
|
|
289
|
+
configured origin exactly (scheme, host and port).
|
|
290
|
+
- A Context7 lookup must name the package as a whole word.
|
|
291
|
+
- `config-unchanged` now warns by default and never asks the agent to revert the change: the
|
|
292
|
+
person may have made it, and the session is judged by the start's rules anyway.
|
|
293
|
+
- `init` enables only the tools it finds and proposes the rest with how to install each, so a
|
|
294
|
+
fresh `doctor` is green. AgentShield counts as wired when the agent hooks demand it.
|
|
295
|
+
- Stop has a 120 s budget and gitleaks 40 s, so a slow scan is reported, not cut into a pass.
|
|
296
|
+
- The session brief declares the evidence off limits and still does not say which rules only warn.
|
|
297
|
+
|
|
298
|
+
### Fixed
|
|
299
|
+
|
|
300
|
+
- Trellis did not recognise the pre-commit hook it writes itself (0.3): `doctor` reported Gitleaks
|
|
301
|
+
unwired right after `init --agents`. The hook now names its script literally.
|
|
302
|
+
- A missing session record, session id or git repository no longer degrades silently: the first
|
|
303
|
+
two block once and say why, the third is reported.
|
|
304
|
+
|
|
305
|
+
## [0.3.0] - 2026-10-27
|
|
306
|
+
|
|
307
|
+
An independent review showed that 0.2's enforcement could be made inert while looking active.
|
|
308
|
+
This release makes the rules judge evidence and fail closed.
|
|
309
|
+
|
|
310
|
+
### Security
|
|
311
|
+
|
|
312
|
+
- Every command now names the scoped package (`npx @synovitec/trellis`). The bare `npx trellis`
|
|
313
|
+
that 0.2's README, AGENTS.md block and rule messages printed resolves to an unrelated package
|
|
314
|
+
on the registry when this one is not installed, and npx would have downloaded and run it.
|
|
315
|
+
- The hooks call the installed script with node (`node "$CLAUDE_PROJECT_DIR/node_modules/..."`),
|
|
316
|
+
never through npx. The generated pre-commit hook does the same, and says how to remove itself
|
|
317
|
+
when the package is gone instead of failing with an npx error.
|
|
318
|
+
- `trellis scan` runs AgentShield pinned (`ecc-agentshield@1.6.0`), not whatever is newest.
|
|
319
|
+
|
|
320
|
+
### Changed
|
|
321
|
+
|
|
322
|
+
- Changed files are read from git, not from the transcript: a snapshot at session start (HEAD
|
|
323
|
+
and the dirty files, by content), compared at stop. A write through the shell, an MCP editor or
|
|
324
|
+
a subagent counts like one through the Edit tool.
|
|
325
|
+
- Subagent transcripts (`<session>/subagents/*.jsonl`) are read: their checks and their edits
|
|
326
|
+
are the session's.
|
|
327
|
+
- A rule is met by a real run, parsed as commands, after the change, by time: `echo gitleaks`,
|
|
328
|
+
a comment, `gitleaks version`, `browser_close` and a scan run before the change no longer count.
|
|
329
|
+
A browser check is a navigation to the configured origin followed by a snapshot or screenshot.
|
|
330
|
+
A Context7 lookup must name the added package; a dependency written into package.json by hand
|
|
331
|
+
counts as added. MCP tools are recognised under plugin and connector names too.
|
|
332
|
+
- `secrets-scanned`: the Stop hook runs gitleaks itself over the session's commits.
|
|
333
|
+
- `graph-first` can only warn: sending an agent back after the work is done cannot fix it, and
|
|
334
|
+
a grep over a command's output (`npm test | grep fail`) is no longer a file search.
|
|
335
|
+
- Blocks are counted per session (`maxBlocks`, default 2), then the person decides. Claude
|
|
336
|
+
Code's `stop_hook_active` is no longer trusted, since any other Stop hook sets it.
|
|
337
|
+
- The session brief lists what will be checked, not which rules only warn.
|
|
338
|
+
- `doctor` matches runs, not mentions (comments are ignored), reads Makefile, justfile,
|
|
339
|
+
Jenkinsfile and more CI files, and reports a hook whose script is not installed as broken.
|
|
340
|
+
- The default UI paths cover any folder (monorepos) and leave out tests, stories and build output.
|
|
341
|
+
- abatty is an optional peer dependency: Trellis runs the project's own copy.
|
|
342
|
+
|
|
343
|
+
### Added
|
|
344
|
+
|
|
345
|
+
- `config-unchanged`: trellis.config.json edited during a session is a finding, and the session
|
|
346
|
+
is judged against the rules as they were when it started. It blocks unless set otherwise.
|
|
347
|
+
- A broken config, or a transcript that cannot be read, is reported, never a silent pass.
|
|
348
|
+
- The hook finds the project from `CLAUDE_PROJECT_DIR`, else the nearest folder holding the config.
|
|
349
|
+
- `trellis uninstall`: the hooks, Trellis's pre-commit hook and the AGENTS.md block come out; the
|
|
350
|
+
config and MCP servers stay.
|
|
351
|
+
- `init` upgrades hook entries written by an older version, and uses the hooks folder git really
|
|
352
|
+
runs (core.hooksPath, worktrees).
|
|
353
|
+
|
|
354
|
+
## [0.2.0] - 2026-10-27
|
|
355
|
+
|
|
356
|
+
### Added
|
|
357
|
+
|
|
358
|
+
- Agent hooks, so a coding agent uses the tools systematically rather than when it remembers to.
|
|
359
|
+
`trellis init --agents` adds three Claude Code hooks to `.claude/settings.json` (beside the
|
|
360
|
+
team's own, never replacing one) and, when there is none, a pre-commit hook that runs the
|
|
361
|
+
secret scan for every agent and every person.
|
|
362
|
+
- `trellis hook session-start` hands the agent the routing, what is broken here, and the rules
|
|
363
|
+
it will be held to.
|
|
364
|
+
- `trellis hook post-tool` reminds it once, at the third file search with no graph query, to
|
|
365
|
+
ask the code graph.
|
|
366
|
+
- `trellis hook stop` reads the session transcript and judges what was done, not what was
|
|
367
|
+
claimed. Five rules, each active only when its tool is enabled: `ui-verified` (a UI edit is
|
|
368
|
+
followed by a browser check), `agent-setup-scanned` (an agent config edit by an AgentShield
|
|
369
|
+
run), `docs-looked-up` (a new dependency by a Context7 lookup), `graph-first` (many searches
|
|
370
|
+
by a graph query), `secrets-scanned` (a commit by a secret scan, unless a git hook runs one).
|
|
371
|
+
- `agents` in `trellis.config.json`: `mode` (`block` sends the agent back with the reason,
|
|
372
|
+
`warn` tells the person, `off`), per-rule overrides, and the UI and agent-setup path globs.
|
|
373
|
+
`init --agents` starts in `warn`. A stop already sent back once is never blocked again, so a
|
|
374
|
+
session cannot loop.
|
|
375
|
+
- `trellis doctor` reports agent rules that are configured but have no hook to run them.
|
|
376
|
+
|
|
377
|
+
## [0.1.0] - 2026-10-27
|
|
378
|
+
|
|
379
|
+
### Added
|
|
380
|
+
|
|
381
|
+
- `trellis init`: writes `trellis.config.json` from the stack it finds, adds the MCP servers
|
|
382
|
+
(Context7, Playwright MCP, Postgres MCP) to `.mcp.json` without touching existing entries, puts
|
|
383
|
+
the tool routing in a managed block of `AGENTS.md`, and git-ignores the tools' output. Run
|
|
384
|
+
twice, it changes nothing. `--dry-run` writes nothing.
|
|
385
|
+
- `trellis doctor`: every enabled tool as ok, warn, unwired (installed, but nothing calls it) or
|
|
386
|
+
missing, with the command that fixes each. `--json` for machines, `--strict` fails on warnings.
|
|
387
|
+
- `trellis guide [tool|role]`: which tool answers which question, its rule, and its state here.
|
|
388
|
+
- `trellis scan`: runs Gitleaks, OSV-Scanner and AgentShield. A scanner that could not run fails
|
|
389
|
+
the scan unless `--allow-missing`; `--history` scans the whole git history.
|
|
390
|
+
- `trellis gate|explain|prove|ratchet|status|rules`: abatty's commands, run as they are.
|
|
391
|
+
- Twelve tools across four roles: AgentShield (frame); graphify, Context7, Postgres MCP, Find
|
|
392
|
+
Skills (guide); ECC, Playwright MCP (help); abatty, Playwright E2E, Gitleaks, OSV-Scanner,
|
|
393
|
+
Lighthouse (guard).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
Copyright 2026 Synovitec
|
|
179
|
+
|
|
180
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
181
|
+
you may not use this file except in compliance with the License.
|
|
182
|
+
You may obtain a copy of the License at
|
|
183
|
+
|
|
184
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
185
|
+
|
|
186
|
+
Unless required by applicable law or agreed to in writing, software
|
|
187
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
188
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
189
|
+
See the License for the specific language governing permissions and
|
|
190
|
+
limitations under the License.
|