@meada/meada 0.0.0-stage → 1.0.0
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/.gitignore +2 -0
- package/CHANGELOG.md +709 -0
- package/LICENSE +202 -0
- package/README.md +191 -2
- package/binary-install.js +348 -0
- package/binary.js +124 -0
- package/install.js +4 -0
- package/npm-shrinkwrap.json +52 -0
- package/package.json +94 -3
- package/run-meada.js +4 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Meada are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project follows
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [1.0.0] - 2026-10-30
|
|
10
|
+
|
|
11
|
+
The tree declares `1.0.0`: this release raises the workspace, the `meada-core` pin, the Dockerfile,
|
|
12
|
+
the compose defaults and the Helm chart from `0.1.0`, and the `v1.0.0` tag follows its merge on
|
|
13
|
+
main. Publication is still ahead and belongs to the maintainer under
|
|
14
|
+
[#154](https://github.com/ClaudioMascaro/meada/issues/154): `cargo publish` of the crates (core
|
|
15
|
+
first), approval of the staged npm package with 2FA, and publication of the GitHub draft release,
|
|
16
|
+
which triggers the OCI image and Helm chart workflow.
|
|
17
|
+
Release notes and the scope evidence checklist are available in
|
|
18
|
+
[pt-BR](https://github.com/ClaudioMascaro/meada/blob/main/docs/releases/1.0.0.md),
|
|
19
|
+
[English](https://github.com/ClaudioMascaro/meada/blob/main/docs/releases/1.0.0.en.md) and
|
|
20
|
+
[Spanish](https://github.com/ClaudioMascaro/meada/blob/main/docs/releases/1.0.0.es.md).
|
|
21
|
+
The [1.x stability promise](https://github.com/ClaudioMascaro/meada/blob/main/docs/site/en/stability.md)
|
|
22
|
+
starts at publication. The [#32 tracking checklist](https://github.com/ClaudioMascaro/meada/issues/32)
|
|
23
|
+
still needs the post-publication evidence.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Public fixture benchmark (issue #149): release-profile index/reindex timings, root and
|
|
28
|
+
workspace index sizes, and default-answer/tool-list token estimates in every PR/main CI
|
|
29
|
+
summary and retained JSON/Markdown artifact. Timings are informational.
|
|
30
|
+
|
|
31
|
+
- The English user guide and README (#150, spec #32): `docs/user-guide.en.md` translates both
|
|
32
|
+
halves of the pt-BR guide section by section (sections 1 to 11) with the same neutral examples
|
|
33
|
+
and links the English stability page; `README.en.md` now links it instead of the pt-BR guide.
|
|
34
|
+
No code, default dependency or golden changed.
|
|
35
|
+
- The Spanish user guide (#151, spec #32): `docs/user-guide.es.md`, sections 1 to 11 of the pt-BR
|
|
36
|
+
guide with the same structure and neutral names, linking the Spanish stability page. The Spanish
|
|
37
|
+
README links it instead of the Portuguese guide, and the three READMEs link the stability page
|
|
38
|
+
from the Status section. No code, default dependency or golden changed.
|
|
39
|
+
- The output grammar reference (#145, spec R1): `docs/reference/output-grammar.md`, in English like
|
|
40
|
+
the MCP surface, specifies every line kind of `search`, `context` and `trace`, the link notation
|
|
41
|
+
with its level and reason, the levels in order, the contract identifiers (`http::`, `http::ANY::`
|
|
42
|
+
for a provider of every method, `channel::`, `package::`, `resource::`), the `search` kinds, the
|
|
43
|
+
chain footer, the cut note and the token estimate, and tells readers to skip line kinds and
|
|
44
|
+
fields they do not know. Its tables are the grammar check's input: `tests/it/grammar.rs` matches
|
|
45
|
+
every line of every rendered-answer golden against the line kinds of its tool and fails naming
|
|
46
|
+
the golden and the line, fails on a documented line kind no golden shows, and holds the
|
|
47
|
+
document's levels, entry kinds, open item kinds and `search` kinds to the code's. The stability
|
|
48
|
+
document links it. No output changed; new goldens pin the answers only inline assertions covered (`no symbol
|
|
49
|
+
matches`, `no resource matches`, the server's `index behind the mirror`, and `trace` with no
|
|
50
|
+
start, several symbols, several call sites or a clean working tree).
|
|
51
|
+
- The pt-BR user guide, second half (#147, spec #32), in `docs/user-guide.md` (sections 8 to 11):
|
|
52
|
+
each contract kind (HTTP routes, declared resources and gateway prefixes, OpenAPI and AsyncAPI
|
|
53
|
+
documents, channels with the limit that broker clusters are not modelled, packages), each 1.0
|
|
54
|
+
language pack (TypeScript and JavaScript, Python, Go, Rust) with its frameworks, clients,
|
|
55
|
+
environment reads, manifests, channel clients and known limits (including that Rust calls inside
|
|
56
|
+
macros are not indexed), shape drift, and communities and processes. Java, Kotlin, C# and Swift
|
|
57
|
+
are named only as planned Release 2 work. No code, default dependency or golden changed.
|
|
58
|
+
- The pt-BR user guide, first half (#146, spec #32), in `docs/user-guide.md`: installation through
|
|
59
|
+
every channel (Cargo, prebuilt binary, shell installer, `npx @meada/meada`, container), MCP setup with
|
|
60
|
+
`npx @meada/meada mcp` and the three tools with their parameters, the `meada.toml` reference (every
|
|
61
|
+
table and key), decisions and deciders (protocols, environment variables, what is sent,
|
|
62
|
+
privacy), every CLI command and the export. The READMEs in all three languages gain the
|
|
63
|
+
installation channels, first steps, MCP setup and a 1.0 status section with the stability
|
|
64
|
+
promise. No code, default dependency or golden changed.
|
|
65
|
+
- Surface goldens (#144, spec #32): `tests/golden/mcp.tools.json` holds the MCP tool list (names,
|
|
66
|
+
descriptions and parameter schemas of `search`, `context` and `trace`), checked beside the
|
|
67
|
+
1,000-token limit of the descriptions, and `tests/golden/cli.help-all.en.txt` the English help of
|
|
68
|
+
every command of the default build. A test pins the default token budget of 2,000. Both
|
|
69
|
+
goldens change only with `MEADA_BLESS=1`.
|
|
70
|
+
- The stability document (#148, spec #32), `docs/site/{pt,en,es}/stability.md`: the surface 1.x
|
|
71
|
+
freezes (the MCP tools, the output grammar, CLI commands and flags, the `meada.toml`,
|
|
72
|
+
`meada.decisions.toml` and `meada.workspace.toml` schemas, the export), what is breaking, what is
|
|
73
|
+
minor, what is not frozen, and the review gate. The READMEs and the site home pages link it.
|
|
74
|
+
- `search`, `context` and `trace` read from each index its contract rows and only the symbols,
|
|
75
|
+
calls and communities of their answer (#396, **Partial export**), instead of every row of every
|
|
76
|
+
repository. Answers are the same bytes; on junit5 a `search` or `context` peaks at about 40 MB
|
|
77
|
+
instead of 170 to 190 MB and answers in about 0.3 s instead of 1 to 1.4 s. The index gains
|
|
78
|
+
seven lookup indexes (about 40% larger on junit5) and its schema version changes, so existing
|
|
79
|
+
indexes are rebuilt. `meada export`, `status` and the decision report still read every row.
|
|
80
|
+
- A Meada skill for agents, `skills/meada/SKILL.md`: when to reach for `search`, `context` and
|
|
81
|
+
`trace`, how to read each level, and that a missing caller is unproven. The READMEs and the
|
|
82
|
+
site's MCP page show how to install it.
|
|
83
|
+
- Per-channel install and MCP setup: the READMEs open with one row per channel (npx, Cargo, shell
|
|
84
|
+
installer, archive, Docker) with its install line and MCP server command, and gain a Docker MCP
|
|
85
|
+
line that mounts the folder holding the repository at the same path and runs without a network.
|
|
86
|
+
`scripts/container-smoke` runs that line as a stdio client.
|
|
87
|
+
- The site's Deploy page (`docs/site/{pt,en,es}/deploy.md`) covers Docker, Compose and Helm, and
|
|
88
|
+
the chart gains `values-minimal.yaml`, which `scripts/helm-check` renders.
|
|
89
|
+
|
|
90
|
+
### Changed
|
|
91
|
+
|
|
92
|
+
- For `meada-core` crate users: the export's structs (`Export` and each of its pieces, with
|
|
93
|
+
`Shape` and `Drift`) are `#[non_exhaustive]`, so a field added in 1.x is a minor change. Build
|
|
94
|
+
one with `T::default()` and named field assignments, or, for a struct with a field that has no
|
|
95
|
+
default (`Document`, `Import`, `Link`, `Process`, `Step`, `Stale`), with `T::new` taking every
|
|
96
|
+
field in declaration order (`Shape::new` takes the source, the rest set after it). A struct
|
|
97
|
+
literal or an exhaustive pattern of these types outside `meada-core` no longer compiles. No
|
|
98
|
+
output, golden or exported byte changed.
|
|
99
|
+
- The stability promise (spec #32) lets 1.x grow the MCP surface. In
|
|
100
|
+
`docs/site/{pt,en,es}/stability.md` and the three READMEs, these are minor changes: adding an MCP
|
|
101
|
+
tool while the tool list stays within its 1,000-token budget, adding an accepted value to an
|
|
102
|
+
existing parameter (a new value of `changes`) and adding a field at the end of an existing line
|
|
103
|
+
kind. Removing or renaming a tool or parameter and changing what an existing one means stay
|
|
104
|
+
breaking. ADR 0008 is amended: three tools is where the surface started, not a ceiling.
|
|
105
|
+
- `SECURITY` (pt-BR, English and Spanish): the latest published 1.x version receives security
|
|
106
|
+
fixes, which ship as a patch or minor release.
|
|
107
|
+
|
|
108
|
+
### Fixed
|
|
109
|
+
|
|
110
|
+
- Console GitHub grant (#496): a session's GitHub grant and its state (`unread`, `current`,
|
|
111
|
+
`expired`) are read from the same permissions set at the same instant, so a session never shows
|
|
112
|
+
`expired` with a grant read while the set was fresh.
|
|
113
|
+
- Release preparation keeps the GitHub release in draft state for the maintainer to publish.
|
|
114
|
+
A pinned cargo-dist regeneration helper compares the entire workflow after the explicit
|
|
115
|
+
draft policy; OCI publication follows the human `release: published` event.
|
|
116
|
+
|
|
117
|
+
### Prepared
|
|
118
|
+
|
|
119
|
+
- The v1 scope: HTTP, declared resources and gateway paths, OpenAPI/AsyncAPI, channels and
|
|
120
|
+
packages; levels and cross-repository chains, recorded decisions with optional deciders,
|
|
121
|
+
shape drift, TypeScript/JavaScript, Python, Go, Rust, communities and processes.
|
|
122
|
+
- The R1 stability promise, normative MCP output grammar, surface goldens, complete user
|
|
123
|
+
references and public-fixture CI benchmark. Fixture timings are informational.
|
|
124
|
+
- Configured Cargo, binary, shell, npm and OCI distribution. Configuration is not evidence
|
|
125
|
+
of actual publication or a successful published-installer smoke check.
|
|
126
|
+
|
|
127
|
+
### Acceptance
|
|
128
|
+
|
|
129
|
+
The [public #152 receipt](https://github.com/ClaudioMascaro/meada/issues/152#issuecomment-6379316350)
|
|
130
|
+
retains both complete runs on AMD Ryzen 7 5825U, 16 logical CPUs, 7.16 GiB RAM,
|
|
131
|
+
Ubuntu 24.04.5 LTS, x86_64. The measured build was
|
|
132
|
+
`9b99c3a4091b6c1b2c590c43059763f25b6987ef`, `meada 0.1.0`, not every later documentation head.
|
|
133
|
+
|
|
134
|
+
| Metric | Target | First complete run | Complete rerun |
|
|
135
|
+
| --- | --- | --- | --- |
|
|
136
|
+
| Cold configured-workspace index | <= 7 s | 12.680 s, not met | 2.711 s, met |
|
|
137
|
+
| Root index size | <= 37 MB | 21.057536 MB, met | 21.057536 MB, met |
|
|
138
|
+
| Single-file configured-workspace reindex | <= 1 s | 1.346 s, not met | 1.152 s, not met |
|
|
139
|
+
| Compact MCP `tools/list` result | <= 1,000 tokens | 622, met | 622, met |
|
|
140
|
+
| Largest default answer among 11 queries | <= 2,000 tokens | 1,996, met | 1,996, met |
|
|
141
|
+
|
|
142
|
+
MB means 10^6 bytes; tokens are ceil(UTF-8 bytes / 4). Public fixture peers supplement the
|
|
143
|
+
private workload; the receipt does not claim every feature naturally occurs in the private root.
|
|
144
|
+
The [rerun of 2026-10-30](https://github.com/ClaudioMascaro/meada/issues/152#issuecomment-6531862493)
|
|
145
|
+
on main `1d648ad6` uses a reconstructed workspace (not directly comparable) and meets four of the
|
|
146
|
+
five targets: cold index (medians 5.586 to 6.625 s, <= 7 s), root index size (31.883266 MB,
|
|
147
|
+
<= 37 MB), `tools/list` (643 tokens, <= 1,000) and largest default answer (1,999 tokens,
|
|
148
|
+
<= 2,000). The single-file reindex (<= 1 s) met the target in the two quieter runs (0.99 and
|
|
149
|
+
0.931 s) and missed it in the two runs at a load average near 3 (1.068 and 1.123 s). The
|
|
150
|
+
maintainer [waived the reindex target for 1.0.0](https://github.com/ClaudioMascaro/meada/issues/350#issuecomment-6532716065),
|
|
151
|
+
recorded on [#350](https://github.com/ClaudioMascaro/meada/issues/350).
|
|
152
|
+
|
|
153
|
+
Known limit: the single-file reindex sits near 1 s and exceeds it under load. #350 stays open for
|
|
154
|
+
1.x, with the goal of 1 s with headroom under ordinary load; a speedup changes no output, so it
|
|
155
|
+
ships as a patch or minor release. Publication is done separately by the maintainer under
|
|
156
|
+
[#154](https://github.com/ClaudioMascaro/meada/issues/154).
|
|
157
|
+
|
|
158
|
+
## [0.1.0] - 2026-10-30
|
|
159
|
+
|
|
160
|
+
The first release: the trunk of the roadmap (ADR 0003) and every spec merged since, with
|
|
161
|
+
identity automation (ADR 0016, #319) and named workspaces (ADR 0020) landing on the release day.
|
|
162
|
+
The trunk's detailed entries are in [docs/releases/0.1.0.md](https://github.com/ClaudioMascaro/meada/blob/v0.1.0/docs/releases/0.1.0.md);
|
|
163
|
+
the two later specs are listed below in full. Prebuilt binaries cover Linux and macOS on x86_64
|
|
164
|
+
and arm64 and Windows on x86_64; Windows arm64 waits for a toolchain that builds it.
|
|
165
|
+
|
|
166
|
+
### Added
|
|
167
|
+
|
|
168
|
+
- TypeScript and JavaScript indexing with tree-sitter (`.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`,
|
|
169
|
+
`.cjs`): definitions, imports (relative paths, `index` resolution, `tsconfig.json` `paths`)
|
|
170
|
+
and calls, each with a level (`exact`, `inferred` or `ambiguous`). A call through a local
|
|
171
|
+
binding never resolves `exact` to the definition it hides.
|
|
172
|
+
- Python language pack (spec #24): definitions, imports (relative, packages, `import *` with
|
|
173
|
+
`__all__`, source roots from `pyproject.toml`) and calls, with receivers resolved through
|
|
174
|
+
`self`, annotations, constructor calls and the method resolution order. The main guard and
|
|
175
|
+
`[project.scripts]` targets are program entry points.
|
|
176
|
+
- Go language pack (spec #25), with no Go toolchain: functions, methods, structs and interfaces;
|
|
177
|
+
imports through the module path of the owning `go.mod` or a local `replace`; one scope per
|
|
178
|
+
package directory; build constraints for a fixed `linux/amd64` target; method calls through
|
|
179
|
+
the receiver types the source writes; `func main` of each `package main` as an entry point.
|
|
180
|
+
- Rust language pack (spec #30), with no Rust toolchain: crates built from their roots through
|
|
181
|
+
`mod` declarations; `use` declarations with renames, globs and `pub use` re-exports; path and
|
|
182
|
+
method calls resolved through the module tree and written types, a type from outside the
|
|
183
|
+
repository staying opaque; only the default configuration of a crate, for a fixed `x86_64`
|
|
184
|
+
Linux target and its default features; `fn main` of each binary target as an entry point.
|
|
185
|
+
- Java language pack (issues #84, #85, #91), with no JDK or build tool: package-qualified types
|
|
186
|
+
and signatures, overloads narrowed by argument count, package and static imports, calls
|
|
187
|
+
through fields, parameters, locals, return types and supertypes, read per class `family`, and
|
|
188
|
+
`public static void main` as an entry point.
|
|
189
|
+
- Kotlin language pack (issue #95), with no Kotlin compiler: `.kt` and `.kts` files with classes,
|
|
190
|
+
objects, companions, extension functions and properties; single, aliased and star imports;
|
|
191
|
+
calls `exact` when the argument count leaves one function; Kotlin and Java files of one
|
|
192
|
+
repository call each other.
|
|
193
|
+
- C# language pack (issue #107), with no .NET SDK or MSBuild: `.cs` files grouped into projects
|
|
194
|
+
by SDK-style `.csproj` files, namespace-qualified and `partial` types, `using` in all its forms
|
|
195
|
+
(`global`, `static`, aliases, a project's `<Using>` items and implicit usings), what referenced
|
|
196
|
+
projects pass on and the `Directory.Build.props` files MSBuild imports.
|
|
197
|
+
- Swift language pack (spec #29, #117), with no Swift toolchain or Xcode: each SwiftPM target is
|
|
198
|
+
a module; symbols named with their argument labels and extension members under the extended
|
|
199
|
+
type; calls narrowed by labels, `exact` on a receiver of written type; a `@main` type and a
|
|
200
|
+
`main.swift` start a process.
|
|
201
|
+
- HTTP contracts across repositories. TypeScript `fetch` consumers (client wrappers and base URLs
|
|
202
|
+
from the environment included) link to Next.js app router and Express providers; every link
|
|
203
|
+
carries a level and a reason, and a call whose URL cannot be read is an `unresolved consumer`,
|
|
204
|
+
never linked.
|
|
205
|
+
- HTTP providers in every other pack: FastAPI, Flask and Django URLconfs; `net/http`, gin, echo,
|
|
206
|
+
chi and gorilla/mux; axum, actix-web and Rocket; Spring MVC and JAX-RS; Ktor; ASP.NET Core
|
|
207
|
+
controllers and minimal APIs; Vapor and Hummingbird. Routers and groups are followed through
|
|
208
|
+
assignments, arguments, return values and imports, so a route is served under every prefix
|
|
209
|
+
around it; a prefix or mount Meada cannot read serves nothing.
|
|
210
|
+
- HTTP consumers in every other pack: `requests`, `httpx` and `aiohttp`; `net/http` and resty;
|
|
211
|
+
reqwest and ureq; RestTemplate, WebClient, `HttpRequest`, OkHttp and the `declarative client`s
|
|
212
|
+
OpenFeign and Retrofit (Java and Kotlin); the Ktor client; `HttpClient`, typed clients and
|
|
213
|
+
Refit; `URLSession`, Alamofire and AsyncHTTPClient. URLs fold from literals, constants,
|
|
214
|
+
concatenation and format strings, and a `client wrapper` is read at each of its call sites.
|
|
215
|
+
- Base URLs from the environment and configuration in every pack (`os.Getenv`, `std::env::var`,
|
|
216
|
+
`System.getenv`, `Environment.GetEnvironmentVariable`, `ProcessInfo`), Spring `@Value` with
|
|
217
|
+
`application.properties` and `application.yml`, and C# configuration with `appsettings.json`,
|
|
218
|
+
mapped to a `peer` through `base_url_env`. Meada never reads the value. Cross-language fixtures
|
|
219
|
+
link each language with TypeScript.
|
|
220
|
+
- Channels in Python, Go, Rust, Java, Kotlin and C#: Kafka, RabbitMQ, Amazon SQS, NATS, Redis
|
|
221
|
+
streams, JMS and Azure Service Bus clients are a `producer` or a consumer of a `channel`, linked
|
|
222
|
+
to the same channel in other languages, with the new broker kinds `jms` and `servicebus`. A
|
|
223
|
+
call counts only when its client traces back to the library; a name Meada cannot evaluate is
|
|
224
|
+
an `unresolved side`.
|
|
225
|
+
- Packages as contracts across seven `package ecosystem`s: npm (spec #22), PyPI, Go (spec #25),
|
|
226
|
+
Cargo (spec #30), Maven and Gradle (spec #26), NuGet (spec #28) and SwiftPM (spec #29).
|
|
227
|
+
A dependency on a workspace package links to it with its range flag, an import of the package
|
|
228
|
+
crosses into the provider's public declarations, `search` takes the kind `package`, and
|
|
229
|
+
`context` on an exported declaration lists its consumers in other repositories.
|
|
230
|
+
- Shape drift (spec #23, ADR 0009): each field a TypeScript or JavaScript consumer reads from a
|
|
231
|
+
response that the linked route does not return. Provider shapes come from an OpenAPI
|
|
232
|
+
`provider document`, a zod schema or the returned object literals, also through packages
|
|
233
|
+
(issue #175); consumer shapes from reads, casts and type annotations. `trace`, `context`,
|
|
234
|
+
`search` (kind `drift`), `meada status` and `meada export` show it.
|
|
235
|
+
- Communities and processes (spec #31): groups of symbols that call each other, found with a
|
|
236
|
+
deterministic Leiden and named after their most common directory, and `entry point`s (`route`,
|
|
237
|
+
`channel`, `export`, `program`) whose call tree `trace` renders from `process::<identity>`
|
|
238
|
+
across repositories. `search` takes the kinds `community` and `process`, and `context` adds
|
|
239
|
+
a symbol's community and the processes it belongs to.
|
|
240
|
+
- The query surface: an MCP server over stdio with three read-only tools, `search`, `context` and
|
|
241
|
+
`trace`, answering compact English lines under a token budget; `context` names the span's
|
|
242
|
+
range on its first line and cuts the span text first. `search` with kind `duplicate` lists the
|
|
243
|
+
symbols whose text is the same in two or more places of the workspace.
|
|
244
|
+
- The `meada` command (`index`, `status`, `export` with byte-reproducible JSON, `mcp`), messages
|
|
245
|
+
in Portuguese or English after `LANG`, `meada.toml` with `[[peer]]` and `[serve]`, workspace
|
|
246
|
+
discovery through the `origin` remote, and a per-repository SQLite index in `.meada/`
|
|
247
|
+
refreshed on every query from the files changed on disk. `meada status` points to the new
|
|
248
|
+
`inference` issue form when a repository holds a shape drift or an `inferred` link.
|
|
249
|
+
- Server mode (spec #259, ADR 0011): `meada mcp --listen` serves the three tools over MCP
|
|
250
|
+
streamable HTTP, one endpoint per repository, from the mirrors a `workspace manifest` lists;
|
|
251
|
+
indexes are refreshed by command, never by the server, and a reverse proxy admits the clients.
|
|
252
|
+
- Cloud modules (ADR 0012), cargo features off by default and all enabled by `cloud`: the shared
|
|
253
|
+
Valkey connection; the `snapshot store`, where `meada snapshot publish` uploads a `generation`
|
|
254
|
+
with BLAKE3-checked bundles and `meada snapshot pull` extracts it as hostile input; serving
|
|
255
|
+
generations with `--generations`, pinned at `/<identity>@<generation>`; the generation pointer
|
|
256
|
+
and retained generations in Valkey; and the `refresh lock` with its `fence`, never swapping a
|
|
257
|
+
`node` to a lower one.
|
|
258
|
+
- Operations on generations (ADR 0014): `meada snapshot pointer set`, `retain` and `drop` and
|
|
259
|
+
`meada snapshot fence recover` (#299, #300), a named lock holder, and the `heartbeat` each
|
|
260
|
+
`node` writes to Valkey after every pull round.
|
|
261
|
+
- OIDC admission (ADR 0012, feature `oidc`): the server becomes an OAuth 2.1 resource server
|
|
262
|
+
that checks a bearer JWT against the issuer's JWKS and bounds every answer to the identities
|
|
263
|
+
its claims, or its groups through `[oidc.groups]`, grant (#290, #291).
|
|
264
|
+
- GitHub App credentials (ADR 0013, feature `github-app`): `meada github-app credential` is git's
|
|
265
|
+
credential helper for the indexing job, minting installation tokens cached in Valkey and
|
|
266
|
+
skipping throttled installations.
|
|
267
|
+
- Workspace versions (spec #293, ADR 0014): with `[workspace] source = "store"`, the
|
|
268
|
+
`[[repository]]` entries live as immutable documents in the snapshot store, managed with
|
|
269
|
+
`meada workspace import`, `list`, `show`, `apply` (under the refresh lock, rolling back by
|
|
270
|
+
applying an older one) and `pull`.
|
|
271
|
+
- The management `console` (spec #293, ADR 0014, feature `ui`): `meada ui --listen` serves a
|
|
272
|
+
static Vite, React and TypeScript front end and a JSON API, with OIDC sign-in by authorization
|
|
273
|
+
code with PKCE S256, `viewer` and `operator` roles, operational pages for fleet, store,
|
|
274
|
+
repositories, GitHub App and admission, a query playground, code graph views, and operator
|
|
275
|
+
writes with one audit line each, in Brazilian Portuguese and English. The workspace editor
|
|
276
|
+
and version history (issue #310) validate entries and diff versions in the browser through
|
|
277
|
+
the shared WebAssembly core.
|
|
278
|
+
- Team ownership (ADR 0015): a workspace entry may name its `owner` team, and a `team operator`
|
|
279
|
+
may save and apply changes to its teams' entries alone; any other change is refused naming
|
|
280
|
+
each entry and its reason. The editor marks refused changes as they are typed, and the
|
|
281
|
+
console documentation gains a Teams section. The whole is additive, a minor change.
|
|
282
|
+
- Distribution: a cargo-dist release workflow building binaries for Linux, macOS and Windows on
|
|
283
|
+
x86_64 and arm64 with a shell installer, and an npm package `@meada/meada` (spec #32) staged
|
|
284
|
+
with each release for `npx @meada/meada`; multi-arch OCI images, default and `-cloud` (issue
|
|
285
|
+
#279), with the console's static files in the cloud image (issue #313); a Helm chart (#281);
|
|
286
|
+
Docker Compose examples (#280); and opt-in console profiles for both (issue #314).
|
|
287
|
+
- Documentation and community: README, CONTRIBUTING, code of conduct, security policy and issue
|
|
288
|
+
and PR templates in Portuguese, English and Spanish; a CI workflow; a multilingual
|
|
289
|
+
documentation site (issue #320); ADR 0020, recording the direction of spec S5.
|
|
290
|
+
- Every language pack and every manifest and configuration reader has a hostile-input probe
|
|
291
|
+
held to a time and size budget.
|
|
292
|
+
- Validation and operation of the named-workspace flow (#347, spec S5): a failure test that forces
|
|
293
|
+
the role check, the fetch/index and the local publication to fail in turn and shows the current
|
|
294
|
+
generation stays queryable, eligibility stays fail closed and a revoked grant never revives
|
|
295
|
+
(only a later complete permissions set restores it); the upgrade procedure (migrate to schema 8,
|
|
296
|
+
upgrade every binary before the first refresh that records a context, then configure), the
|
|
297
|
+
failure table and the acceptance map on the named-workspaces page in Brazilian Portuguese,
|
|
298
|
+
English and Spanish; `[catalog]` and `[workspaces]` in the Compose bootstrap examples; and the
|
|
299
|
+
Helm values `modules.workspaces` with `values-workspaces.yaml`, which put those sections (and
|
|
300
|
+
the `[ui]` block the job needs) in the console's and the job's bootstrap and never a node's.
|
|
301
|
+
The measurement against a real GitHub organization (#338) stays open and does not gate S5. No
|
|
302
|
+
code, default dependency or golden changed.
|
|
303
|
+
- The proposal server (#346, ADR 0022, feature `proposals`, enabled by `cloud`): `meada workspaces
|
|
304
|
+
mcp --listen <addr> --workspace <bootstrap>` with `[proposals]` (`public_url`, `audience`)
|
|
305
|
+
serves CLI agents four read-only MCP tools over the console's own named-workspace functions:
|
|
306
|
+
`catalog` (the repositories the caller may select), `workspaces` and `show` (the workspaces it
|
|
307
|
+
may see, with their `seq`) and `propose`, which validates manifest text as a new workspace or a
|
|
308
|
+
change and returns the canonical manifest, the diff against the base, diagnostics, each named
|
|
309
|
+
identity's eligibility, the refusals the console would give, the effective configuration,
|
|
310
|
+
`applicable` and the base, storing nothing. Identity, login, grant and role come from a bearer
|
|
311
|
+
token for `[proposals] audience`, which no node or console accepts and which accepts no
|
|
312
|
+
node's or console's token; no tool takes an argument naming an actor, a role or a grant, and
|
|
313
|
+
repository hints from an agent's own GitHub read MCP change no result. Every Postgres session
|
|
314
|
+
of the server is read-only, it calls no GitHub, node or identity provider API and writes no
|
|
315
|
+
audit record, store object or Valkey key. Inputs and answers are bounded (a body of 80 KiB, a
|
|
316
|
+
manifest of 64 KiB, an answer of 192 KiB, 32 calls in flight). The node's MCP server, the
|
|
317
|
+
console and the default build are unchanged.
|
|
318
|
+
- Guided authoring of named workspaces (#345, ADR 0021 "As built (#345)", feature `workspaces`):
|
|
319
|
+
the console edits the manifest of a workspace the subject operates through form controls
|
|
320
|
+
(repositories from the catalog, serve settings, resources, peers and environment-variable
|
|
321
|
+
names) that are one document with the manifest text, both going through `meada-core`'s
|
|
322
|
+
WebAssembly. It shows each entry's eligibility, ingestion and effective configuration in text,
|
|
323
|
+
the diff against the stored version, and freezes the reviewed payload until the person
|
|
324
|
+
confirms; server refusals (`entries_refused`, `stale_base`, a lost operator role) are shown as
|
|
325
|
+
worded and the catalog is read again. `namedDiff` and `namedChanged` join the WebAssembly
|
|
326
|
+
exports. Viewers get no mutation control. No server route changes; nothing changes without
|
|
327
|
+
`[workspaces]`.
|
|
328
|
+
- Context-specific configuration of named workspaces (#344, ADR 0021 contract 6, feature
|
|
329
|
+
`workspaces`): `GET /api/v1/workspaces/<id>/effective[?version=]` shows, per entry the caller
|
|
330
|
+
reads, where its configuration comes from (the repository's own `meada.toml` wins whole, else
|
|
331
|
+
the workspace's `serve`, `resource` and `peer` blocks, else nothing), the blocks that are
|
|
332
|
+
ignored, whether the repository is indexed, pending ingestion or excluded, and diagnostics
|
|
333
|
+
(`ignored_by_repository`, `unknown_until_ingested`, `excluded_no_sponsor`, `peer_missing`,
|
|
334
|
+
`documents_not_indexed`). `POST /api/v1/workspaces/effective` shows the same view for manifest
|
|
335
|
+
text before it is saved and stores nothing; creation, save and apply answers carry the same
|
|
336
|
+
`diagnostics`, so an ignored declaration never looks plainly applied. Two workspaces that
|
|
337
|
+
share a repository keep their own peers, resources and serve hosts: the configuration is applied
|
|
338
|
+
when answering and never enters a bundle, so a change of it re-indexes nothing and a pinned
|
|
339
|
+
generation keeps what it recorded. The decision is `meada_core::effective`, also exported to
|
|
340
|
+
WebAssembly (`namedEffective`). The console's workspace page shows the view in text and has a
|
|
341
|
+
text editor for the manifest of a workspace the subject operates. Nothing changes without
|
|
342
|
+
`[workspaces]`.
|
|
343
|
+
- The version lifecycle of named workspaces (#343, ADR 0021 contract 4, feature `workspaces`):
|
|
344
|
+
`POST /api/v1/workspaces/<id>/versions` saves a version without moving anything (an operator
|
|
345
|
+
submits the entries it reads and the version stored keeps the ones it cannot read; the
|
|
346
|
+
platform administrator submits the whole manifest), `.../apply` applies a stored version (a
|
|
347
|
+
version that was current before is a rollback), `.../rename`, `.../delete` (versions and
|
|
348
|
+
history are kept) and `.../operators` (the platform administrator delegates; a workspace keeps
|
|
349
|
+
at least one operator). Every write carries the `base` it read: a stale base, a revoked role, a
|
|
350
|
+
refused entry or a version of another workspace is refused and changes nothing, and the
|
|
351
|
+
transaction rechecks authority, base and the eligibility of what the operation adds or changes
|
|
352
|
+
against the newest permissions set. Only an operator of the workspace or the platform
|
|
353
|
+
administrator writes; a viewer is a 403 and a caller who cannot see the workspace gets the 404
|
|
354
|
+
of an unknown id. `GET .../versions`, `.../versions/<version>?against=` (with a diff) and
|
|
355
|
+
`.../history` read them under the same discovery rule. Each operation is one audit record
|
|
356
|
+
(`workspaces.save`, `.apply`, `.rollback`, `.rename`, `.delete`, `.operators`) with the
|
|
357
|
+
workspace and base. `meada workspaces list|show|versions|diff|create|save|apply|rename|delete|operators` are
|
|
358
|
+
the command-line twins (the platform administrator, `cli` audit records). Migration 8 adds
|
|
359
|
+
`workspace_versions.entries`: run `meada postgres migrate` before upgrading the binary. Without
|
|
360
|
+
`[workspaces]` nothing changes.
|
|
361
|
+
- The first named workspace of spec S5 (#342, ADR 0021 contracts 2 to 5, cargo feature
|
|
362
|
+
`workspaces`): `[workspaces]` (`mirrors`, an absolute root; `max_workspaces`, default 200, 1 to
|
|
363
|
+
2000; `max_entries`, default 100, 1 to 500) needs `[ui]` with `operations = true`,
|
|
364
|
+
`[oidc.github]`, `[catalog]` and `[workspace] source = "store"`. An admitted subject whose
|
|
365
|
+
GitHub login holds an accepted role on every selected repository, directly or through a group,
|
|
366
|
+
creates a workspace with `POST /api/v1/workspaces`; `GET /api/v1/workspaces` and
|
|
367
|
+
`GET /api/v1/workspaces/<id>` list and show it to whoever's own grant reads every one of its
|
|
368
|
+
repositories (anyone else gets the one 404 of an unknown id), with `state` (`applied` or
|
|
369
|
+
`live`) and each entry's `ingestion` (`pending`, `ready` or `excluded`), never ready before a
|
|
370
|
+
generation records it. The creator operates it; group peers only view. The
|
|
371
|
+
decision is taken in one Postgres transaction against the newest permissions set (migration 7:
|
|
372
|
+
`workspaces`, `workspace_operators`, `workspace_versions`, `workspace_history` and a `context`
|
|
373
|
+
column on `permission_exclusions`; run `meada postgres migrate`); the version document is the
|
|
374
|
+
shared store's `workspaces/<version id>.json`. `meada workspace pull` also writes the contexts
|
|
375
|
+
file, `meada github-app roles` applies the sponsor rule (an entry no operator of its workspace
|
|
376
|
+
holds an accepted role on is left out before the fetch, `no_sponsor`, which is not a failed
|
|
377
|
+
fetch), and the repositories only named workspaces select are fetched, indexed and bundled once
|
|
378
|
+
at `<mirrors>/<identity>` (`meada workspace show --pulled` lists them). A generation records them
|
|
379
|
+
as `members` and the named workspaces as `contexts` (ids and digests in the generation id; a
|
|
380
|
+
deployment without named workspaces keeps every id), and a node serves
|
|
381
|
+
`/-/workspaces/<id>/<identity>` only to a token that reads every repository of the context (the
|
|
382
|
+
first segment `-` is reserved); the console's playground gains `workspace` and
|
|
383
|
+
`GET /api/v1/workspaces/<id>/repositories/<identity>/export`. The console has the workspaces
|
|
384
|
+
page (list, detail, creation from the catalog) and WebAssembly exports for the shared
|
|
385
|
+
rules. Save, apply, rename, delete and delegation (#343), per-context configuration (#344),
|
|
386
|
+
guided authoring (#345) and proposals (#346) are not built. Without `[workspaces]` every
|
|
387
|
+
process behaves as before; upgrade every binary, nodes included, before the first refresh that
|
|
388
|
+
records a context.
|
|
389
|
+
- The repository catalog of spec S5 (#341, ADR 0021 contract 1, cargo feature `workspaces`,
|
|
390
|
+
part of `cloud`): `[catalog]` (`allow` patterns on `[oidc.github] host`, `max_repositories`
|
|
391
|
+
1 to 5000 with no default) makes `meada github-app roles` list, besides the workspace's own
|
|
392
|
+
repositories, those the Apps' installations cover (`GET /installation/repositories`, every page,
|
|
393
|
+
only for owners a pattern names) and `allow` admits, into the same permissions set with a
|
|
394
|
+
`source` on each repository (Postgres migration 6; run `meada postgres migrate`). A failed or
|
|
395
|
+
throttled listing, a body that is not the documented object or a candidate set above the cap
|
|
396
|
+
fails the run and writes nothing. The console gains `GET /api/v1/catalog` (bounded, opaque
|
|
397
|
+
pagination, `limit`, `after`, `q`) and `POST /api/v1/catalog/check`, answering only the
|
|
398
|
+
repositories the caller's login holds an accepted role on, direct or group-derived alike, with
|
|
399
|
+
the states `ready` and `pending` (`no_set`, `stale_set`, `missing_login`) and `unavailable`
|
|
400
|
+
without a reason, plus the repository catalog page; App coverage, group names in a token and
|
|
401
|
+
agent metadata alone grant nothing, and discovery creates no workspace, ACL or index. Without
|
|
402
|
+
`[catalog]` every process behaves as before. The cost of expanded coverage against a real
|
|
403
|
+
organization is not measured (#338 does not gate it).
|
|
404
|
+
- Spec S5 consolidated (#340, documentation only; nothing is implemented): ADR 0020 now holds
|
|
405
|
+
the accepted domain decision (platform administrator, workspace operator and inherited viewer,
|
|
406
|
+
creation eligibility from accepted repository roles, the frozen Y/A/B matrix, hidden discovery
|
|
407
|
+
for partial readers, revocation), ADR 0021 the contracts (catalog, selected entries, shared
|
|
408
|
+
ingestion membership, version lifecycle, query selection, effective configuration,
|
|
409
|
+
deterministic ids, migration and the slice graph #341 to #347) and ADR 0022 the authenticated
|
|
410
|
+
stateless proposal server. The glossary gains the terms, ADRs 0002, 0012, 0014, 0015 and 0016
|
|
411
|
+
carry dated amendments, `tests/fixtures/named-workspaces.json` is the Y/A/B table with a
|
|
412
|
+
`meada-core` test that checks it against the rules, and the named workspaces and roadmap pages
|
|
413
|
+
are updated in three languages. No serialized role, route, id, golden or default build changes.
|
|
414
|
+
- Identity automation for enterprise deployments (ADR 0016, spec #319), in the entries below: the
|
|
415
|
+
`postgres` and `directory` features and the `[postgres]`, `[directory]`, `[oidc] type`,
|
|
416
|
+
`[oidc.entra]` and `[oidc.github]` sections; `meada postgres migrate`, `meada github-app roles`
|
|
417
|
+
and `meada workspace show --pulled`; an entry's `added_by` and the `unattributed` and
|
|
418
|
+
`added_by_change` reasons of `entries_refused`; the session's `login`, `github_host` and
|
|
419
|
+
`github_grant`, the token check's `sources`; the audit and `GET /api/v1/audit`; the SCIM endpoint
|
|
420
|
+
under `/scim/v2/`, the `deprovisioned` refusal, the owner picker, `owner_missing` and `excluded`.
|
|
421
|
+
Under Spec R1's stability rule every one of them is additive: a minor change. Without the new
|
|
422
|
+
sections every process behaves as before, and the default build, the CLI help, the MCP surface
|
|
423
|
+
and every export and trace golden are unchanged.
|
|
424
|
+
- The identity automation documentation, in three languages: an enterprise identity guide (the
|
|
425
|
+
data layer, the audit, the provider type, the GitHub grant, additions under the provider, the
|
|
426
|
+
directory, Entra overage and Known ceilings), recipes for Entra ID, Okta and Keycloak, the
|
|
427
|
+
roadmap and diagrams with S4 delivered, and the architecture map's Known ceilings gaining the
|
|
428
|
+
login length and the audit's growth. S5 (#339), named workspaces and authoring through the UI or
|
|
429
|
+
MCP, stays planned.
|
|
430
|
+
- The Compose example gains `compose.postgres.yaml` (a Postgres 15 with TLS and the three
|
|
431
|
+
recommended roles, and a one-shot `meada postgres migrate` step that finishes before the job,
|
|
432
|
+
the console and the nodes), a `fetch-pulled.sh` for the job's refresh under `[oidc.github]`, and
|
|
433
|
+
commented `[postgres]`, `[oidc.github]`, `[directory]` and `[oidc.entra]` examples in the
|
|
434
|
+
bootstraps. The Helm chart gains `modules.postgres`, `modules.githubApp`, `modules.oidc.github`,
|
|
435
|
+
`modules.oidc.type` and `entra`, `modules.directory`, a migrate hook Job with pods that wait for
|
|
436
|
+
the schema, an example Postgres (`postgres.enabled`) and `values-identity.yaml`; the default
|
|
437
|
+
values render nothing of Postgres.
|
|
438
|
+
- `[oidc.github]`, the GitHub grant provider's table (ADR 0016): `host` (default `github.com`, a
|
|
439
|
+
lowercase host name), `login_claim` (required), `roles` (a non-empty array of distinct role
|
|
440
|
+
names of 1 to 100 characters with no control character, compared exactly with GitHub's
|
|
441
|
+
`role_name`; default `["read", "triage", "write", "maintain", "admin"]`) and `max_age_seconds`
|
|
442
|
+
(required, at least 60). It needs `[postgres]` and a build with `postgres`, each error naming
|
|
443
|
+
them; beside it `[oidc]` needs neither `identities_claim` nor `groups_claim`.
|
|
444
|
+
- The GitHub grant at admission (ADR 0016): nodes and the console with `[oidc.github]` keep the
|
|
445
|
+
newest permissions set in memory, read every 60 seconds without admission ever waiting on
|
|
446
|
+
Postgres, and a token reads the union of its claims grant and what its `login_claim` login (1
|
|
447
|
+
to 39 ASCII letters, digits, `-` and `_`, compared in lowercase) holds an accepted role on,
|
|
448
|
+
everywhere the grant is read: a node's admission, routing, answers and 404 lists, and every
|
|
449
|
+
bounded view of the console, the team operator role, the entry authorization and the token
|
|
450
|
+
check. The GitHub part grants for at most `max_age_seconds` past the set's `finished` on
|
|
451
|
+
Postgres's clock, then nothing, the claims grant unaffected; a machine token's `*` never
|
|
452
|
+
depends on Postgres. A node starts and serves without Postgres, and names a schema version it
|
|
453
|
+
does not know once on stderr. Every row read is checked as hostile input.
|
|
454
|
+
- `GET /api/v1/session` gains, with `[oidc.github]`, `login` (when the token has a valid one),
|
|
455
|
+
`github_host` and `github_grant` (`current`, `expired` or `unread`); `grant` is the union.
|
|
456
|
+
`POST /api/v1/admission/check` gains `sources`: `{"claims": "*" | [identities], "github":
|
|
457
|
+
[identities]}`, each sorted, from the console's cache. `GET /api/v1/admission` gains `github`:
|
|
458
|
+
the grant provider's settings, the grant's state and the newest set's id, `finished`, age, `max_age_seconds`,
|
|
459
|
+
repositories, rows and exclusions. The admission and session pages show them, and audit
|
|
460
|
+
records carry the subject's login.
|
|
461
|
+
- Additions under the GitHub grant provider (ADR 0015, ADR 0016): a team operator may add, for
|
|
462
|
+
its team, an entry of a repository on `[oidc.github] host` that its grant does not read yet.
|
|
463
|
+
The console's save sets the addition's `added_by` to the saver's login whatever the submitted
|
|
464
|
+
text says (and clears it on additions off the host), and the role check fetches the entry only
|
|
465
|
+
while that login holds an accepted role. Two refusal reasons join `entries_refused`, in
|
|
466
|
+
precedence order `outside_grant`, `unattributed` (an addition on the host without `added_by`,
|
|
467
|
+
or by a subject without a login), `ownerless`, `owner_change`, `added_by_change` (an edit
|
|
468
|
+
changing, clearing or setting `added_by`) and `not_your_team`. A save that adds an identity
|
|
469
|
+
the workspace already holds outside the grant is refused `outside_grant`, naming it as
|
|
470
|
+
submitted. The global operator's saves and the CLI keep `added_by` as written.
|
|
471
|
+
`authorize_entries` and `compose_entries` of `meada-core`, and their WebAssembly exports, take
|
|
472
|
+
the subject's login and the GitHub grant provider's host (none without the GitHub grant provider, answering as before),
|
|
473
|
+
and the editor marks the new reasons as it marks the others, in pt-BR and English, and shows
|
|
474
|
+
each entry's `added_by`.
|
|
475
|
+
- `excluded`: with `[oidc.github]`, `GET /api/v1/workspace` and `GET
|
|
476
|
+
/api/v1/workspace/versions/<id>` mark `excluded: true` on each entry of the version the newest
|
|
477
|
+
permissions set checked whose addition it left out, for those who see the entry; the field is
|
|
478
|
+
absent otherwise. The versions page and the editor mark those entries with the reason that the
|
|
479
|
+
adder holds no accepted role.
|
|
480
|
+
- `meada github-app roles --workspace <bootstrap manifest>` (features `github-app`, `postgres`
|
|
481
|
+
and `oidc`), the job's role check at every refresh: for each identity of the workspace on
|
|
482
|
+
`[oidc.github] host` it lists `GET /repos/{owner}/{repo}/collaborators?affiliation=all&per_page=100`,
|
|
483
|
+
every page, through the installation ADR 0013's selection picks, and replaces the permissions
|
|
484
|
+
set in Postgres in one transaction (what GitHub showed of each repository, its rows, every
|
|
485
|
+
collaborator listed with its `role_name`, and its exclusions with their reason), refused when a set with a higher `MEADA_REFRESH_FENCE` exists. With
|
|
486
|
+
`source = "store"`, every addition whose `added_by` holds no accepted role is left out:
|
|
487
|
+
written to `<index_dir>/workspace/excluded`, and `meada index`, `status`, `export` and
|
|
488
|
+
`snapshot publish` treat it as absent from the workspace, refusing to run, naming the role
|
|
489
|
+
check, when the file is missing or names another version. A 403 or 429 passes to the next
|
|
490
|
+
installation; every one throttled, a 5xx, a timeout, an answer past the size cap or a
|
|
491
|
+
`Link` header that leaves the repository's listing fails the run, which writes nothing. A
|
|
492
|
+
404 or an owner no installation covers gives a warning and no rows. No App permission beyond
|
|
493
|
+
Metadata (read) is needed. The job's refresh becomes `workspace pull`, `github-app roles`,
|
|
494
|
+
the fetch of what `show --pulled` lists, `index` and `snapshot publish`.
|
|
495
|
+
- `meada workspace show --pulled --workspace <bootstrap manifest>` prints the version `meada
|
|
496
|
+
workspace pull` left on disk in the `[[repository]]` shape, without the entries the role
|
|
497
|
+
check excluded, for the fetch script; with a version argument it is a usage error.
|
|
498
|
+
|
|
499
|
+
- `added_by`, an optional key of a workspace version's `[[repository]]` entries beside `repo`,
|
|
500
|
+
`path` and `owner` (ADR 0016): the GitHub login that added the entry, 1 to 39 characters of
|
|
501
|
+
lowercase ASCII letters, digits, `-` and `_`. It is imported, stored, shown (`meada workspace
|
|
502
|
+
show` prints it right after `owner`) and pulled like `owner`, joins the version id and no
|
|
503
|
+
generation or bundle id, and nothing reads it to decide anything yet. An entry without it keeps
|
|
504
|
+
its version id. A bad value is an error naming the file, the line and the key, in a
|
|
505
|
+
bootstrap manifest's own `[[repository]]` entries (`source = "file"`) the key is an error
|
|
506
|
+
naming `source = "store"`, and a repository's `meada.toml` still rejects it as unknown.
|
|
507
|
+
- `[oidc] type` names the identity provider that issues the deployment's tokens (ADR 0016):
|
|
508
|
+
`generic` (the default), `entra`, `okta` or `keycloak`. None changes the admission, the grant or
|
|
509
|
+
any answer, and a bootstrap manifest without the key works as before; the console's `GET
|
|
510
|
+
/api/v1/admission` gains `"type"`, which the admission page shows beside the issuer. Any other
|
|
511
|
+
value, or one that is not a string, is an error naming the file, the line, the key and the four
|
|
512
|
+
values.
|
|
513
|
+
- `[oidc.entra] overage = "directory"` (ADR 0016): a Microsoft Entra ID token in more groups than
|
|
514
|
+
it can carry (`_claim_names` mapping `groups_claim` to a source, no `groups`) gets its groups
|
|
515
|
+
from the directory the identity provider pushes through the console's SCIM endpoint: the
|
|
516
|
+
`group_attribute` values of the groups whose members include the active user whose
|
|
517
|
+
`user_attribute` equals the token's `user_claim`. They replace the missing claim everywhere
|
|
518
|
+
`groups_claim` is read: a node's grant through `[oidc.groups]`, and the console's grant,
|
|
519
|
+
`[ui] operator_groups`, the team operator's teams, the session's `groups` and the token check.
|
|
520
|
+
It needs `[directory]` and `groups_claim`, each error naming the file, the line and the key.
|
|
521
|
+
Nodes and the console look a token up once at admission and keep the answer five minutes, per
|
|
522
|
+
`user_claim` value; a failed lookup is not kept, and with Postgres unreachable the token has no
|
|
523
|
+
groups, as before, with one stderr line. Meada never calls the source's endpoint. The console
|
|
524
|
+
still refuses a deactivated or deleted provisioned user as `deprovisioned` before any cached
|
|
525
|
+
answer is used. `overage = "ignore"` keeps today's behavior.
|
|
526
|
+
- `[oidc.entra]`, the settings only Entra ID needs, allowed under `type = "entra"` alone, with one
|
|
527
|
+
key: `overage = "ignore"` (the default), a token in overage having no groups, as before. Any
|
|
528
|
+
other value, and an unknown key, is an error naming the file, the line and the key.
|
|
529
|
+
- The data layer (ADR 0016): the `postgres` cargo feature, off by default and enabled by `cloud`,
|
|
530
|
+
and the bootstrap manifest's `[postgres]` section, which needs no other section. It names the
|
|
531
|
+
connection URL only through `url_env` (default `MEADA_POSTGRES_URL`) or `url_file`, plus
|
|
532
|
+
`ca_file`, `schema` (default `meada`), `pool_size`, `connect_timeout_seconds` and
|
|
533
|
+
`statement_timeout_seconds`. The URL is `postgres://` or `postgresql://` with a TCP host and no
|
|
534
|
+
query parameter (`sslmode` included); an error names the key, never the URL. A host outside
|
|
535
|
+
loopback always connects over TLS with its name verified, against `ca_file` alone when set,
|
|
536
|
+
else the system roots; a loopback host connects in plain TCP unless `ca_file` is set. Every
|
|
537
|
+
table lives in the schema `[postgres]` names. `[postgres]` in a build without the feature is
|
|
538
|
+
the error naming it. Postgres 15 and later.
|
|
539
|
+
- `meada postgres migrate --workspace <bootstrap manifest>` creates the schema when absent and
|
|
540
|
+
applies each migration up to the binary's version, each in its own transaction, under an
|
|
541
|
+
advisory lock keyed by the schema, so two runs serialize; it prints the version reached and
|
|
542
|
+
never lowers one. It ships migration 1, the schema's version. Every later connection refuses a
|
|
543
|
+
version the binary does not know: a lower one (an absent schema included) names `meada
|
|
544
|
+
postgres migrate`, a higher one names both versions.
|
|
545
|
+
- The directory (ADR 0016): the `directory` cargo feature, implying `postgres` and enabled by
|
|
546
|
+
`cloud`, and the bootstrap manifest's `[directory]` section, which needs `[postgres]` and
|
|
547
|
+
`[oidc]`: `token_env` (default `MEADA_SCIM_TOKEN`) or `token_file`, `user_claim`,
|
|
548
|
+
`user_attribute` (`userName` or `externalId`) and `group_attribute` (`displayName`, the
|
|
549
|
+
default, or `externalId`). Only the console reads the token, at start: shorter than 32
|
|
550
|
+
characters, unset or unreadable, it refuses to start naming the key, never the value.
|
|
551
|
+
`[directory]` in a build without the feature is the error naming it.
|
|
552
|
+
- The SCIM 2.0 endpoint: with `[ui]` and `[directory]` the console serves `/scim/v2/`, where
|
|
553
|
+
Entra ID's and Okta's provisioning create, read, filter, page, replace, patch and delete users
|
|
554
|
+
and groups, stored in the data layer (migration 3) as the provider sent them, never with
|
|
555
|
+
`password`. It admits only its own bearer token (no console token, and no request carrying
|
|
556
|
+
`Origin`), bounds a body at 1 MiB and a resource at 64 KiB, answers RFC 7644's errors with
|
|
557
|
+
their `scimType` and no echoed value, a 503 with Postgres unreachable, and the
|
|
558
|
+
`/ServiceProviderConfig`, `/ResourceTypes` and `/Schemas` documents. A deleted user leaves
|
|
559
|
+
every group and a revocation marker until it is provisioned again, active. Each write leaves
|
|
560
|
+
one stderr line, `scim: <method> <resource> <id>: <status>`.
|
|
561
|
+
- Deprovisioning in the console (ADR 0016): with `[directory]`, the token's `user_claim` is looked
|
|
562
|
+
up in the directory by `user_attribute` (`userName` ignoring case, `externalId` exact). A user
|
|
563
|
+
found inactive, or deleted and not provisioned active since, gets a 403 `deprovisioned` on every
|
|
564
|
+
route but `GET /api/v1/config`, whatever its role, and the console shows a deactivated-account
|
|
565
|
+
page. A user never provisioned is admitted. With the directory unreadable, reads are admitted
|
|
566
|
+
and a write is a 503 `audit_unavailable` before any audit insert, with one stderr line that
|
|
567
|
+
names the directory and never the claim. Migration 4 adds the directory's state row (the schema
|
|
568
|
+
version is now 4); a directory that had already lost its last group before the upgrade starts
|
|
569
|
+
with no group ever provisioned, so nothing is flagged until a group is provisioned again.
|
|
570
|
+
- The owner picker: `GET /api/v1/directory/groups?prefix=<text>`, for the global operator alone,
|
|
571
|
+
lists at most 100 group names starting with the prefix (compared exactly, sorted), and the
|
|
572
|
+
workspace editor offers them while the caret is inside an `owner = "..."` string.
|
|
573
|
+
- `owner_missing`: `GET /api/v1/workspace` and `workspace/versions/<id>` add `"owner_missing":
|
|
574
|
+
true` to an entry whose owner names no directory group, once a group was ever provisioned; the
|
|
575
|
+
versions page and the editor say so in text. `GET /api/v1/admission` gains the directory's
|
|
576
|
+
users and groups counts and the last SCIM write, which the admission page shows.
|
|
577
|
+
- The audit (ADR 0016): with `[postgres]`, every console write (`workspace.save`,
|
|
578
|
+
`workspace.apply`, `pointer.set`, `pointer.retain`, `pointer.drop`, `fence.recover`) and every
|
|
579
|
+
CLI twin of one (`meada workspace import` and `apply`, `meada snapshot pointer set`, `retain`
|
|
580
|
+
and `drop`, `meada snapshot fence recover`) leaves a record in `<schema>.audit` (migration 2):
|
|
581
|
+
who (subject, role, a team operator's owners), what (action and arguments), when (Postgres's
|
|
582
|
+
clock) and the outcome. The record is written as `pending` before the write touches Valkey or
|
|
583
|
+
the store, so a write whose record cannot be written is refused and changes nothing: the
|
|
584
|
+
console answers 503 `audit_unavailable`, the CLI exits 1 naming Postgres. Role refusals and
|
|
585
|
+
refusals under the lock are recorded too. The console starts with Postgres unreachable (reads
|
|
586
|
+
are served) and refuses to start on a schema version it does not know, as each CLI twin exits
|
|
587
|
+
1. The global operator reads the records, newest first, through `GET
|
|
588
|
+
/api/v1/audit?before=<id>&limit=<n>` and the console's new audit page. Without `[postgres]`
|
|
589
|
+
every write behaves as before, and the stderr lines are unchanged in both cases.
|
|
590
|
+
- `meada status --workspace` with `[postgres]` prints `postgres: reachable, schema <name>
|
|
591
|
+
version <n>` or `postgres: unreachable (<reason>)`, a version refusal included, without
|
|
592
|
+
changing its exit code.
|
|
593
|
+
|
|
594
|
+
### Changed
|
|
595
|
+
|
|
596
|
+
- The repository is a Cargo workspace (ADR 0014, #296, #297): the library crate `meada-core`
|
|
597
|
+
holds the manifest schema, the generation and version ids, the export's types, the query
|
|
598
|
+
results and shape drift, builds for WebAssembly for the console, and is published to
|
|
599
|
+
crates.io before `meada`. Every golden and message of the default build is unchanged.
|
|
600
|
+
- A refresh publishes the whole workspace or nothing (ADR 0012, #316), and a workspace of any
|
|
601
|
+
number of repositories opens and answers (the limit of eleven is gone).
|
|
602
|
+
- `context` and `trace` show the overloads of one method as one set, and a call that decorates
|
|
603
|
+
a definition (an annotation, an attribute, a decorator, a route attribute) is listed under
|
|
604
|
+
`annotations:` in `context` instead of among the callees.
|
|
605
|
+
- A Kotlin `HttpClient` held through more locals than `LOCAL_DEPTH` is now a consumer.
|
|
606
|
+
- The `decisions` `decider`'s default model is now `typesafe/jev-1.14`; `MEADA_DECIDER_MODEL`
|
|
607
|
+
still overrides it.
|
|
608
|
+
- The coding standards class each string table a language pack holds, and a test fails on an
|
|
609
|
+
unlisted one.
|
|
610
|
+
|
|
611
|
+
### Fixed
|
|
612
|
+
|
|
613
|
+
- `meada mcp` answers about the client's first MCP root and falls back to its own directory
|
|
614
|
+
without roots (#192); a `roots/list` that times out after 5 s or fails answers about the last
|
|
615
|
+
root, or returns a tool error, instead of hanging or answering about the wrong tree (#282,
|
|
616
|
+
#285).
|
|
617
|
+
- TypeScript and JavaScript resolution: a call with type arguments under `await`, the `paths` of
|
|
618
|
+
a nested `tsconfig.json`, functions defined by assignment (`exports.y =`, prototypes, object
|
|
619
|
+
literal methods) and a name two `export *` give differently.
|
|
620
|
+
- Python resolution: nested `pyproject.toml` package roots, star imports and names bound again
|
|
621
|
+
(the last binding decides, `del` and a grown or computed `__all__` included).
|
|
622
|
+
- Java and Kotlin resolution: annotations written in full, method annotations through a base
|
|
623
|
+
from outside the repository, calls on another call's result, secondary constructors,
|
|
624
|
+
`expect` and `actual` classes, star imports, varargs, Java locals followed into their value,
|
|
625
|
+
separate field and method namespaces, and Maven or Gradle coordinates named by a property.
|
|
626
|
+
Ambiguous or unresolved OpenFeign and Retrofit methods are `open item`s that `meada decide`
|
|
627
|
+
resolves.
|
|
628
|
+
- C# resolution: types of enclosing namespaces, `internal` types, a qualified name two projects
|
|
629
|
+
declare, library names written in full, member reads between calls and overloads across
|
|
630
|
+
projects; `obj/` and `bin/` stay out of the index.
|
|
631
|
+
- Swift resolution: a module per package with `@testable import`, `typealias` receivers,
|
|
632
|
+
implicit members, capitalized function calls, outside member reads as `opaque outside`, Vapor
|
|
633
|
+
middleware groups and `if let` environment reads.
|
|
634
|
+
- Rust resolution: calls on another call's result, types brought by a glob import, a `mod` any
|
|
635
|
+
test build compiles, and broker clients read by path.
|
|
636
|
+
- A class whose outside bases are all platform types with `known outside members` resolves
|
|
637
|
+
calls to its own members at `exact` (ADR 0019).
|
|
638
|
+
- Spring and Retrofit contracts: an AMQP publish of a repository class, `@Value` fields beside
|
|
639
|
+
same-named locals, Gradle source sets outside `src/main`, Retrofit path rules, and `context`
|
|
640
|
+
on a shared artifact's class listing its calls in other repositories.
|
|
641
|
+
- What gets indexed: `node_modules`, `Pods`, Carthage `Checkouts` and minified files stay out,
|
|
642
|
+
`.meadaignore` is read like a `.gitignore`, `Package@swift-<version>.swift` is a manifest, a
|
|
643
|
+
large generated C# file is read whole (or named by `meada status`), and an invalid or
|
|
644
|
+
oversized config no longer stops indexing.
|
|
645
|
+
- Hostile input: crafted chains of re-exports, receivers, includes and static imports no longer
|
|
646
|
+
overflow the stack; folded values stop at a budget of 2^16 steps per file or 256 levels; a
|
|
647
|
+
manifest past 1 MiB and a provider document past 16 MiB are read capped, and a Spring YAML
|
|
648
|
+
past its key budget reads as empty.
|
|
649
|
+
- Performance: method chains, files of thousands of names, imports or definitions, Java local
|
|
650
|
+
chains, deeply nested Rust blocks and large C# project files no longer take time quadratic
|
|
651
|
+
or worse in their size, and a class family near 128 classes resolves the same whatever the
|
|
652
|
+
reading order.
|
|
653
|
+
- Console and fences: manifest groups and validation errors respect the viewer's grant, PKCE
|
|
654
|
+
redirects keep the issuer's query parameters, and fence recovery shares the fleet reader's
|
|
655
|
+
Valkey clock and heartbeat checks.
|
|
656
|
+
- Rust item macros (#398): the braced body of a `macro_rules!` of the repository whose first arm
|
|
657
|
+
takes `$($x:item)*` and writes each item back behind outer attributes only (tokio's
|
|
658
|
+
`cfg_rt! { pub mod runtime; }`) is read as items of the module invoking it: its functions,
|
|
659
|
+
types, `impl` blocks, `use` and `mod` declarations and the calls inside them, with spans into
|
|
660
|
+
the file. A `mod` in it reaches its file, and the `#[cfg(..)]` attributes the arm writes gate
|
|
661
|
+
every item under the crate's default features. A name defined several times qualifies only when
|
|
662
|
+
all definitions agree; any other macro stays unread. A refresh parses a file again when what it
|
|
663
|
+
invokes changes elsewhere, and equals a cold index.
|
|
664
|
+
|
|
665
|
+
### Measured
|
|
666
|
+
|
|
667
|
+
The maintainer ran this release on the private dogfood set on 2026-10-14 (ADR 0003).
|
|
668
|
+
|
|
669
|
+
Links. One consumer repository calls the provider from four sites in its current code (ADR 0003
|
|
670
|
+
counted five); all four link to the right route at `inferred`, through `base_url_env`. The other
|
|
671
|
+
consumer repository's three calls do not link: its client receives `fetch` as a parameter and
|
|
672
|
+
takes the URL from a destructured object, two patterns this release does not read (#157, #158).
|
|
673
|
+
|
|
674
|
+
Cost, on the provider repository at the 2026-10-11 commit nearest to 633 files (634 TypeScript
|
|
675
|
+
and JavaScript files), median of three runs on a 16-thread Linux laptop:
|
|
676
|
+
|
|
677
|
+
| Metric | Target | Result |
|
|
678
|
+
| --- | --- | --- |
|
|
679
|
+
| Cold index time | ≤ 7 s | 1.2 s |
|
|
680
|
+
| Cold index size | ≤ 37 MB | 21.8 MB |
|
|
681
|
+
| Single-file reindex | ≤ 1 s | 0.24 s |
|
|
682
|
+
| Tool list | ≤ 1k tokens | 454 tokens |
|
|
683
|
+
| Default answer | ≤ 2k tokens | 1,834 tokens, the largest of 11 answers |
|
|
684
|
+
|
|
685
|
+
At today's 664 files: 1.3 s, 23.6 MB and 0.26 s. Tokens count 4 bytes each, as the answer budget
|
|
686
|
+
does. These numbers gate 1.0, not this release.
|
|
687
|
+
|
|
688
|
+
Communities and processes (spec #31) were measured on 2026-10-17 on the same repository, at the
|
|
689
|
+
same commit and at that day's 692 files. The runs used release builds of `main` just before and
|
|
690
|
+
just after the spec, each a median of three runs on a 16-thread Linux laptop with 7.2 GiB of
|
|
691
|
+
RAM. MB here means 10^6 bytes.
|
|
692
|
+
|
|
693
|
+
| Metric | Target | 634 files, before → after | 692 files, before → after |
|
|
694
|
+
| --- | --- | --- | --- |
|
|
695
|
+
| Cold index time | ≤ 7 s | 1.81 s → 1.80 s | 2.20 s → 2.12 s |
|
|
696
|
+
| Cold index size | ≤ 37 MB | 22.7 MB → 23.0 MB | 26.8 MB → 27.1 MB |
|
|
697
|
+
| Single-file reindex | ≤ 1 s | 0.74 s → 0.76 s | 0.89 s → 0.92 s |
|
|
698
|
+
| Tool list | ≤ 1k tokens | | 620 tokens |
|
|
699
|
+
| Default answer | ≤ 2k tokens | | 1,996 tokens, the largest of 11 answers |
|
|
700
|
+
|
|
701
|
+
The tool list counts the whole `tools/list` result as compact JSON. Among the 11 answers are
|
|
702
|
+
`context` on the three most-called symbols, each with its community and process membership,
|
|
703
|
+
and `trace` from the two longest processes. The spec adds about 0.3 MB and 0.03 s to a reindex.
|
|
704
|
+
The rise of the single-file reindex since 2026-10-14 predates it (specs #22 and #23), and at
|
|
705
|
+
692 files it is close to its target.
|
|
706
|
+
|
|
707
|
+
[Unreleased]: https://github.com/ClaudioMascaro/meada/compare/v1.0.0...HEAD
|
|
708
|
+
[1.0.0]: https://github.com/ClaudioMascaro/meada/releases/tag/v1.0.0
|
|
709
|
+
[0.1.0]: https://github.com/ClaudioMascaro/meada/releases/tag/v0.1.0
|