toolgraph 0.1.0__tar.gz → 0.2.0__tar.gz
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.
- toolgraph-0.2.0/CHANGELOG.md +386 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/PKG-INFO +227 -62
- {toolgraph-0.1.0 → toolgraph-0.2.0}/README.md +224 -60
- toolgraph-0.2.0/contracts/control-preflight.schema.json +128 -0
- toolgraph-0.2.0/contracts/observation-window.schema.json +113 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/pyproject.toml +41 -15
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/__init__.py +1 -1
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/artifact_safety.py +8 -10
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/artifacts.py +33 -16
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/cli.py +204 -92
- toolgraph-0.2.0/toolgraph/config.py +227 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/control_plan.py +245 -76
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/crawler/client.py +2 -5
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/crawler/crawl.py +4 -6
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/crawler/transports.py +13 -4
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/graph/driver.py +104 -22
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/graph/loader.py +2 -3
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/graph/queries.py +186 -265
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/graph/schema.py +129 -11
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/graph/selector.py +33 -58
- toolgraph-0.2.0/toolgraph/health.py +229 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/manifest/ingest.py +452 -51
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/models.py +11 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/policy_bundle.py +18 -24
- toolgraph-0.2.0/toolgraph/policy_propose.py +953 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/policy_review.py +25 -23
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/preflight.py +8 -9
- toolgraph-0.2.0/toolgraph/quickstart/README.md +55 -0
- toolgraph-0.2.0/toolgraph/quickstart/governance.yaml +63 -0
- toolgraph-0.2.0/toolgraph/redaction.py +128 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/review_candidates.py +56 -93
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/server/app.py +4 -13
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/uris.py +1 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/yaml_loader.py +1 -3
- toolgraph-0.1.0/CHANGELOG.md +0 -153
- toolgraph-0.1.0/contracts/control-preflight.schema.json +0 -75
- toolgraph-0.1.0/toolgraph/config.py +0 -106
- toolgraph-0.1.0/toolgraph/quickstart/governance.yaml +0 -28
- toolgraph-0.1.0/toolgraph/redaction.py +0 -82
- {toolgraph-0.1.0 → toolgraph-0.2.0}/.gitignore +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/LICENSE +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/SECURITY.md +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/contracts/control-plan.schema.json +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/contracts/policy-bundle.schema.json +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/contracts/policy-review-plan.schema.json +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/contracts/preflight.schema.json +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/contracts/review-annotations.schema.json +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/contracts/tracegraph/README.md +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/contracts/tracegraph/review-candidates.schema.json +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/contracts/tracegraph/v1.json +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/crawler/__init__.py +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/errors.py +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/graph/__init__.py +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/graph/store.py +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/manifest/__init__.py +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/manifest/parser.py +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/quickstart/policy_gateway_server.py +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/quickstart/servers.yaml +0 -0
- {toolgraph-0.1.0 → toolgraph-0.2.0}/toolgraph/server/__init__.py +0 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Toolgraph are documented here.
|
|
4
|
+
|
|
5
|
+
## 0.2.0 - 2026-10-15
|
|
6
|
+
|
|
7
|
+
Third alpha. Read **Upgrading from 0.1.0** below before installing: it lists
|
|
8
|
+
seven changes an existing setup, checkout or embedding program can notice.
|
|
9
|
+
The one most installations meet first is that commands now refuse a backend
|
|
10
|
+
variable that disagrees with the config file.
|
|
11
|
+
|
|
12
|
+
### Upgrading from 0.1.0
|
|
13
|
+
|
|
14
|
+
- Commands stop with `ERROR: conflicting backend configuration (...)` when
|
|
15
|
+
`TOOLGRAPH_BACKEND` or `TOOLGRAPH_DB_PATH` disagrees with the config file;
|
|
16
|
+
0.1.0 silently let the variable win. Pass `toolgraph --config <file>
|
|
17
|
+
<command>`, or unset the variable; when it comes from `.env`, remove it
|
|
18
|
+
there, since the next command loads `.env` again. See the first bullet
|
|
19
|
+
under Changed.
|
|
20
|
+
- `control-preflight` can now return `advisory_warn` for a plan that 0.1.0
|
|
21
|
+
answered with `advisory_allow`. See the `control-preflight` bullet under
|
|
22
|
+
Changed before relying on `advisory_allow` as a gate.
|
|
23
|
+
- A program that imports Toolgraph no longer gets `./.env` loaded for it.
|
|
24
|
+
- `toolgraph check` no longer creates a missing Ladybug database; it exits 1.
|
|
25
|
+
A script that used `check` to initialize should run `toolgraph init` or
|
|
26
|
+
`init-schema` first. See the `check` bullet under Changed.
|
|
27
|
+
- Identities, control-plan strings and review notes that 0.1.0 accepted can
|
|
28
|
+
now be refused as credential-shaped (for example `?access_token=` or `?credential=`). See
|
|
29
|
+
the credential bullet under Changed.
|
|
30
|
+
- `GraphState.token()` and `queries.with_generation()` are gone; use the
|
|
31
|
+
`graph_state` token results carry and `queries.with_graph_state()`. See
|
|
32
|
+
Removed.
|
|
33
|
+
- The repository's `examples/policy_propose_from_observed.py`, which 0.1.0
|
|
34
|
+
introduced, is gone; use `toolgraph policy propose`, which reads the live
|
|
35
|
+
graph. See its entry under Removed for the provenance flags.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- **`toolgraph doctor [--json]`** checks the configuration file (present,
|
|
40
|
+
valid JSON, mode 0600, and `backend_source`: which of `--config`, the
|
|
41
|
+
environment, the config file or the built-in default decided the backend),
|
|
42
|
+
backend connectivity, the physical schema and the graph state. It opens the
|
|
43
|
+
database read-only — on Neo4j in READ access mode — and redacts what it
|
|
44
|
+
reports. It exits 1 when any check fails; a warning alone exits 0.
|
|
45
|
+
- **`toolgraph ingest-manifest --dry-run [--json]`** reports the full delta a
|
|
46
|
+
manifest would apply without changing the database: agents, policies,
|
|
47
|
+
grants and `granted_by`, data access, `GOVERNED_BY`, expected exceptions,
|
|
48
|
+
provenance, and the resources and tools that would be pruned for having no
|
|
49
|
+
edges left. It exits 1 when the manifest would be rejected; with `--json` it
|
|
50
|
+
then prints a rejection report with the warnings, not a delta (the delta
|
|
51
|
+
lists stay empty because no comparison was made). A missing database file is refused and none is
|
|
52
|
+
created; an existing but uninitialized one is refused with `init-schema`
|
|
53
|
+
guidance. On Neo4j the preview
|
|
54
|
+
session opens in READ access mode, so the server rejects an auto-commit write
|
|
55
|
+
issued through it (verified on Neo4j 5.26 over `bolt://`); that is an access
|
|
56
|
+
mode, not access control, and does not replace a read-only role. `--json`
|
|
57
|
+
requires `--dry-run`. Plain `ingest-manifest` behaves as before.
|
|
58
|
+
- **`toolgraph policy propose GOVERNANCE OBSERVED`** proposes grant changes to
|
|
59
|
+
`governance.yaml` from an observation-window JSONL export
|
|
60
|
+
(`contracts/observation-window.schema.json`), resolving grants against the
|
|
61
|
+
live graph. It never writes to the graph and never edits the manifest. The
|
|
62
|
+
export's envelope must match the manifest and the live graph;
|
|
63
|
+
`--revalidate-current` accepts a moved generation and re-checks additions
|
|
64
|
+
against the current strict catalog, and `--unverified-provenance` accepts a
|
|
65
|
+
missing envelope or identity and labels the result unverified. Other
|
|
66
|
+
options: `--min-would-block` (default 3), `--window-label`, `--json`, and
|
|
67
|
+
`-o/--output`, which cannot be combined with `--json` and writes the file
|
|
68
|
+
atomically with mode 0600 (a failure before the replacement leaves an
|
|
69
|
+
existing file as it was). It refuses, with a redacted `ERROR:` diagnostic
|
|
70
|
+
and exit 1 rather than a traceback: an export over 50,000,000 bytes,
|
|
71
|
+
malformed YAML, and any proposal row it would emit whose agent, tool or note
|
|
72
|
+
is credential-shaped — that error names the position (`additions[0].tool`),
|
|
73
|
+
never the value. See
|
|
74
|
+
`docs/policy-propose.md`.
|
|
75
|
+
- **`control-preflight` reports potential exposures.** Each record in the new
|
|
76
|
+
`potential_exposures` field joins an eligible reader tool on one plan node to
|
|
77
|
+
an eligible writer tool on a distinct node downstream of it, with the
|
|
78
|
+
resources involved (redacted) and a shortest witness path. It marks a
|
|
79
|
+
potential path, not proof that data moved. The search is bounded: at most 100 records,
|
|
80
|
+
64,000 bytes of records and 65,536 traversal steps; when it stops early the
|
|
81
|
+
result also carries `truncated: true` and `total_candidate_pairs_found`,
|
|
82
|
+
which is then a lower bound. The fields are additive to the v1 schema. See
|
|
83
|
+
`docs/adr/0013-bounded-control-plan-preflight.md`.
|
|
84
|
+
- `toolgraph audit-report` JSON now carries the graph state it was read at
|
|
85
|
+
(`graph_generation`, `graph_instance_id`, `graph_state`,
|
|
86
|
+
`graph_state_verified`), as the MCP `audit_report` tool already did. The
|
|
87
|
+
Markdown format is unchanged.
|
|
88
|
+
- `toolgraph example init` now writes a `README.md` into the generated
|
|
89
|
+
directory. `docs/` is excluded from the sdist, so a user who installed from
|
|
90
|
+
PyPI previously landed in a directory of three unexplained files with only
|
|
91
|
+
the CLI's one-line hint to go on. The file lists the commands in order,
|
|
92
|
+
states the expected outcome, and links the full guide.
|
|
93
|
+
- `examples/README.md` maps each `servers-*.yaml` / `governance-*.yaml` input
|
|
94
|
+
to the script or workflow that consumes it, and to what it needs to run.
|
|
95
|
+
- A `Documentation` URL in the project metadata, so the beginner guide is
|
|
96
|
+
reachable from the PyPI sidebar.
|
|
97
|
+
|
|
98
|
+
### Changed
|
|
99
|
+
|
|
100
|
+
- **An environment variable can no longer silently override `toolgraph
|
|
101
|
+
init`'s config.** Without `--config`, `TOOLGRAPH_BACKEND` and
|
|
102
|
+
`TOOLGRAPH_DB_PATH` used to win over `.toolgraph/config.json` (or the file
|
|
103
|
+
`TOOLGRAPH_CONFIG` names). Copying `.env.example` (which sets
|
|
104
|
+
`TOOLGRAPH_BACKEND=neo4j`) into a directory where `toolgraph init` had
|
|
105
|
+
written a Ladybug config sent every command to Neo4j with no warning. When
|
|
106
|
+
the two disagree on the backend or on the resolved database path, commands
|
|
107
|
+
now stop with `ERROR: conflicting backend configuration (...)`. To migrate,
|
|
108
|
+
pass `--config <file>` to choose the file, or unset the variable (remove it
|
|
109
|
+
from `.env` if it is set there). Agreeing
|
|
110
|
+
values, or only one of the two being set, behave as before. `--help`,
|
|
111
|
+
`toolgraph init` and `toolgraph example init` are not blocked, so `init` can
|
|
112
|
+
still be used to rewrite the file. `toolgraph doctor` reports which input
|
|
113
|
+
decides the backend as `backend_source`.
|
|
114
|
+
- **Only the CLI reads `./.env`.** Importing `toolgraph` as a library used to
|
|
115
|
+
load `.env` from the current directory into `os.environ`. The CLI, including
|
|
116
|
+
`toolgraph serve`, still does; a program that embeds Toolgraph and relied on
|
|
117
|
+
that should call `toolgraph.config.load_dotenv_file()` or set its own
|
|
118
|
+
environment.
|
|
119
|
+
- **`toolgraph check` no longer creates a database.** It opens the configured
|
|
120
|
+
Ladybug file read-only, so a mistyped `db_path` exits 1 with
|
|
121
|
+
`BACKEND_UNAVAILABLE ... does not exist` instead of creating an empty
|
|
122
|
+
database and printing `OK`. Run `toolgraph init` or `init-schema` first.
|
|
123
|
+
- **`control-preflight` can now warn where 0.1.0 allowed.** The decision is
|
|
124
|
+
`advisory_warn`, not `advisory_allow`, when the plan has a potential
|
|
125
|
+
exposure (see Added) or the exposure search was truncated. The exit code is
|
|
126
|
+
still 0 for both. A consumer that gates on `advisory_allow` will see more
|
|
127
|
+
warnings and should review them rather than treat them as failures of the
|
|
128
|
+
tool. A result without the exposure fields, from 0.1.0, means "not
|
|
129
|
+
checked", not "none found"; `total_candidate_pairs_found` on a truncated
|
|
130
|
+
result is a lower bound. ADR-0013 states the decision rules.
|
|
131
|
+
- **Credential checks are shared and refuse more.** Agent and tool identities
|
|
132
|
+
(preflight, `policy compile`, `policy review-plan`, `policy propose`) and
|
|
133
|
+
strings in control plans are now also refused when they carry
|
|
134
|
+
`?access_token=` or `?credential=`, a password URI whose username is an
|
|
135
|
+
email address, or an `@` in a query after a port
|
|
136
|
+
(`https://user@host:443?x=a@b`). `review-candidates annotate` notes that
|
|
137
|
+
assign `secret=` or `access_token=` are now refused too. Error text
|
|
138
|
+
now also hides `credential=...` and a bare `Bearer <token>`. Nothing that
|
|
139
|
+
was refused or hidden before is accepted or shown now.
|
|
140
|
+
- Control-plan validation errors give the location and the reason, never the
|
|
141
|
+
rejected value: `invalid control plan at nodes.0.principals.1: ...`, and a
|
|
142
|
+
bad candidate tool is reported by its index.
|
|
143
|
+
- A file that is not a Ladybug database is now a non-retryable
|
|
144
|
+
`BACKEND_CORRUPT` configuration error, printed as one `ERROR:` line. In
|
|
145
|
+
0.1.0 it surfaced as a Python traceback from Ladybug.
|
|
146
|
+
- `config.configure()` closes the cached backend connection, so a library
|
|
147
|
+
caller switching configs no longer keeps talking to the previous database.
|
|
148
|
+
- Deny-path rows (`check-access` evidence, `unsafe-tools`, `audit-report`,
|
|
149
|
+
selector deny paths) now have a fixed order when rows used to tie: one
|
|
150
|
+
tool under several DENY policies, one tool name on several servers, or a
|
|
151
|
+
READS and a WRITES edge to the same resource. Ties are broken by tool key,
|
|
152
|
+
policy and mode; before, the backend chose, and Ladybug and Neo4j could
|
|
153
|
+
disagree. Rows that did not tie keep their order.
|
|
154
|
+
|
|
155
|
+
### Removed
|
|
156
|
+
|
|
157
|
+
- **`examples/policy_propose_from_observed.py`, the offline proposal script.**
|
|
158
|
+
There is no longer a graph-free way to produce a proposal. Its replacement,
|
|
159
|
+
`toolgraph policy propose` (see Added), applies the same row and proposal
|
|
160
|
+
rules but resolves grants against the live graph, and checks the envelope
|
|
161
|
+
against it too: an envelope the script accepted is refused when it lacks a
|
|
162
|
+
graph instance id, or when its generation, digest or instance differs from
|
|
163
|
+
the live graph. Re-export from the current graph, or pass
|
|
164
|
+
`--revalidate-current` (a moved generation) or `--unverified-provenance`
|
|
165
|
+
(missing identity) to proceed. The script was repository-only and never
|
|
166
|
+
shipped in the distribution. Its tests now drive `toolgraph.policy_propose`.
|
|
167
|
+
- `GraphState.token()`, which nothing called and which returned a different
|
|
168
|
+
set of keys than the `graph_state` token that results actually carry.
|
|
169
|
+
- `toolgraph.graph.queries.with_generation()`, which had no production
|
|
170
|
+
caller; use `with_graph_state()`. A strict read that sees the graph state
|
|
171
|
+
change on every retry now raises `queries.GraphStateChurnError`, a
|
|
172
|
+
`RuntimeError` subclass with the same message, so CLI output and exit codes
|
|
173
|
+
are unchanged.
|
|
174
|
+
|
|
175
|
+
### Fixed
|
|
176
|
+
|
|
177
|
+
- Backend configuration errors print one `ERROR:` line and exit 1 instead of
|
|
178
|
+
a Python traceback: an unknown `TOOLGRAPH_BACKEND`, a missing `ladybug`
|
|
179
|
+
extra, and a schema-version mismatch. `toolgraph doctor` is the exception
|
|
180
|
+
by design: it reports the same failure as a FAIL check (exit 1) in its text
|
|
181
|
+
or JSON report.
|
|
182
|
+
- An outage or other backend error while reading the schema version is no
|
|
183
|
+
longer taken for a fresh database, so `init`, `init-schema`, `crawl` and
|
|
184
|
+
`ingest-manifest` stop before running any DDL.
|
|
185
|
+
- `toolgraph check` no longer prints credentials embedded in `NEO4J_URI`, and
|
|
186
|
+
`ingest-manifest` warnings and notices are redacted.
|
|
187
|
+
- `toolgraph init` with an unusable `--state-dir` prints `ERROR: cannot
|
|
188
|
+
initialize ...` and exits 1 instead of a traceback raised from its cleanup;
|
|
189
|
+
a partial database file it cannot remove is named in a warning.
|
|
190
|
+
- `policy review-plan` refuses a graph with no instance id instead of writing
|
|
191
|
+
a plan its own schema rejects.
|
|
192
|
+
- `policy compile` (and `build_policy_bundle()`) refuses a graph state whose
|
|
193
|
+
generation is not a non-negative integer — a boolean, float, string, null
|
|
194
|
+
or negative value — instead of writing it into the bundle. Toolgraph's own
|
|
195
|
+
graph always yields one; the check matters for a hand-edited graph or a
|
|
196
|
+
library caller's own graph reader.
|
|
197
|
+
- Writing a private artifact — `init`'s config file, `preflight --out`,
|
|
198
|
+
`control-preflight --out`, `policy compile --output`, `policy review-plan
|
|
199
|
+
--output` — no longer reports failure when the new file was already in
|
|
200
|
+
place and only re-applying its 0600 mode afterwards failed. The file is
|
|
201
|
+
created 0600 before it is written, so that second step was redundant.
|
|
202
|
+
- `crawl` no longer prints `vNone` for a server that reports no version.
|
|
203
|
+
- The packaged quickstart governance manifest now carries `provenance` on
|
|
204
|
+
every authored edge. The beginner guide claimed every authored edge had an
|
|
205
|
+
evidence pointer and then told the reader to run `unbacked-edges`, which
|
|
206
|
+
listed all three of them. The audit now comes back empty, and both the
|
|
207
|
+
manifest and the guide say what that does and does not prove: the demo tools
|
|
208
|
+
return strings and touch no file, so the READS/WRITES effects are synthetic
|
|
209
|
+
and their evidence cites the fixture that defines them. An empty audit means
|
|
210
|
+
every edge has a pointer, not that a claim is true. As a result `read_note`'s
|
|
211
|
+
risk in the quickstart drops from 0.4 to 0.0 and the compiled bundle's
|
|
212
|
+
digest changes.
|
|
213
|
+
- Beginner guide corrections (both languages): the shared-backend section
|
|
214
|
+
needs a repository clone, since `docker-compose.yml` and `.env.example` ship
|
|
215
|
+
with neither the package nor the generated quickstart; `policy compile`
|
|
216
|
+
prints metadata describing the bundle, not the bundle; and the gateway step
|
|
217
|
+
is labelled optional, matching the README's statement that memtomem-stm is
|
|
218
|
+
not a required dependency.
|
|
219
|
+
|
|
220
|
+
### Dependencies
|
|
221
|
+
|
|
222
|
+
- The `pyjwt` security floor rises from `>=2.13.0` to `>=2.15.0`. 2.13.0
|
|
223
|
+
carries 14 advisories published after it was set: PYSEC-2026-4140 through
|
|
224
|
+
PYSEC-2026-4152 and PYSEC-2026-4183. PYSEC-2026-4141 and PYSEC-2026-4183
|
|
225
|
+
are fixed in 2.15.0, PYSEC-2026-4146 lists 2.13.0 as its last affected
|
|
226
|
+
version, and the rest are fixed in 2.14.0. The floor is what keeps an
|
|
227
|
+
install from PyPI from resolving an older version; the lock already resolves
|
|
228
|
+
a newer one. Toolgraph does not import `pyjwt` itself; it is pinned for the
|
|
229
|
+
MCP SDK's dependency tree.
|
|
230
|
+
|
|
231
|
+
### Testing
|
|
232
|
+
|
|
233
|
+
- The `sse` crawl transport and auth-header forwarding on both HTTP
|
|
234
|
+
transports are now exercised end to end, at the lockfile version and at the
|
|
235
|
+
declared MCP SDK floor. Neither had been run before; no runtime behaviour
|
|
236
|
+
changed.
|
|
237
|
+
|
|
238
|
+
## 0.1.0 - 2026-09-10
|
|
239
|
+
|
|
240
|
+
Second alpha. Upgrading from `0.0.1` takes three things: the MCP SDK 2.x, one
|
|
241
|
+
new field for anything caching MCP responses, and — if you generated a
|
|
242
|
+
quickstart with `0.0.1` — a regenerated server script. Read **Upgrading from
|
|
243
|
+
0.0.1** below before installing.
|
|
244
|
+
|
|
245
|
+
### Upgrading from 0.0.1
|
|
246
|
+
|
|
247
|
+
`toolgraph example init` copies a quickstart out of the package, so a
|
|
248
|
+
quickstart generated by `0.0.1` holds its own copy of the demo MCP server —
|
|
249
|
+
one that imports `mcp.server.fastmcp`. Upgrading that environment installs MCP
|
|
250
|
+
2.x and the copied server stops importing, so the next `toolgraph crawl`
|
|
251
|
+
fails. Updating the package does not touch a directory it already wrote, and
|
|
252
|
+
`example init` refuses an existing destination on purpose.
|
|
253
|
+
|
|
254
|
+
To migrate one, generate a fresh quickstart beside it and move your own work
|
|
255
|
+
across:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
toolgraph example init quickstart-0.1.0
|
|
259
|
+
# Keep your authored governance and your database; take the new server script.
|
|
260
|
+
cp quickstart/governance.yaml quickstart-0.1.0/governance.yaml
|
|
261
|
+
cp -R quickstart/.toolgraph quickstart-0.1.0/.toolgraph
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
`servers.yaml` records the interpreter that generated it, so regenerating from
|
|
265
|
+
the upgraded environment already points at the right one; check it only if you
|
|
266
|
+
generated from somewhere else. Re-run `toolgraph crawl`, then
|
|
267
|
+
`toolgraph ingest-manifest`, to confirm.
|
|
268
|
+
|
|
269
|
+
Verified end to end against the published `0.0.1`: a quickstart it generated
|
|
270
|
+
crawls cleanly on `0.0.1`, fails on the next crawl once the environment holds
|
|
271
|
+
`0.1.0`, and returns the same eligibility verdicts after these steps.
|
|
272
|
+
|
|
273
|
+
### Added
|
|
274
|
+
|
|
275
|
+
- `examples/policy_propose_from_observed.py` reads a window of observed gateway
|
|
276
|
+
usage and prints a reviewable `governance.yaml` grant proposal. It never
|
|
277
|
+
writes governance. Repository-only — it is not part of the distribution — and
|
|
278
|
+
deliberately narrow: it requires an envelope declaring the governance digest
|
|
279
|
+
it was recorded against and refuses a mismatch, refuses an export with no
|
|
280
|
+
envelope unless `--unverified-provenance` is given, proposes a removal only
|
|
281
|
+
from an explicit zero-call row, and accepts only fully qualified timestamps
|
|
282
|
+
with an explicit UTC offset. Digest equality shows what the export declared,
|
|
283
|
+
not where its rows came from; issue #89 tracks what a real binding needs.
|
|
284
|
+
|
|
285
|
+
### Changed
|
|
286
|
+
|
|
287
|
+
- **Breaking:** requires the MCP SDK 2.x (`mcp>=2.1.1,<3`). Environments pinned
|
|
288
|
+
to `mcp` 1.x can no longer install this package, because 2.x removed
|
|
289
|
+
`mcp.server.fastmcp` and renamed every model field from camelCase to
|
|
290
|
+
snake_case. The MCP *wire* format is unchanged, so a client that only speaks
|
|
291
|
+
the protocol needs no change; that is not the same as no change for anything
|
|
292
|
+
written against the Python SDK, caching responses, or reading exception text.
|
|
293
|
+
- An unexpected exception raised by a toolgraph tool body no longer puts its
|
|
294
|
+
message on the wire. Only errors raised deliberately as `ContractError` (for
|
|
295
|
+
example an unknown selector profile) are forwarded; the rest keep the SDK's
|
|
296
|
+
generic text, so an internal failure cannot disclose filesystem paths, query
|
|
297
|
+
bodies or credential-shaped values. Argument-validation failures still echo
|
|
298
|
+
the caller's own input, as the MCP SDK intends.
|
|
299
|
+
- **Breaking:** an MCP response whose read could not be bracketed by a stable
|
|
300
|
+
graph state now carries `graph_generation: null` and
|
|
301
|
+
`graph_state_verified: false` instead of the generation read afterwards.
|
|
302
|
+
Labelling such a result with a generation it was never read at let a verdict
|
|
303
|
+
from before a permission change be cached under the generation that changed
|
|
304
|
+
it, while the tool documentation tells callers to cache per generation.
|
|
305
|
+
Successful reads gain `graph_state_verified: true`, so the absence of a
|
|
306
|
+
guarantee is never inferred from a missing field. Null is not self-enforcing
|
|
307
|
+
— `None` is a usable cache key — so the obligation to skip the cache and to
|
|
308
|
+
attribute nothing to a graph state is the consumer's; ADR-0004 and the README
|
|
309
|
+
state it, and a consumer that turns eligibility into an authorization
|
|
310
|
+
decision should reject or retry such a response.
|
|
311
|
+
|
|
312
|
+
## 0.0.1 - 2026-09-09
|
|
313
|
+
|
|
314
|
+
Initial public alpha release.
|
|
315
|
+
|
|
316
|
+
- Crawl MCP servers into a graph and combine factual capability data with
|
|
317
|
+
operator-authored governance.
|
|
318
|
+
- Explain access, reachability, drift, blast radius, and tool selection.
|
|
319
|
+
- Compile deterministic policy bundles for an independent runtime gateway.
|
|
320
|
+
- Support local Ladybug and shared Neo4j backends.
|
|
321
|
+
- Reject unknown manifest fields and duplicate YAML keys before graph mutation.
|
|
322
|
+
- Persist only credential-free endpoint labels and redact failure diagnostics.
|
|
323
|
+
- Ship an install-only, offline quickstart through `toolgraph example init`.
|
|
324
|
+
- Give MCP consumers a typed `backend_unavailable` error envelope for transient
|
|
325
|
+
graph outages while keeping contract and internal errors fail-loud. Outages
|
|
326
|
+
are typed at the backend seam, and `retryable` follows the tool's advertised
|
|
327
|
+
read-only annotation instead of being assumed, failing closed to `false`
|
|
328
|
+
whenever that annotation cannot be read.
|
|
329
|
+
- Add a body-free `toolgraph.control-plan` v1 contract and advisory
|
|
330
|
+
`control-preflight` command that lints bounded DAG topology and evaluates all
|
|
331
|
+
principal/tool surfaces under one collision-safe graph-state bracket.
|
|
332
|
+
|
|
333
|
+
### Stabilization (2026-09-08)
|
|
334
|
+
|
|
335
|
+
- Resolve ingest references in bounded batches shared with the selector,
|
|
336
|
+
preserving atomic application, diagnostic order and ambiguous identities.
|
|
337
|
+
- Require explicit commit SHAs for ecosystem smokes and clean matching HEADs
|
|
338
|
+
for gateway smokes; record dependencies and artifact evidence.
|
|
339
|
+
- Include Ladybug in `make install` and refresh the pip audit dependency lock.
|
|
340
|
+
- Reconcile historical integration and selection roadmaps with shipped code.
|
|
341
|
+
|
|
342
|
+
### Implementation review fixes (2026-09-07)
|
|
343
|
+
|
|
344
|
+
- Handle empty crawl and manifest collections on both backends while retaining
|
|
345
|
+
stale-edge reconciliation, revocation, and transactional generation updates.
|
|
346
|
+
- Preserve opaque artifact identities, including ordinary URI queries/fragments.
|
|
347
|
+
Reject credential-shaped identities and redact only evidence fields, including
|
|
348
|
+
policy review paths. Existing v1 schemas and decision vocabularies are unchanged.
|
|
349
|
+
- Use collision-safe `v2:` exception keys and deterministic last-entry-wins
|
|
350
|
+
metadata for duplicate logical exceptions. No physical schema migration is
|
|
351
|
+
required: existing keys remain readable. Re-ingest the original governance
|
|
352
|
+
manifest to rebuild keys and restore entries lost to old key collisions, then
|
|
353
|
+
recompile policy bundles. Old artifacts cannot reconstruct lost manifest rows.
|
|
354
|
+
- Require control-plan fields exactly as published: missing `maximum_cycles`,
|
|
355
|
+
explicit null bounds, string/boolean coercions are rejected. JSON integer
|
|
356
|
+
numbers such as `2.0` remain accepted; additive same-major fields remain valid.
|
|
357
|
+
- Compile catalogs in chunks of at most 4,096 candidates under one strict state
|
|
358
|
+
bracket; reject incomplete or conflicting evaluations. Injected readers own
|
|
359
|
+
all state reads, and adapters without the optional `evaluate` capability work.
|
|
360
|
+
- Load runtime settings lazily so help/version and explicit configuration remain
|
|
361
|
+
usable when the default config is broken. Invalid selected configs fail clearly.
|
|
362
|
+
- Reject naive and sub-minute timestamps before preflight/review I/O; development
|
|
363
|
+
validation now installs and checks the RFC 3339 format validator.
|
|
364
|
+
|
|
365
|
+
### Upgrade note for pre-release users (historical)
|
|
366
|
+
|
|
367
|
+
`driver.session()` and `driver.verify_connectivity()` now raise
|
|
368
|
+
`BackendUnavailableError` where they previously surfaced the raw `neo4j`
|
|
369
|
+
`ServiceUnavailable`, `SessionExpired`, `ConnectionAcquisitionTimeoutError`, or
|
|
370
|
+
`DatabaseUnavailable`. Code embedding toolgraph as a library and catching those
|
|
371
|
+
types directly must catch `BackendUnavailableError` (or its `BackendLockedError`
|
|
372
|
+
subclass); the original exception is preserved as `__cause__`. Configuration,
|
|
373
|
+
authentication, and Cypher/client errors are unaffected, as is `get_driver()`.
|
|
374
|
+
Successful CLI output and query result shapes are unchanged; an unhandled
|
|
375
|
+
outage now names `BackendUnavailableError` in the traceback.
|
|
376
|
+
|
|
377
|
+
`driver.is_backend_unavailable()` narrowed to match: it now recognizes only
|
|
378
|
+
`BackendUnavailableError` (anywhere in the cause chain), not the raw `neo4j`
|
|
379
|
+
types it previously classified. Code that pairs the still-raw `get_driver()`
|
|
380
|
+
escape hatch with `is_backend_unavailable()` to decide whether to retry will
|
|
381
|
+
silently stop matching — catch `BackendUnavailableError`, or route the call
|
|
382
|
+
through `driver.session()` so the seam types it.
|
|
383
|
+
|
|
384
|
+
Earlier snapshots could persist stdio arguments or complete HTTP URLs in graph
|
|
385
|
+
endpoint/evidence properties. Reset and rebuild pre-release graphs after
|
|
386
|
+
upgrading. If any argument or URL contained a credential, rotate it as well.
|