@meada/meada 1.0.0 → 1.1.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/CHANGELOG.md +378 -1
- package/README.md +5 -5
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,382 @@ All notable changes to Meada are recorded here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.1.0] - 2026-11-02
|
|
10
|
+
|
|
11
|
+
A minor release on the [1.x stability promise](https://github.com/ClaudioMascaro/meada/blob/main/docs/site/en/stability.md):
|
|
12
|
+
local accounts (ADR 0024) in the console, the proposal server and the nodes, carried by the
|
|
13
|
+
`cloud` image, the Compose recipe and the Helm chart; the export's `format` and indexed commit;
|
|
14
|
+
client-side deadlines on every Postgres call; durable pulled-generation writes; resolver fixes
|
|
15
|
+
for C#, Kotlin, Rust and Swift (tier b3); and the fixes of tiers b1 and b2. The index schema
|
|
16
|
+
version changes, so existing indexes are rebuilt once on first use. `meada-core` 1.1.0 adds `format`, `commit` and `dirty` to `Export`, and its
|
|
17
|
+
`export::parse` refuses a document without `format`: re-export with 1.1.0 before parsing an export
|
|
18
|
+
written by 1.0.0. The tree, the `meada-core` pin, the Dockerfile, the compose defaults and the
|
|
19
|
+
Helm chart declare `1.1.0`, and the `v1.1.0` tag follows this entry's merge on main. Publication
|
|
20
|
+
(crates, the staged npm package, the GitHub draft release) belongs to the maintainer.
|
|
21
|
+
Release notes are available in
|
|
22
|
+
[pt-BR](https://github.com/ClaudioMascaro/meada/blob/main/docs/releases/1.1.0.md),
|
|
23
|
+
[English](https://github.com/ClaudioMascaro/meada/blob/main/docs/releases/1.1.0.en.md) and
|
|
24
|
+
[Spanish](https://github.com/ClaudioMascaro/meada/blob/main/docs/releases/1.1.0.es.md).
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- The export format and the indexed commit (#512, ADR 0004 "Canonical export"): every export
|
|
29
|
+
starts with `format` (`meada_core::export::FORMAT`, 1), which rises only when a reader of the
|
|
30
|
+
previous shape would misread the new one, never for an added optional field; then
|
|
31
|
+
`repository`, then `commit` and `dirty`, both optional and left out together. Locally `meada export` reads `HEAD` before and
|
|
32
|
+
after its refresh and the checkout's status after it (ignored files, `.meada/` among them, do
|
|
33
|
+
not count), and names the commit only when `HEAD` did not move and the index is fresh; outside
|
|
34
|
+
git or before the first commit both are left out. In server mode they come from the commit
|
|
35
|
+
`meada index --workspace` recorded beside the index, or a generation's `head`, never from the
|
|
36
|
+
mirror's live `HEAD`, with `dirty` false. For `meada-core` crate users: `Export` gains
|
|
37
|
+
`format`, `commit` and `dirty` (a minor change, the struct being `#[non_exhaustive]`, so the
|
|
38
|
+
next `meada-core` release is 1.1.0), and `export::parse` refuses a document without `format`,
|
|
39
|
+
naming the field, so an export written before this release no longer parses. Every `*.export.json` golden gained the three fields.
|
|
40
|
+
|
|
41
|
+
- Named workspaces and the proposal server with local accounts (#562, ADR 0024, from #551):
|
|
42
|
+
`[catalog]` and `[workspaces]` (and so `[proposals]`) accept `[accounts.github]` in place of
|
|
43
|
+
`[oidc.github]` and still need one of the two (`[catalog] needs [oidc.github] or
|
|
44
|
+
[accounts.github]`; `[workspaces]` now names `[ui]` and `[catalog]`, the catalog naming the
|
|
45
|
+
provider). Eligibility reads the account's verified login, an operator account is the platform
|
|
46
|
+
administrator, and workspace operator lists, version authors, history and audit records name
|
|
47
|
+
`local:` subjects. `POST /api/v1/account/tokens` takes `{"surface": "proposals"}` when the
|
|
48
|
+
console's bootstrap has `[proposals]` (audience `meada:proposals`; a 422 `invalid_surface`
|
|
49
|
+
without it), `config` carries `"proposals": true` and the profile page offers the surface. The
|
|
50
|
+
proposal server with `[accounts]` and no `[oidc]` reads the public key at start and admits only
|
|
51
|
+
those tokens (a session token or a node's agent token is a 401 with `WWW-Authenticate: Bearer`,
|
|
52
|
+
no protected resource metadata), deriving the subject, login, groups, grant and role from the
|
|
53
|
+
token alone. No golden changed.
|
|
54
|
+
- The verified GitHub login of local accounts (#561, ADR 0024, from #551): `[accounts.github]`
|
|
55
|
+
(`host`, `roles`, `max_age_seconds` as `[oidc.github]`, plus `api`, `https://api.github.com` by
|
|
56
|
+
default on `github.com` and required on any other host) is the GitHub grant provider of local
|
|
57
|
+
accounts. A person pastes a GitHub personal access token on their profile page or sends it to
|
|
58
|
+
`POST /api/v1/account/github`; the console calls `GET /user` on `api` once, keeps the lowercase
|
|
59
|
+
login and drops the token, and answers a fresh session token carrying the login. A login
|
|
60
|
+
another account holds is a 409 `login_taken`, GitHub's refusal a 422, GitHub out of reach a
|
|
61
|
+
503. `POST /api/v1/account/github/remove` and the accounts page's `remove_github` action remove
|
|
62
|
+
it. Session and agent tokens carry the verified login, so the GitHub grant joins the groups
|
|
63
|
+
grant in the console (`session` shows `login`, `github_host` and `github_grant`), on nodes, in
|
|
64
|
+
a team operator's additions (`added_by`), in the audit's `login` column and in the exclusions
|
|
65
|
+
marks; `meada github-app roles` reads `[accounts.github]`. Recorded `account.github` and
|
|
66
|
+
`account.remove_github`; the personal access token is never stored, logged or audited. No
|
|
67
|
+
golden changed.
|
|
68
|
+
|
|
69
|
+
- Helm chart support for local accounts (#565, ADR 0024, from #551): `modules.accounts` renders
|
|
70
|
+
`[accounts]` from values, with `values-accounts.yaml` as the proof-of-concept shape (a writable
|
|
71
|
+
console with `operations = true`, Postgres and no identity provider). The Ed25519 private key
|
|
72
|
+
comes from its own Secret and is mounted only in the console's `console` container, the public
|
|
73
|
+
key from another Secret in the console and the nodes (a node also gets `[ui] public_url`, the
|
|
74
|
+
token issuer, and `[postgres]`), the initial admin password from an optional Secret in the
|
|
75
|
+
console. `modules.accounts.groups` and `modules.accounts.github` carry `[accounts.groups]` and
|
|
76
|
+
`[accounts.github]` (the refresh job, with `modules.githubApp`, gets the GitHub grant provider and runs the role check, and
|
|
77
|
+
`modules.workspaces` accepts it in place of `modules.oidc.github`); `modules.accounts.breakGlass`
|
|
78
|
+
keeps `[accounts]` in the console alone beside `modules.oidc`. The chart refuses a missing
|
|
79
|
+
Postgres or console, equal key Secrets, accounts beside OIDC without break glass, and groups or
|
|
80
|
+
GitHub under break glass. `scripts/helm-check` covers the sections, the mounts and the refusals;
|
|
81
|
+
the chart READMEs (pt-BR, English, Spanish) no longer say a writable console needs OIDC. The
|
|
82
|
+
proposal server is not part of the chart. `[accounts.github]` renders as the spec names it and
|
|
83
|
+
is parsed once #561 lands. No golden changed.
|
|
84
|
+
|
|
85
|
+
- One-command proof of concept in the Compose recipe (#564, ADR 0024, from #551):
|
|
86
|
+
`cp .env.accounts.example .env && docker compose up -d` starts the generations fleet, Valkey,
|
|
87
|
+
the store, Postgres with its migration, a writable console with local accounts and nodes that
|
|
88
|
+
admit agent tokens. `compose.accounts.yaml` adds the Ed25519 key pair (made once by the
|
|
89
|
+
`certificates` step, never printed, the private key mounted read-only in the console alone and
|
|
90
|
+
the public one in the nodes), the console's and the nodes' `[accounts]` manifests, a one-shot
|
|
91
|
+
`seed` of the first workspace version (only while the store has none) and a node proxy that
|
|
92
|
+
forwards `Authorization` with no static bearer. The other profiles are unchanged, and
|
|
93
|
+
`scripts/compose-check` covers the new one. The Compose README (pt, en, es) starts with it and
|
|
94
|
+
covers `MEADA_ADMIN_PASSWORD`, `meada ui reset-admin`, key rotation and rate limiting the
|
|
95
|
+
sign-in route at the proxy.
|
|
96
|
+
|
|
97
|
+
- Agent tokens for the nodes (#560, ADR 0024, from #551): a signed-in local account generates,
|
|
98
|
+
on its profile page or through `POST /api/v1/account/tokens` (`{surface: "nodes", hours}`, 1 to
|
|
99
|
+
168 hours, 8 by default), an agent token shown once (`{token, expires_at, surface}`), signed
|
|
100
|
+
with the account's role and groups as its row stands and a random token id, recorded
|
|
101
|
+
`account.token` (surface, hours, token id, never the token). A change-required account gets the
|
|
102
|
+
403 `password_change_required`, a suspended one the role refusal; the console answers an agent
|
|
103
|
+
token with 401. A node with `[accounts]` and no `[oidc]` (`meada mcp --listen`) now reads the
|
|
104
|
+
signing public key at start, and the console's `[ui] public_url` from a `[ui]` section its
|
|
105
|
+
manifest must carry, refusing to start without either; it admits only agent tokens for the
|
|
106
|
+
nodes (a 401 `Bearer` before routing for any other, a session token included) and answers
|
|
107
|
+
under the grant `[accounts.groups]` gives their groups (every repository without the table),
|
|
108
|
+
as an identity provider's token is answered. The `accounts` feature now implies `ui`. No golden
|
|
109
|
+
changed.
|
|
110
|
+
|
|
111
|
+
- Accounts page in the console (#559, ADR 0024, from #551): the global operator of a console with
|
|
112
|
+
local accounts gets `/accounts`, listing every account (role, groups, state with a suspended
|
|
113
|
+
account marked and a locked one saying until when, change required, last password change), a
|
|
114
|
+
create form (username pattern and password length checked first, "change on first sign-in"
|
|
115
|
+
checked by default) and, per account, role, groups, suspend, reactivate and reset password,
|
|
116
|
+
each behind the shared confirmation dialog with the server's refusals (`last_operator` and the
|
|
117
|
+
rest) shown unchanged. Any other role, and a console under break glass, gets the not-found
|
|
118
|
+
page at that path. The audit page names every `account.*` action in Portuguese and English.
|
|
119
|
+
The password rule `common_password` now has its own copy. Front end only, no golden changed.
|
|
120
|
+
|
|
121
|
+
- Groups of local accounts (#558, ADR 0024, from #551): the global operator gives accounts groups
|
|
122
|
+
(`groups` on `POST /api/v1/accounts`, and the `groups` action of
|
|
123
|
+
`POST /api/v1/accounts/<username>`, each an owner name, else a 422 `invalid_group`);
|
|
124
|
+
`GET /api/v1/accounts` lists them, and each change is recorded `account.groups`.
|
|
125
|
+
`[accounts.groups]` maps groups to identities as `[oidc.groups]` does: once it exists, a local
|
|
126
|
+
session's grant is what its groups map to, and it bounds every view (the manifest, the
|
|
127
|
+
workspace and its versions, the store, the repositories, the credentials, the playground, the
|
|
128
|
+
exports and the graph views) as an identity provider's grant does; without it every local
|
|
129
|
+
account still reads every repository. A local viewer whose group owns an entry of the current
|
|
130
|
+
workspace version its grant reads is a team operator (ADR 0015). Session tokens carry the
|
|
131
|
+
groups, so a change reaches reads at the next sign-in and writes at once (the account row is
|
|
132
|
+
read before each). `GET /api/v1/admission` with `[accounts]` now answers: `accounts` (the mode,
|
|
133
|
+
`accounts` or `break_glass`, and the signing public key's fingerprint, `blake3:` and the hex
|
|
134
|
+
BLAKE3 hash of the raw Ed25519 key) and, without `[oidc]`, the groups table bounded by the
|
|
135
|
+
caller's grant. No golden changed.
|
|
136
|
+
|
|
137
|
+
- Account administration for local accounts (#557, ADR 0024, from #551): the global operator
|
|
138
|
+
lists every account (`GET /api/v1/accounts`: username, role, state, the end of a lock, change
|
|
139
|
+
required, last password change, created and created by, never a hash), creates one
|
|
140
|
+
(`POST /api/v1/accounts`, `{username, role, password, change_required}`, the policy applied to
|
|
141
|
+
the temporary password, a change required unless the body turns it off; a malformed username a
|
|
142
|
+
422 `invalid_username`, a taken one a 409 `username_taken`) and changes one
|
|
143
|
+
(`POST /api/v1/accounts/<username>`, one `action`: `role`, `suspend`, `reactivate` or
|
|
144
|
+
`reset_password`, which clears a lock and requires a change unless told not to; an unknown
|
|
145
|
+
account a 404). Suspending or demoting the last active operator is a 409 `last_operator`,
|
|
146
|
+
decided under the schema's advisory lock so two requests at once never leave none. Anyone else
|
|
147
|
+
gets a 403. The routes answer whatever `[ui] operations` says; workspace and generation writes
|
|
148
|
+
keep requiring it. Every console write of a local account now reads the account's row first,
|
|
149
|
+
so a suspended or demoted account loses its writes at once, while its reads last until its
|
|
150
|
+
token expires. Audit actions `account.create`, `account.role`, `account.suspend`,
|
|
151
|
+
`account.reactivate` and `account.reset_password` name the account and the fields changed,
|
|
152
|
+
never a password, written before the change (503 `audit_unavailable` otherwise); `account` is
|
|
153
|
+
the route-level action of a change refused before its body is read. No route deletes or
|
|
154
|
+
renames an account; under `[accounts] break_glass` (#563) none of these routes exists, each a
|
|
155
|
+
404. No golden changed.
|
|
156
|
+
|
|
157
|
+
- Break-glass account beside the identity provider (#563, ADR 0024, from #551): `[accounts]
|
|
158
|
+
break_glass = true` lets the section sit beside `[oidc]`; without `[oidc]` it is an error, and
|
|
159
|
+
`[accounts.groups]` or `[accounts.github]` under it is an error naming the table. The console
|
|
160
|
+
then signs in only the default account (any other username gets the wrong password's 401,
|
|
161
|
+
audited `not_break_glass`, the table never read) as the global operator, and admits its session
|
|
162
|
+
token beside the identity provider's, each bearer routed by its issuer to one verifier, so the
|
|
163
|
+
identity provider's keys never check a console token and the signing key never checks an
|
|
164
|
+
identity provider's; a console whose `[oidc] issuer` is its own `public_url` refuses to start.
|
|
165
|
+
The password change works; no other route of local accounts exists. Nodes and the proposal
|
|
166
|
+
server keep admitting only the identity provider and read no key. `config` adds `break_glass:
|
|
167
|
+
true`, and the front end offers a discreet break-glass sign-in (`/break-glass`) beside the
|
|
168
|
+
identity provider's, which now waits for a click under break glass. No golden changed.
|
|
169
|
+
|
|
170
|
+
- Hardening of local sign-in (#555, ADR 0024, from #551): the password policy refuses an embedded
|
|
171
|
+
common-password list (SecLists, MIT, the entries of 12 to 128 characters, compared ignoring
|
|
172
|
+
case; rule `common_password`). Ten consecutive failed password checks, at sign-in or as a
|
|
173
|
+
password change's current password, lock an account for fifteen minutes, counted in a
|
|
174
|
+
transaction holding the account row so parallel attempts never pass ten; a success resets the
|
|
175
|
+
count. A locked account gets the wrong password's 401 at a hash's cost, its password unchecked
|
|
176
|
+
and its lock never extended, audited `locked_now` and then `locked`. A stored hash weaker than
|
|
177
|
+
the console's parameters is redone at the next successful sign-in. `meada ui reset-admin
|
|
178
|
+
--workspace <manifest>` (features `ui` and `accounts`) creates or reactivates `admin` as an
|
|
179
|
+
operator, clears its lock and sets the `MEADA_ADMIN_PASSWORD` value or a generated password
|
|
180
|
+
printed once on stdout, with a change required, recorded `account.reset_admin` `via` `cli`
|
|
181
|
+
before the write and refusing with exit 1 when the record cannot be written. No golden changed.
|
|
182
|
+
|
|
183
|
+
- Local sign-in for the console (#554, ADR 0024, from #551): the new `accounts` cargo feature
|
|
184
|
+
(enabled by `cloud`) and its `[accounts]` section, which needs `[postgres]` and never sits
|
|
185
|
+
beside `[oidc]`, name the Ed25519 signing key pair and the default account's initial password,
|
|
186
|
+
each from a variable or a file. Migration 9 adds the `accounts` table, so `meada postgres
|
|
187
|
+
migrate` reaches version 9. On its start the console checks the key pair and creates the
|
|
188
|
+
default account `admin` (password `admin123` with a change required, or the policy-checked
|
|
189
|
+
`MEADA_ADMIN_PASSWORD` without) once, under an advisory lock. `POST /api/v1/login` answers a
|
|
190
|
+
session token the console signs with EdDSA for eight hours; a token carrying the change-required
|
|
191
|
+
mark opens only `POST /api/v1/account/password`. Every route but the static files, `config` and
|
|
192
|
+
`login` then needs a session token; `[ui] operations = true` is accepted with `[accounts]` and
|
|
193
|
+
no `[oidc]`; `config` names the sign-in mode (`sign_in`: `accounts`, or `oidc` with an identity
|
|
194
|
+
provider), `session` adds `account: true`, and
|
|
195
|
+
every account reads every repository while no groups table bounds it. Passwords are argon2id at
|
|
196
|
+
OWASP's parameters under a policy of 12 to 128 characters that refuses the default password and
|
|
197
|
+
the username, at most four hashes run at once, and every sign-in, refusal and password change
|
|
198
|
+
is audited (`account.login`, `account.password`, role `none` for an unknown username) before its
|
|
199
|
+
token. The module table gains `accounts`; no golden changed.
|
|
200
|
+
|
|
201
|
+
- The console's sign-in pages for local accounts (#556, from #551): when `config` says
|
|
202
|
+
`"sign_in": "accounts"` the console shows its own sign-in page (labelled username and password,
|
|
203
|
+
one generic refusal, `busy` and `audit_unavailable` shown as themselves) instead of the identity
|
|
204
|
+
provider's redirect. A change-required sign-in goes straight to a forced password change page
|
|
205
|
+
(the current password and the new one twice, the server's rule shown on a refusal) that offers
|
|
206
|
+
nothing to navigate to until it succeeds, and then opens the workspace. The session token lives
|
|
207
|
+
in `sessionStorage` for the tab's life, is sent as the bearer and is cleared on sign-out and on
|
|
208
|
+
any 401 (which returns to sign-in; a refused current password in the change is not one); it is never in `localStorage`, a URL
|
|
209
|
+
or a log. A profile page offers the voluntary password change and sign-out. pt-BR and English
|
|
210
|
+
copy; the identity provider flow is unchanged. Front end only: no golden changed.
|
|
211
|
+
|
|
212
|
+
- `meada status` names a Kotlin file the grammar misreads (#546, from #434). A declaration read
|
|
213
|
+
as an infix call leaves the file out of the index; the file now sets the new `Facts::misparsed`
|
|
214
|
+
(absent from every other file's stored facts) instead of `unbuilt`, and a new status line,
|
|
215
|
+
`Msg::FileMisparsed`, in English and Portuguese, names it beside the byte-budget and using
|
|
216
|
+
drops. The Java and Kotlin layout skips it as it skips the other drops. The schema version is
|
|
217
|
+
the same: a file dropped before the upgrade gets its line when it next changes, or at once
|
|
218
|
+
after deleting `.meada/index.db`. The `cli.status-budget` goldens gained the new line.
|
|
219
|
+
|
|
220
|
+
### Changed
|
|
221
|
+
|
|
222
|
+
- The grant providers take neutral inputs (#553, from #551): the new `grant` module computes a
|
|
223
|
+
subject's grant from its groups and its GitHub login (`Providers::grant`), with no reference
|
|
224
|
+
to a token, and holds the GitHub grant provider's permissions cache, which moved out of the
|
|
225
|
+
OIDC module; the OIDC module feeds it from a token's claims. Every reader of `[oidc.github]`
|
|
226
|
+
goes through `Manifest::github`, the console and the proposal server build their sessions
|
|
227
|
+
from a neutral admitted subject (`grant::Admitted`), and a node's pass carries only a grant.
|
|
228
|
+
Nothing a caller sees changes; no golden changed.
|
|
229
|
+
|
|
230
|
+
- The axum provider reader walks each value once per file (#431): the values a file's rebound
|
|
231
|
+
`let`s reach share one `seen` set, and `Code::origins` is not asked for a call whose builder
|
|
232
|
+
chain goes through a method no router hands back, nor for a name written as text, a path, a
|
|
233
|
+
number, a boolean or a character. The reader's share of a one-file refresh on this repository
|
|
234
|
+
drops from about 1.4 s to about 0.1 s. No golden changed.
|
|
235
|
+
|
|
236
|
+
### Fixed
|
|
237
|
+
|
|
238
|
+
- Swift: a call whose labels differ from a known outside member's resolves as if that base
|
|
239
|
+
were absent (#463): `x.hash(seed: 1)` on a type that conforms to `Hashable` and declares
|
|
240
|
+
`hash(seed:)` is `exact`, while `x.hash(into: &h)` stays opaque. A call with no labels, or in
|
|
241
|
+
another language, is still compared by its bare name.
|
|
242
|
+
- Swift: an alias of an outside type reaches the repository's extensions of that type (#440). A
|
|
243
|
+
receiver typed by `typealias Anim = Animation`, or by an alias of an alias, and `Anim.build()`
|
|
244
|
+
match `extension Animation { }` as `Animation` does; an alias of an outside type with no
|
|
245
|
+
extension stays unresolved.
|
|
246
|
+
- Rust: a call after `?` or `.await` is typed by the value they unwrap (#435, after #401): the
|
|
247
|
+
pack keeps beside a function's written result the first type argument of a `Result` or
|
|
248
|
+
`Option` (`Def::unwrapped`) and the `Output = T` of `impl Future<Output = T>` and
|
|
249
|
+
`Pin<Box<dyn Future<Output = T>>>` (`Def::output`, read only through `.await`), so
|
|
250
|
+
`load()?.port()` and `fut().await.port()` resolve within `T`'s family, `exact` when it
|
|
251
|
+
declares one `port`, where `?` left a name match and a plain `fn` returning a future resolved
|
|
252
|
+
to nothing. A type outside the repository, or a `?` before an `.await`, leaves the call as
|
|
253
|
+
it was. The stored facts gain
|
|
254
|
+
`Def::unwrapped`, `Def::output`, `Call::unwraps` and `Call::awaits`, so the schema version
|
|
255
|
+
changes and Rust indexes are rebuilt once.
|
|
256
|
+
- Kotlin: a class, object or interface nested in an `expect` declaration, at any depth, is
|
|
257
|
+
`expect` too (#437), so a use of it resolves to the `expect` one instead of staying
|
|
258
|
+
`ambiguous` among its `actual` declarations.
|
|
259
|
+
- C# `using static T` imports only the static members and nested types `T` declares (#433): a bare
|
|
260
|
+
`Go()` with `Go` on a base of `T` was an `exact` link to the base's method, which C# rejects
|
|
261
|
+
("inherited members aren't imported"), and is now a name match at best; `using static` of the
|
|
262
|
+
base itself still gives it. Java's `import static` and Kotlin keep reading inherited members.
|
|
263
|
+
- Kotlin: a bare call resolves in Kotlin's order, an extension of the implicit receiver in scope
|
|
264
|
+
before a top-level function of its name (#436, from #402): inside `fun CoroutineScope.x() {
|
|
265
|
+
launch { } }` the call is `exact` to `CoroutineScope.launch`, not to the top-level `launch`
|
|
266
|
+
(`ambiguous` with it when the extension may apply to any receiver), and an `object`'s name is
|
|
267
|
+
its instance, so `Dispatchers.setMain(..)` reaches an imported `fun Dispatchers.setMain` (it
|
|
268
|
+
was no link) while a class's name keeps companion and statics. "Known ceilings" drops the
|
|
269
|
+
bare-call line. No golden changed.
|
|
270
|
+
- C#: an `internal`, `private` or `private protected` constructor or method of a public type is
|
|
271
|
+
a candidate from its own project's files alone (#441); `InternalsVisibleTo` stays unread.
|
|
272
|
+
- The console writes one `ui: directory: unreadable` stderr line per request when the directory
|
|
273
|
+
cannot be read (#451, after #335): the request's session remembers the failure at admission,
|
|
274
|
+
so the flags, the admission counts and the owner picker take that answer and write no second
|
|
275
|
+
line (`workspace` wrote two), and a write it refuses as 503 `audit_unavailable` prints no
|
|
276
|
+
pre-authentication audit line beside it. Reads still answer with the flags unavailable, the
|
|
277
|
+
counts null and the picker a 503; writes still refuse as before. No golden changed.
|
|
278
|
+
- Postgres calls that had no client-side deadline now have one (#447): a SCIM request (under the
|
|
279
|
+
directory's one write lock), the role check's fence read and write transaction, and the
|
|
280
|
+
console's start check, so a Postgres that accepts the connection and stops answering ends in a
|
|
281
|
+
503, an exit 1 with no exclusions file a reader admits, and a console that starts, instead of
|
|
282
|
+
holding them forever. `postgres::Config::bound(n)` now adds the version check's two statements
|
|
283
|
+
itself and every call site counts only its own (the audit and directory reads and the overage
|
|
284
|
+
lookup moved accordingly; the accounts' calls, which undercounted, and the permissions poll
|
|
285
|
+
gained the two). No golden changed.
|
|
286
|
+
- A crash after the pointer swap can no longer leave `current` naming a version document that
|
|
287
|
+
never reached the disk (#518): the pointer, the version document `meada workspace pull` writes,
|
|
288
|
+
the pulled contexts file and the exclusions file are replaced through one helper
|
|
289
|
+
(`pull::replace`) that syncs the new file before the rename and removes `<name>.new` when a
|
|
290
|
+
step fails. A failed pointer rename now names `current`, not `current.new`.
|
|
291
|
+
- The docs say what 1.0.0 ships: nine languages (TypeScript, JavaScript, Python, Go, Rust, Java,
|
|
292
|
+
Kotlin, C# and Swift). The three READMEs, the three 1.0.0 release notes and the three user
|
|
293
|
+
guides no longer call Java, Kotlin, C# and Swift later 1.x work, the user guides gain a section
|
|
294
|
+
for each of the four (9.5 to 9.8) with their brokers (JMS, Azure Service Bus) and package
|
|
295
|
+
ecosystems (Maven and Gradle, NuGet, SwiftPM), and ADR 0003 records the change in an
|
|
296
|
+
amendment. No code, default dependency or golden changed.
|
|
297
|
+
- A server-mode mirror's `meada.toml` and `meada.decisions.toml` are read through no symbolic
|
|
298
|
+
link (#547, after #506), as a pulled generation's are: `mirror()`, behind `Manifest::read`
|
|
299
|
+
(`mcp --listen`, snapshot pull, the lock and pointer operations, workspace versions, the
|
|
300
|
+
heartbeat, the UI, the workspaces CLI, the GitHub App), now refuses a link unopened, one that
|
|
301
|
+
stays inside the mirror included, a file that is not regular and a file replaced while read,
|
|
302
|
+
with the generation's messages, and bounds each file at 1 MiB (`GENERATION_CONFIG_MAX`, now
|
|
303
|
+
built without the `snapshot` feature). A local checkout and an operator's own files still
|
|
304
|
+
follow links. The architecture map and the snapshots page, in its three languages, say so.
|
|
305
|
+
- No manifest error quotes a written value (#545), after #449 stopped serde's `invalid type` and
|
|
306
|
+
`invalid value`: `unknown variant` names the key and the values it accepts (`` `workspace.source`:
|
|
307
|
+
is not one of the values it accepts (`file`, `store`) ``) instead of serde's text, an error no
|
|
308
|
+
key spans reads "a value is not valid here" instead of serde's message, and the rule errors
|
|
309
|
+
for `serve.prefix`, `serve.strip`, `serve.documents` and a resource's `kind` name the key and
|
|
310
|
+
the rule (`` `serve.strip`: must start with `/` ``) instead of the value. The console's cut of
|
|
311
|
+
an error to its position stays as a second guard (it still covers an error built outside
|
|
312
|
+
`describe` that names an identity). English and pt-BR texts changed; the workspace validation
|
|
313
|
+
fixture changed only by the new texts.
|
|
314
|
+
- The deploy fetch reads a `[[repository]]` entry by its own keys (#544): any table header ends
|
|
315
|
+
the entry before it, so a `[[repository.peer]]` or `[[repository.resource]]` table no longer
|
|
316
|
+
replaces its `repo` or `path`, and a value is the text between its quotes, so a trailing
|
|
317
|
+
comment is not part of it. Before, such a version stopped the Compose refresh and let the chart
|
|
318
|
+
clone the peer's repository into the entry's path (or into a directory named after the
|
|
319
|
+
comment). `fetch-pulled.sh` and the chart's `workspace.fetch` carry one awk program, and a
|
|
320
|
+
test fails when the two copies drift. No golden changed.
|
|
321
|
+
- A repository identity on the `[oidc.github]` host compares without case wherever it meets the
|
|
322
|
+
catalog (#508): the roles job gives a selected `github.com/Acme/API` and the catalog's
|
|
323
|
+
`github.com/acme/api` one `both` row keyed as written, the `max_repositories` cap does not
|
|
324
|
+
count it as extra, and the console's catalog check and listing match it. A repository selected
|
|
325
|
+
in two spellings (a deployment's `github.com/Acme/API`, a named workspace's
|
|
326
|
+
`github.com/acme/api`) keeps one `source` row under the first and gets its role rows under
|
|
327
|
+
each, so grants, which match exactly, read both. Other hosts compare exactly. No golden
|
|
328
|
+
changed.
|
|
329
|
+
- Compose mirror origins (#445): `fetch.sh` and `fetch-pulled.sh` read an existing mirror's
|
|
330
|
+
`origin` before fetching and refuse one whose HTTP(S) URL carries credentials (a mirror cloned
|
|
331
|
+
from a URL with a token keeps it there), naming the identity and never the URL, and the refresh
|
|
332
|
+
stops before index and publish as a failed fetch does. The READMEs tell the operator to run
|
|
333
|
+
`git remote set-url origin <URL without credentials>` in such a mirror.
|
|
334
|
+
- Java and Kotlin: a file with thousands of fields and locals is read in linear time (#442, from
|
|
335
|
+
#424). The hides check indexes the fields by name instead of scanning every field per local,
|
|
336
|
+
the class around a write is found from the prefixes of its scope, and `declared_before` (and
|
|
337
|
+
Kotlin's `assigned`) read a node's parent from an index built once per file, since
|
|
338
|
+
`Node::parent()` is found from the root and made a deep file quadratic per write. Same answers;
|
|
339
|
+
no golden changed.
|
|
340
|
+
- Indexing: a file a pack drops for passing a limit is named by `meada status` (#434, from
|
|
341
|
+
#419). The Java, Kotlin and Swift qualified-name budget (4 MiB) now sets `Facts::budget`, as the
|
|
342
|
+
C# one does, instead of `unbuilt`, which prints nothing; a C# project that gives its files more
|
|
343
|
+
than 256 usings (`PROJECT_USINGS`) marks each of them with the new `Facts::usings`, which a new
|
|
344
|
+
status line names (`Msg::FileTooManyUsings`, with the count). Kotlin's `misparsed` file stayed
|
|
345
|
+
`unbuilt` (a grammar defect, not a limit; #546 names it too). An index built before this change keeps a Java,
|
|
346
|
+
Kotlin or Swift file dropped by its budget as `unbuilt` until the file changes. No limit
|
|
347
|
+
changed. The `cli.status-budget` goldens gained the new cases; no stored format golden changed
|
|
348
|
+
(the new field is absent from every sample), so the schema version is the same.
|
|
349
|
+
- Resolution: the receiver and call depth (`RECEIVER_DEPTH`) is one count for the whole lookup
|
|
350
|
+
(#432, from #417), carried through a glob, an alias or an on-demand import as the link count is,
|
|
351
|
+
so a chain that alternates receivers and imports costs its length, not `RECEIVER_DEPTH` per link
|
|
352
|
+
up to `LINK_DEPTH`. No limit changed and no answer under the limits; no golden changed.
|
|
353
|
+
- Helm: the default `workspace.fetch` (#444, from #316) reads the repositories from `$1` when
|
|
354
|
+
given (the pulled list under `modules.oidc.github`) else from `workspace.repositories`, now
|
|
355
|
+
mounted as `repositories.toml` so a store-sourced refresh, whose bootstrap lists none, still
|
|
356
|
+
fetches the operator's list, refuses a URL carrying a credential and, as the compose
|
|
357
|
+
scripts do (#445), an existing mirror whose `origin` carries one, keeps each mirror strictly
|
|
358
|
+
under `/data/mirrors`, and on failure prints `refresh: fetch failed for <identity>; the refresh
|
|
359
|
+
stops before publication` and exits nonzero without echoing a URL or token.
|
|
360
|
+
`scripts/helm-check` runs the rendered script under `sh` with a stub `git` to check these; the
|
|
361
|
+
chart READMEs describe it.
|
|
362
|
+
- Workspace import (#450): `meada workspace import` reads its file once, bounded by the version
|
|
363
|
+
size limit, and audits the id of the text it stored; a file past the bound or not a regular
|
|
364
|
+
file is refused before any audit record is written.
|
|
365
|
+
- Manifest type errors (#449): a `meada.toml` or workspace manifest key of the wrong type, or
|
|
366
|
+
with an invalid value, reports the key and the expected kind (`` `oidc.entra`: has the wrong
|
|
367
|
+
type (a table was expected) ``) instead of serde's message, which echoed the written value, so a
|
|
368
|
+
secret pasted under the wrong key no longer reaches stderr.
|
|
369
|
+
- Pulled generations (#506): a mirror's `meada.toml` or `meada.decisions.toml` that is a symbolic
|
|
370
|
+
link is refused unopened, the error naming the file, so a repository can no longer make a node
|
|
371
|
+
read and parse a file of the host. A link into the mirror (`meada.toml -> conf/meada.toml`) is
|
|
372
|
+
refused too, as the index refuses a linked pack config. The file opened is checked to be the one
|
|
373
|
+
checked. Secrets and CAs a manifest names still follow links (Kubernetes mounts them through
|
|
374
|
+
one). The snapshots page says so in its three languages.
|
|
375
|
+
- Named workspaces on a node (#510): a node shows a named workspace's route
|
|
376
|
+
(`/-/workspaces/<id>/<identity>`, pinned or not) and lists it in a 404 only to a token whose
|
|
377
|
+
grant reads every entry of the workspace's version, the entries the sponsor rule excluded
|
|
378
|
+
included, as the console does. Before, once A of a Y+A workspace lost its sponsor, a subject
|
|
379
|
+
reading Y alone found the workspace on a node. A generation's context now records `excluded`,
|
|
380
|
+
the identities the sponsor rule left out, sorted, and its digest covers them; a context without
|
|
381
|
+
one is written as before, with the same generation id, and a generation published earlier reads
|
|
382
|
+
as none excluded. A node on an older binary refuses a generation that records an exclusion, so
|
|
383
|
+
upgrade nodes before or with the publisher. No golden changed.
|
|
384
|
+
|
|
9
385
|
## [1.0.0] - 2026-10-30
|
|
10
386
|
|
|
11
387
|
The tree declares `1.0.0`: this release raises the workspace, the `meada-core` pin, the Dockerfile,
|
|
@@ -704,6 +1080,7 @@ and `trace` from the two longest processes. The spec adds about 0.3 MB and 0.03
|
|
|
704
1080
|
The rise of the single-file reindex since 2026-10-14 predates it (specs #22 and #23), and at
|
|
705
1081
|
692 files it is close to its target.
|
|
706
1082
|
|
|
707
|
-
[Unreleased]: https://github.com/ClaudioMascaro/meada/compare/v1.
|
|
1083
|
+
[Unreleased]: https://github.com/ClaudioMascaro/meada/compare/v1.1.0...HEAD
|
|
1084
|
+
[1.1.0]: https://github.com/ClaudioMascaro/meada/releases/tag/v1.1.0
|
|
708
1085
|
[1.0.0]: https://github.com/ClaudioMascaro/meada/releases/tag/v1.0.0
|
|
709
1086
|
[0.1.0]: https://github.com/ClaudioMascaro/meada/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Escolha a linha que combina com o que você já tem. Todos os canais entregam o
|
|
|
18
18
|
| Rust | `cargo install meada` | `meada mcp` |
|
|
19
19
|
| Linux ou macOS | o instalador de shell abaixo | `meada mcp` |
|
|
20
20
|
| Qualquer sistema | o arquivo do seu sistema nas [releases do GitHub](https://github.com/ClaudioMascaro/meada/releases), com `meada` no `PATH` | `meada mcp` |
|
|
21
|
-
| Docker | `docker pull ghcr.io/claudiomascaro/meada:1.
|
|
21
|
+
| Docker | `docker pull ghcr.io/claudiomascaro/meada:1.1.0` | a linha `docker run` em [Conectar um agente](#conectar-um-agente-mcp) |
|
|
22
22
|
|
|
23
23
|
```sh
|
|
24
24
|
# Instalador de shell (Linux e macOS)
|
|
@@ -75,7 +75,7 @@ Com Docker (Linux e macOS):
|
|
|
75
75
|
"mcpServers": {
|
|
76
76
|
"meada": {
|
|
77
77
|
"command": "sh",
|
|
78
|
-
"args": ["-c", "exec docker run -i --rm --network none --user \"$(id -u):$(id -g)\" -v \"${PWD%/*}:${PWD%/*}\" -w \"$PWD\" ghcr.io/claudiomascaro/meada:1.
|
|
78
|
+
"args": ["-c", "exec docker run -i --rm --network none --user \"$(id -u):$(id -g)\" -v \"${PWD%/*}:${PWD%/*}\" -w \"$PWD\" ghcr.io/claudiomascaro/meada:1.1.0 mcp"]
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -97,7 +97,7 @@ ler os níveis de uma resposta. No Claude Code:
|
|
|
97
97
|
|
|
98
98
|
```sh
|
|
99
99
|
mkdir -p ~/.claude/skills/meada
|
|
100
|
-
curl -fsSL https://raw.githubusercontent.com/ClaudioMascaro/meada/v1.
|
|
100
|
+
curl -fsSL https://raw.githubusercontent.com/ClaudioMascaro/meada/v1.1.0/skills/meada/SKILL.md -o ~/.claude/skills/meada/SKILL.md
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
Outros agentes: veja [Agentes e MCP](https://claudiomascaro.github.io/meada/mcp/#skill-para-agentes).
|
|
@@ -111,8 +111,8 @@ Meada 1.0 entrega todo o escopo da v1 do [ADR 0003](docs/adr/0003-v1-scope-and-r
|
|
|
111
111
|
- **Níveis**: um nível em cada ligação, cadeias que atravessam repositórios e decisões com
|
|
112
112
|
decisor opcional (`meada decide`).
|
|
113
113
|
- **Análises**: drift de forma, comunidades e processos.
|
|
114
|
-
- **Linguagens no 1.0**: TypeScript
|
|
115
|
-
|
|
114
|
+
- **Linguagens no 1.0**: nove, TypeScript, JavaScript, Python, Go, Rust, Java, Kotlin, C# e
|
|
115
|
+
Swift (#26 a #29, ADR 0003).
|
|
116
116
|
|
|
117
117
|
**Promessa de estabilidade.** A partir do 1.0 vale o semver: uma mudança incompatível em um
|
|
118
118
|
destes itens pede uma versão major.
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"hasInstallScript": true,
|
|
20
20
|
"license": "Apache-2.0",
|
|
21
21
|
"name": "@meada/meada",
|
|
22
|
-
"version": "1.
|
|
22
|
+
"version": "1.1.0"
|
|
23
23
|
},
|
|
24
24
|
"node_modules/detect-libc": {
|
|
25
25
|
"engines": {
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"requires": true,
|
|
51
|
-
"version": "1.
|
|
51
|
+
"version": "1.1.0"
|
|
52
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"artifactDownloadUrls": [
|
|
3
|
-
"https://github.com/ClaudioMascaro/meada/releases/download/v1.
|
|
3
|
+
"https://github.com/ClaudioMascaro/meada/releases/download/v1.1.0"
|
|
4
4
|
],
|
|
5
5
|
"bin": {
|
|
6
6
|
"meada": "run-meada.js"
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"zipExt": ".tar.xz"
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
|
-
"version": "1.
|
|
92
|
+
"version": "1.1.0",
|
|
93
93
|
"volta": {
|
|
94
94
|
"node": "18.14.1",
|
|
95
95
|
"npm": "9.5.0"
|