Answer design 39 bus-and-switch's questions by the principles, so it can ship on main as it is
mesh/check being carried to 8d772ad40c95 on 18639c73921a | from e1819e0a0d08
mesh/delivery merged: source.main moved to novox/hq@18639c73921a
mesh/check being carried to 8d772ad40c95 on 18639c73921a | from e1819e0a0d08
mesh/delivery merged: source.main moved to novox/hq@18639c73921a
The lead answered the four open questions against hq's principles on 2026-11-11: the vault serves licence secrets to the tool runner's account (principle 6), the operator's word replaces ADR 0183 §21 and ADR 0206 §16, §18 with no record, the store database stays one milestone as the carry-over's read-only source (principle 7, the delivery gap named), and the milestone no longer waits for design 47's G: its conditions are said on the operator channel as any module does today. Claude-Session: https://claude.ai/code/session_01NcbgfHb9d4T9nq5Q6WqdiJ
This commit is contained in:
@@ -44,32 +44,45 @@ readings, and never a session. Issue 1409 (no warning before a licence's limit)
|
||||
part, the main file and the glossary. The order between them is the module graph's, each an edge the delivery plan
|
||||
derives (§50).
|
||||
|
||||
**Open design questions.** Three questions no accepted record settles; the lines that wait on one say so, and develop
|
||||
does not start on them before the operator's answer is written here (playbook 02, *Answering a design question*):
|
||||
**The lead's answers, by the principles.** Four questions no accepted record settled were answered on 2026-11-11 by
|
||||
the lead of issue 1409, against hq's principles, as the operator's way of working since 21:15 that evening has it (the
|
||||
lead answers develop and design questions against the principles; no decision record is written for a detail). Each
|
||||
line they reach names its answer:
|
||||
|
||||
- **Q1, the vault's own-account rule.** ADR 0550 §3 moves the grants and tokens into the vault and waits until the
|
||||
licence manager and each agent module run under a bus account of their own (ADR 0349 §11, issue 569); both run
|
||||
today in their machine's tool runner, under its account. Lines §15 to §20 wait on it.
|
||||
- **Q2, the record behind the switch.** ADR 0183 §21 (moving a consumer is a person's act), ADR 0206 §16 (no
|
||||
`licence.switched` event) and ADR 0206 §18 (every later change is a person's act) are in force; the operator's
|
||||
answers of 2026-11-11 change them. A record changing those three clauses comes before develop (playbook 02,
|
||||
*Changing a decision*). Lines §24 to §33 are written from the operator's answers and wait on it.
|
||||
- **Q3, how the first start reads today's rows.** The carry-over (§11 to §14) reads the store database with the
|
||||
module's own credential, and a build whose manifest no longer requires `postgres-database` is composed with no
|
||||
credential for it, while a retiring provision keeps its account only until the operator's Approve (ADR 0570 §5).
|
||||
Line §11's source waits on it.
|
||||
- **A1, the vault's own-account rule** (§15 to §17). ADR 0550 §3 waits until the licence manager and each agent
|
||||
module run under a bus account of their own (ADR 0349 §11, issue 569); both run in their machine's tool runner,
|
||||
under its account. Settled by principle 6 (inside the mesh every agent and module is trusted; who may do what is not
|
||||
a question) and the operator's instruction that who may do what is not the operator's question (approved
|
||||
2026-11-11 21:15 CET): the vault serves licence secrets to the tool runner's account. Its check that the asking
|
||||
machine is the node the binding names stays, as a guard against mistakes, not as a permission.
|
||||
- **A2, the switch without a record** (§24 to §33). ADR 0183 §21 (moving a consumer is a person's act), ADR 0206 §16
|
||||
(no `licence.switched` event) and ADR 0206 §18 (every later change is a person's act) are in force. The operator
|
||||
decided, 2026-11-11 about 21:10 to 22:20 CET: switch automatically at 99 %, on all nodes, configurable; and said that
|
||||
night not to write records for what they decided. So the operator's answer of 2026-11-11 replaces those three
|
||||
clauses for this module, and no record is written.
|
||||
- **A3, how the first start reads today's rows** (§1, §11, §14). A build whose manifest no longer requires
|
||||
`postgres-database` is composed with no credential for it, and `mesh-store.query` refuses the grant column. Settled
|
||||
by principle 7 (the simplest thing that works): this milestone keeps `requires postgres-database`, and the `secret`
|
||||
`grant-key` that opens the grants at rest, as the carry-over's read-only source; dropping both, and the database's
|
||||
retirement on the operator's Approve, is the module's next change once `licences_carried` (§44) shows the
|
||||
carry-over done. The delivery gap this names: no path hands a build the credential of a provision its manifest
|
||||
dropped, so a module cannot read its retiring database at the first start of its new form.
|
||||
- **A4, no wait for design 47's milestone G** (§9, §50). Settled by principle 7: this milestone ships soon, built only
|
||||
on what main holds today (mesh-sdk, the vault, the operator channel, and today's way a module says a condition,
|
||||
ADR 0437 §9), so it uses neither G's writer nor its stream.
|
||||
|
||||
## The kept data on the bus
|
||||
|
||||
1. **The manifest asks for its kept data by template and requires no database.** `requires` loses
|
||||
`postgres-database` and the `secret` requirement `grant-key` (its key ends with the grants' move, §20); `contributes`,
|
||||
`binds`, `secrets`, the resources `database-url` and `prepare` and the run-once step `prepare` go with them. It
|
||||
gains one `bus-state` entry per piece of kept data, each in the typed long form (ADR 0595 §1): the local name, the
|
||||
storage type, the template and its options, as §2 to §9 list them. The old section `state` (today `bindings`) and
|
||||
the `reads` entry `claude-code.holdings` go: the module moves to the new form whole (ADR 0595 §11), keeping the local
|
||||
name `bindings` so its data stays. Why: the operator, 2026-11-11, and principle 1; the templates are ADR 0595 §2.
|
||||
How it is checked: the manifest validator accepts the manifest at the merge check; a catalog test finds no
|
||||
`postgres-database`, `reads` or old `state` in it.
|
||||
1. **The manifest asks for its kept data by template, and keeps its database only as the carry-over's source.**
|
||||
It gains one `bus-state` entry per piece of kept data, each in the typed long form (ADR 0595 §1): the local name,
|
||||
the storage type, the template and its options, as §2 to §9 list them. The old section `state` (today
|
||||
`bindings`) and the `reads` entry `claude-code.holdings` go: the module moves to the new form whole (ADR 0595 §11),
|
||||
keeping the local name `bindings` so its data stays. `requires` keeps `postgres-database` and the `secret`
|
||||
`grant-key`, with their `contributes`, `binds`, `secrets` and the resource `database-url`, read only by §11 and
|
||||
never written (A3); the run-once step `prepare`, which makes the tables, goes, since nothing writes them again.
|
||||
`uses` gains `operator-channel` (§9, §28). Why: the operator, 2026-11-11, and principle 1; the templates are
|
||||
ADR 0595 §2; A3. How it is checked: the manifest validator accepts the manifest at the merge check; a catalog test
|
||||
finds no `reads`, old `state` or `prepare` in it; a unit test finds no write to the database in the code.
|
||||
2. **State `licences`: one key per licence, no secret in it.** Template `state`, option `history` 1. The key is the
|
||||
licence's key form (§10). The value: `name`, `kind` (`subscription` or `api-key`), `account` (the account id),
|
||||
`address`, `organisation`, `refreshFingerprint`, `accessExpiresAt`, `refreshExpiresAt`, `failures` (refreshes
|
||||
@@ -118,14 +131,20 @@ does not start on them before the operator's answer is written here (playbook 02
|
||||
for each table of today's database the rows read and the entries written. Written once, at the end of §11; never
|
||||
deleted, since its presence is what keeps a second start from carrying over again. Why: ADR 0437 §7's way of
|
||||
carrying a module's state over once. How it is checked: §13.
|
||||
9. **State `findings` and the events `finding-events`.** The manager is an observer (design 47 G §383): it asks
|
||||
the state `findings` by the template `state` and the stream `finding-events` by the template `events`, with
|
||||
`max-age-days` 7 and `max-mib` 16, its field `events` listing `finding-written`, `finding-cleared` and
|
||||
`findings-stated`, as design 47 G §529 has every part that is not of the foundation declare them; `consumes`
|
||||
gains `mesh-conditions.findings-asked`. It writes its findings (§29 to §31, §33) with mesh-sdk's finding writer
|
||||
(design 47 G §461), which states them at its start and on the keeper's ask. Why: a condition reaches the operator
|
||||
only from a finding the condition keeper hears (ADR 0564 §2 as design 47 G §383 changes it). How it is checked:
|
||||
whole (manager), a finding written raises its condition at the keeper and its clearing clears it.
|
||||
9. **Its conditions are said on the operator channel, and kept in its own state `conditions`.** The manager says
|
||||
each condition of §11, §29 to §31 and §33 as any module does on main today (ADR 0437 §9): published on
|
||||
`mesh.seat.operator-channel.accept.condition.claude-licence-manager` (the subject the operator channel's contract
|
||||
names, through its client in mesh-contracts), carrying the condition's `key`, `severity` (`urgent` or `warning`),
|
||||
`scope` and `id`, `raised` or `cleared`, its `summary` and ADR 0253's words (`headline`, `needs`, `explanation`
|
||||
opened by its verdict, `resolved` and `actions`); the router alone decides whether and where the operator is told.
|
||||
It keeps each condition it said raised in its state `conditions` (template `state`, option `history` 1, one key per
|
||||
condition key, its body and `since`), written before it is said and deleted when it is said cleared. At its start
|
||||
it judges each kind anew from its own state and readings, says raised again each it still holds and cleared each
|
||||
key of `conditions` it no longer holds. A raise or clear is said again only when what it says changes (ADR 0289
|
||||
§1). Why: A4 (today's way a module raises a condition, without G's writer); the state lets a restart clear what
|
||||
it said. How it is checked: unit, a condition is written in `conditions` and published once with these fields;
|
||||
a start with a key it no longer holds says it cleared; whole (manager and the router on a test bus), a raised
|
||||
condition reaches the desk channel and its clearing clears it.
|
||||
10. **A licence's key form.** A licence's name is its account's address, or its id where it has none, or the name
|
||||
given at `adopt` for an API key (today's `NameFor`); a name holds characters a key may not. Its key is the name
|
||||
with each `@` written as `_at_` and every other character outside letters, digits, `-` and `.` written as `_`. An
|
||||
@@ -140,13 +159,13 @@ does not start on them before the operator's answer is written here (playbook 02
|
||||
`carried` has no key `postgres`, the manager, before it hears an event, reads a holding report, refreshes or reads
|
||||
usage, reads every row of the six tables of its database `claude_licences` and writes them as §12 says, then the
|
||||
key `carried.postgres`. Until that key exists it serves its read verbs and tools only, answering every other
|
||||
with "the carry-over has not ended", and writes the finding `carry-over-failed` (urgent; "Needs you: the licence
|
||||
with "the carry-over has not ended", and says the condition `carry-over-failed` (§9; urgent; "Needs you: the licence
|
||||
manager could not carry over its licences; no consumer is moved and no token is refreshed until it does", naming
|
||||
the table and the error) when a read or a write fails, cleared when the key is written. Why: a manager that ran on
|
||||
an empty state would take every holding report for a candidate (ADR 0206 §5) and exchange refresh tokens it
|
||||
already holds; failure is loud (principle 3). Its source, the database read with the module's own credential,
|
||||
waits on Q3. How it is checked: whole (manager, a store database filled from a copy of today's rows), every row
|
||||
is carried and the key written; with the database unreachable the finding is raised and no vendor call is made.
|
||||
already holds; failure is loud (principle 3). Its source is the database read with the module's own credential, which this milestone keeps for it alone (A3). How it is checked: whole (manager, a store database filled from a copy of today's rows), every row
|
||||
is carried and the key written; with the database unreachable the condition is raised and no vendor call is made. A table that does not
|
||||
exist (a fresh install) is read as no rows.
|
||||
12. **What each table becomes.**
|
||||
- `licence` → a key of `licences` (§2) per row; each row's `sealed` grant is opened with today's `grant.key` and
|
||||
handed to the vault as §15 says, its generation 1; the row's other columns become the key's fields.
|
||||
@@ -165,44 +184,48 @@ does not start on them before the operator's answer is written here (playbook 02
|
||||
log's last entry for that subject and skips rows at or before its `at`). Why: ADR 0437 §7. How it is checked:
|
||||
whole, a second start writes nothing; a start stopped after `bindings` and started again leaves every count equal
|
||||
to the rows read.
|
||||
14. **The database's fate, and that this delivery moves data.** Once `carried.postgres` exists and §20's scan finds
|
||||
no grant left outside the vault, the database `claude_licences` is retired: the provision is retiring once no
|
||||
build a want names requires it, and its account is removed only on the operator's Approve (ADR 0570 §5); the
|
||||
database is renamed aside by the postgres module's `postgres_retire_database` and deleted only by a person
|
||||
(ADR 0230 §14). The delivery of this milestone moves data, so it is said to the operator before it starts
|
||||
14. **The database's fate, and that this delivery moves data.** In this milestone the database `claude_licences`
|
||||
stays, read once by §11 and never written. Once `licences_carried` (§44) shows the carry-over done and §20's scan
|
||||
finds no grant left outside the vault, the module's next change drops `requires postgres-database` and
|
||||
`grant-key`; the provision is then retiring, its account removed only on the operator's Approve (ADR 0570 §5),
|
||||
and the database is renamed aside by the postgres module's `postgres_retire_database` and deleted only by a
|
||||
person (ADR 0230 §14). The delivery of this milestone moves data, so it is said to the operator before it starts
|
||||
(which module, its machine, that its licences, bindings and grants move to the bus and the vault), and the
|
||||
manager's machine is backed up before the module moves, as for every module that holds data. Why: ADR 0570 §5,
|
||||
ADR 0230; the operator's instruction that a delivery moving data is said first. How it is checked: the delivery's
|
||||
plan names the backup; after the delivery `mesh-store.databases` lists `claude_licences` until the operator's
|
||||
Approve, then its renamed-aside form.
|
||||
manager's machine is backed up before the module moves, as for every module that holds data. Why: A3,
|
||||
ADR 0570 §5, ADR 0230; the operator's instruction that a delivery moving data is said first. How it is checked:
|
||||
the delivery's plan names the backup; after the delivery `mesh-store.databases` still lists `claude_licences`.
|
||||
|
||||
## Grants and tokens in the vault
|
||||
|
||||
*Lines §15 to §20 build ADR 0550 §3 and wait on Q1.*
|
||||
*Lines §15 to §20 build ADR 0550 §3, on the tool runner's account (A1).*
|
||||
|
||||
15. **The vault holds two secrets per licence, handed to it by the manager alone.** `licence.<licence key>.grant`
|
||||
holds, for a subscription, the refresh token, the access token and both expiries, and for an API key the key;
|
||||
`licence.<licence key>.access` holds a subscription's access token and its expiry, or the API key. The vault
|
||||
serves a new verb on its seat, `licence-put`: `licence`, `part` (`grant` or `access`), `generation` and the value
|
||||
sealed to the vault's key; callable by the holder of the seat `anthropic-licence-manager` only, refused for any
|
||||
other caller naming the rule; it keeps the newest generation and refuses an older one; and it says its event
|
||||
sealed to the vault's key; answered for a call from the machine that holds the seat `anthropic-licence-manager`
|
||||
(as the seat's placement names it), and refused from any other machine naming the rule, a guard against a
|
||||
mistaken caller rather than a permission (A1); it keeps the newest generation and refuses an older one; and it says its event
|
||||
`secret-changed` with the secret's name and generation, never the value. The manager calls it after every
|
||||
adoption, rotation and carry-over (§12), the `grant` first. Why: ADR 0550 §3 (the manager hands what it gets back
|
||||
to the vault, keeping nothing at rest); the names, the verb and its fields are the design's choice. How it is
|
||||
checked: unit (vault), `licence-put` from another caller is refused and an older generation is refused; unit
|
||||
checked: unit (vault), `licence-put` from another machine is refused and an older generation is refused; unit
|
||||
(manager), a rotation calls it twice and keeps no token after the call returns.
|
||||
16. **The manager reads a grant from the vault when it needs one, and keeps it only for that act.** To refresh,
|
||||
adopt again or read usage, the manager calls `mesh-vault.get` for `licence.<licence key>.grant`, sealed to its own
|
||||
public key, under the licence's lease (§4), and drops the value when the act ends. Why: ADR 0550 §3, ADR 0183 §6.
|
||||
How it is checked: unit, the manager's state and log hold no token after a rotation.
|
||||
17. **A consumer's agent module gets its token from the vault, which serves only the licence a binding names.** The
|
||||
vault answers `mesh-vault.get` for `licence.<licence key>.access` only to an asker whose machine is the node the
|
||||
asking consumer's binding names, and only for the licence that binding names. It learns the bindings from the
|
||||
vault answers `mesh-vault.get` for `licence.<licence key>.access` to the tool runner's account of a machine,
|
||||
and only when that machine is the node the asking consumer's binding names and the licence is the one that
|
||||
binding names: a guard against a node applying another's licence by mistake, not a permission (A1). Its rule that a
|
||||
value goes only to a module under a bus account of its own (ADR 0349 §11) does not apply to the licence
|
||||
secrets. It learns the bindings from the
|
||||
manager's `binding.changed` (§21), which it consumes, and at its start from `anthropic-licence-manager.bindings`,
|
||||
its first view, keeping them in its own state `licence-bindings` (template `state`, one key per consumer, the
|
||||
licence and generation, deleted on a release). The agent module never receives a refresh token (ADR 0206 §11).
|
||||
Why: ADR 0550 §3; ADR 0596 §2 (the vault asks the manager, never reads its state); the state is the design's
|
||||
choice. How it is checked: unit (vault), a token is refused to a machine no binding names and for a licence its
|
||||
Why: ADR 0550 §3; A1; ADR 0596 §2 (the vault asks the manager, never reads its state); the state is the
|
||||
design's choice. How it is checked: unit (vault), a token is refused to a machine no binding names and for a licence its
|
||||
binding does not name (the main file's acceptance test), and given after a `binding.changed` naming it.
|
||||
18. **`current` answers which licence and its health, never the token.** The seat verb `current` takes `consumer`
|
||||
and answers `licence`, `kind`, `generation`, `health` (`ok`, `failing`, `expiring`) and `accessExpiresAt`; its
|
||||
@@ -248,7 +271,8 @@ does not start on them before the operator's answer is written here (playbook 02
|
||||
|
||||
## Switching on usage
|
||||
|
||||
*Lines §24 to §33 are the operator's answers of 2026-11-11 and wait on Q2's record.*
|
||||
*Lines §24 to §33 are the operator's answer of 2026-11-11, which replaces ADR 0183 §21 and ADR 0206 §16 and §18 for
|
||||
this module (A2): the manager moves consumers by itself on its own readings, and says every switch as an event.*
|
||||
|
||||
24. **The manager reads each subscription's usage on a cadence, and keeps the reset times.** Every
|
||||
`usage_minutes` (§33), for each licence of kind `subscription`, the manager reads the vendor's usage with the
|
||||
@@ -283,33 +307,32 @@ does not start on them before the operator's answer is written here (playbook 02
|
||||
says it once per act that moves consumers, by `switch` or by §26: `from`, `to`, `consumers` (the list), `by` (a
|
||||
person's name or `usage`), `why`, and for §26 `window` (`session` or `weekly`), `pct`, `resetsAt` and the target's
|
||||
`room`. The same fields are the log entry `switched` (§7). Why: the operator, 2026-11-11 ("each switch said as an
|
||||
event"); this changes ADR 0206 §16 (Q2). How it is checked: unit, one event per act with the consumers moved.
|
||||
event"); this replaces ADR 0206 §16 for this module (A2). How it is checked: unit, one event per act with the consumers moved.
|
||||
28. **Every switch the manager makes is told to the operator.** After an act of §26 the manager calls
|
||||
`operator-channel.tell` (ADR 0380): `subject` the seat `anthropic-licence-manager`, `headline` "<consumers> moved
|
||||
to <target>", and one line "<L> reached <pct> % of its <window> window, which resets at <time>; <target> has
|
||||
<room> % room". A switch by the `switch` verb is not told, since the person who asked knows it. Why: the operator,
|
||||
2026-11-11 ("told to the operator"); a notice that needs no answer is `tell` (ADR 0380). How it is checked: unit,
|
||||
§26 calls `tell` once per act, and `switch` never.
|
||||
29. **No licence with room is a condition, cleared when one has room.** When §26 finds no target for L, the
|
||||
manager writes the finding `licence.<L key>.no-room`, severity `urgent`: headline "No licence has room for
|
||||
29. **No licence with room is a condition, cleared when one has room.** When §26 finds no target for L, the manager says the condition `licence.<L key>.no-room` (§9), severity `urgent`: headline "No licence has room for
|
||||
<consumers>"; "Needs you: add a licence, or wait for <L>'s <window> window to reset at <time>"; explanation
|
||||
naming L's percentage and every other licence's room or why it is not known; resolved line "<consumers> have a
|
||||
licence with room again". The manager clears it when a later reading of L falls below `switch_at_pct`, when its
|
||||
consumers have moved (a target was found at a later reading of L), or when L has no consumer left. Why: the
|
||||
operator, 2026-11-11 ("a condition when no licence has room, cleared when one has"); its words are ADR 0253's. How
|
||||
it is checked: unit, with every licence at its threshold the finding is written once and cleared by a reading with
|
||||
room; whole, the condition keeper raises and clears it.
|
||||
it is checked: unit, with every licence at its threshold the condition is said once and cleared by a reading with
|
||||
room; whole, the router takes its raise and its clearing.
|
||||
30. **A licence near its limit is warned about once per crossing.** When a licence with one or more consumers has
|
||||
`sessionPct` or `weeklyPct` at or above `warn_at_pct`, the manager writes the finding `licence.<L key>.near-limit`,
|
||||
`sessionPct` or `weeklyPct` at or above `warn_at_pct`, the manager says the condition `licence.<L key>.near-limit` (§9),
|
||||
severity `warning`: headline "<L> at <pct> % of its <window> window"; "Nothing for you to do: <consumers> move to
|
||||
<target> at <switch_at_pct> %" while §26 would find a target, "Needs you: no other licence has room" when it would
|
||||
not; explanation naming the reset time. It is written again only when what it says changes (ADR 0289 §1), and
|
||||
cleared when both windows read below `warn_at_pct`. Why: issue 1409 (a warning before the limit, cleared when the
|
||||
reading falls back); ADR 0183 §21 (warned once per crossing of a declared threshold). How it is checked: unit, a
|
||||
rising series of readings writes the finding once at the crossing and again only when its words change; a reading
|
||||
rising series of readings says the condition once at the crossing and again only when its words change; a reading
|
||||
after the reset clears it.
|
||||
31. **A licence whose usage cannot be read is a condition.** When a subscription's latest successful reading is
|
||||
older than `usage_stale_minutes`, the manager writes the finding `licence.<L key>.usage-unread`, severity
|
||||
older than `usage_stale_minutes`, the manager says the condition `licence.<L key>.usage-unread` (§9), severity
|
||||
`warning`, naming the vendor's reason from `readError`; "Needs you" only when consumers are bound to it ("<consumers>
|
||||
are not moved off it while its usage is unknown"); cleared at its next successful reading. Why: principle 3 (a
|
||||
switch that cannot see must say so). How it is checked: unit, three failed readings in a row write it, one
|
||||
@@ -325,8 +348,7 @@ does not start on them before the operator's answer is written here (playbook 02
|
||||
`warn_at_pct` (number 1 to 100, default 90), `usage_minutes` (number 1 to 60, default 5) and
|
||||
`usage_stale_minutes` (number 5 to 180, default 15). The settings file's template and its `takes-config` list
|
||||
them beside today's five. A person sets one with `mesh-config.config`, at mesh or node level. When
|
||||
`room_below_pct` is above `switch_at_pct` or `warn_at_pct` is above `switch_at_pct`, the manager writes the finding
|
||||
`settings.<setting>.invalid` (warning, naming both values) and moves no consumer until it is fixed. Each change of
|
||||
`room_below_pct` is above `switch_at_pct` or `warn_at_pct` is above `switch_at_pct`, the manager says the condition `settings.<setting>.invalid` (§9) (warning, naming both values) and moves no consumer until it is fixed. Each change of
|
||||
a value it reads is written in the audit under `settings`. Why: the operator, 2026-11-11 ("make it configurable";
|
||||
99 by default); 90 as the room and the warning is the value the session used that night, so a licence warned
|
||||
about is never a target; the bounds are the design's choice. How it is checked: unit, each setting's default and
|
||||
@@ -340,7 +362,7 @@ tool's effect (the manifest's mark).*
|
||||
34. **`licences` shows every licence with its latest usage and room.** The seat verb answers, per licence, today's
|
||||
fields (`name`, `kind`, `identity`, expiries, `failures`, the consumers bound to it) and gains `usage` (§2's
|
||||
latest reading with its reset times and `readAt`), `room` (§25, or why it is not known), `target` (whether §26
|
||||
would choose it now), `adoptedAt`, `adoptedFrom` and the open findings about it. Reads. Why: principle 4; the
|
||||
would choose it now), `adoptedAt`, `adoptedFrom` and the open conditions about it. Reads. Why: principle 4; the
|
||||
operator's "read every licence with its latest usage and reset times". How it is checked: unit, an answer per
|
||||
licence carries each field and no token.
|
||||
35. **`usage` answers the history.** The seat verb takes `licence` (absent for all), `since` (a time) and `limit`
|
||||
@@ -365,13 +387,12 @@ tool's effect (the manifest's mark).*
|
||||
`key`; answers each entry's keys (or the subjects of a log with their last entry), one key's value, and its fill
|
||||
against its cap. Reads. Why: principle 4 and ADR 0564 §12 (a read of every bucket and stream a part keeps). How it
|
||||
is checked: a unit test lists every bus-state entry of the manifest and fails on one this tool does not read.
|
||||
42. **`licences_resync`: recover.** Says `binding.changed` again for every binding, `holdings.asked`, states its
|
||||
findings again through the writer (§9) and calls `licence-put` again for each licence's `access` (§15); answers
|
||||
42. **`licences_resync`: recover.** Says `binding.changed` again for every binding, `holdings.asked`, says again each condition it holds (§9) and calls `licence-put` again for each licence's `access` (§15); answers
|
||||
what it said. Acts. Why: principle 4 (everything that goes wrong is recovered through its tools): a consumer, the
|
||||
vault or the keeper that missed an event converges on one call. How it is checked: unit, each is said once per
|
||||
binding, licence and finding.
|
||||
43. **`licences_findings`: its findings.** Answers each key of `findings` with its fields and `since`. Reads. Why:
|
||||
design 47 G §383 (the owner's read tool shows its newest findings). How it is checked: unit.
|
||||
binding, licence and condition.
|
||||
43. **`licences_conditions`: the conditions it said.** Answers each key of `conditions` (§9) with its body and
|
||||
`since`. Reads. Why: principle 4 (everything it holds can be read). How it is checked: unit.
|
||||
44. **`licences_carried`: the carry-over's record, and its recovery.** Answers `carried.postgres` (§8), or, when it
|
||||
is absent, the failure `carry-over-failed` names; with `run` true and the key absent, starts §11 again; refused
|
||||
with the key present. Acts with `run`, reads without. How it is checked: unit, `run` is refused once carried.
|
||||
@@ -405,10 +426,10 @@ tool's effect (the manifest's mark).*
|
||||
instruction on designing a change as it should be).
|
||||
50. **The order is the module graph's.** The edges the delivery plan derives: the bus module creates the manager's
|
||||
bus-state entries before its want moves (the provision `bus-state`, ADR 0570 §4); `mesh-vault`'s `licence-put`
|
||||
and its consumption of `binding.changed` are served before the manager calls them (a served verb); `claude-code`
|
||||
declares `holdings-changed` before the manager consumes it (ADR 0595 §11's judge refuses the reverse); and the
|
||||
finding writer of mesh-sdk and the keeper's consumption of `finding-events` are design 47's milestone G, delivered
|
||||
before this one. A node's agent keeps its last token, which lives hours, while the manager moves.
|
||||
and its consumption of `binding.changed` are served before the manager calls them (a served verb); and `claude-code`
|
||||
declares `holdings-changed` before the manager consumes it (ADR 0595 §11's judge refuses the reverse). It waits for
|
||||
no other milestone: the operator channel's condition subject, the vault and mesh-sdk are used as main holds them
|
||||
(A4). A node's agent keeps its last token, which lives hours, while the manager moves.
|
||||
51. **Tonight's interim rule ends with the delivery.** Until the delivery, a session follows the readings and calls
|
||||
`switch` (issue 1409's note of 2026-11-11 20:11); once §26 runs live, that session stops, said on issue 1409.
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ which is the consumer on every node.
|
||||
## Every grant in the vault
|
||||
|
||||
*Decided 2026-10-26 (ADR 0550 §3), on the operator's answer that every secret lives in the vault; built in milestone
|
||||
bus-and-switch (§15 to §20 of its part, [39-milestones/bus-and-switch.md](39-milestones/bus-and-switch.md)), which
|
||||
waits on the vault's own-account rule (its open question Q1). Until it is delivered, the sections below hold as first
|
||||
written.*
|
||||
bus-and-switch (§15 to §20 of its part, [39-milestones/bus-and-switch.md](39-milestones/bus-and-switch.md)), the vault
|
||||
serving the licence secrets to the tool runner's account (principle 6; its answer A1). Until it is delivered, the
|
||||
sections below hold as first written.*
|
||||
|
||||
- **The vault keeps every licence's secrets**, two per licence: its grant, and the access token a consumer is handed
|
||||
([24](24-the-secrets-vault.md)). The manager keeps its bindings and its licence records without a secret.
|
||||
@@ -61,7 +61,8 @@ the seat's verbs. It has no port, no route, no file under anyone's home.
|
||||
|
||||
**It keeps all its data in the bus's own storage, by the kept-data templates, and requires no database**
|
||||
(ADR 0595, ADR 0596; the operator, 2026-11-11). Built in milestone bus-and-switch (§1 to §14 of its part), which carries
|
||||
today's store database over at the manager's first start and then retires it on the operator's Approve:
|
||||
today's store database over at the manager's first start, reading it and never writing it; the module's next change
|
||||
drops the database, which is then retired on the operator's Approve (principle 7; its answer A3):
|
||||
|
||||
| entry | template | holds |
|
||||
|---|---|---|
|
||||
@@ -72,7 +73,7 @@ today's store database over at the manager's first start and then retires it on
|
||||
| `usage` | log | every reading, raw beside normalised (ADR 0054), 35 days |
|
||||
| `audit` | log | every adoption, refusal, rotation, binding, switch and setting, kept for good |
|
||||
| `carried` | state | the record of the one carry-over from the store database |
|
||||
| `findings` and `finding-events` | state and events | its findings for the condition keeper |
|
||||
| `conditions` | state | each condition it said on the operator channel, so a restart can clear what it said |
|
||||
|
||||
Every grant and token is the vault's (*Every grant in the vault*, above). Until the milestone is delivered, the
|
||||
module keeps its store database, its tables `licence` (with the grant encrypted at rest with a key the vault made for
|
||||
@@ -146,8 +147,8 @@ it as it writes the operator's — the predecessor ran its agents exactly so.
|
||||
**Binding is a person's act through the seat's verbs**, listed by the mesh MCP server: `bind`, `switch`,
|
||||
`release`. **A licence at its threshold is left by the manager itself** (the operator, 2026-11-11: "when the current
|
||||
licence usage is at 99%, switch to a licence that still has room", "on all nodes"; built in milestone
|
||||
bus-and-switch, §24 to §33, which waits on a record changing ADR 0183 §21 and ADR 0206 §16, §18, its open question
|
||||
Q2): on each of its own usage readings, a licence whose session or weekly usage reaches the threshold (99 % by
|
||||
bus-and-switch, §24 to §33; the operator's answer replaces ADR 0183 §21 and ADR 0206 §16, §18 for this module, with
|
||||
no record, its answer A2): on each of its own usage readings, a licence whose session or weekly usage reaches the threshold (99 % by
|
||||
default) has every consumer bound to it moved to the licence with the most room; each switch is said as
|
||||
`licence.switched` and told to the operator; a licence near its limit is warned about once per crossing, and no
|
||||
licence with room is a condition until one has room. The threshold, the room a target needs and the switch itself
|
||||
@@ -194,8 +195,8 @@ report, and adopted by refreshing it.
|
||||
the audit logger records them all. *2026-10-04 (ADR 0206):* `licence.rotated` and `licence.switched` are
|
||||
gone; a rotation or a switch is a new generation in the `bindings` state. *Milestone bus-and-switch:*
|
||||
`binding.changed` says every change of a binding, `licence.switched` comes back for every switch, `holdings.asked`
|
||||
asks every node's agent module for its holdings, and its findings go on `finding-events` (§9, §21, §23, §27 of its
|
||||
part).
|
||||
asks every node's agent module for its holdings, and its conditions are said on the operator channel (§9, §21, §23,
|
||||
§27 of its part).
|
||||
|
||||
**State**: `bindings`, which it keeps; the agent module's `holdings`, which it reads. *Milestone bus-and-switch:*
|
||||
the kept data of §1, and no other module's state read: the agent module's holdings are heard as its
|
||||
@@ -209,8 +210,8 @@ consumer sends — ADR 0206; from ADR 0550 §3, which licence a consumer holds a
|
||||
token, which the vault hands). The manager asks a node for a candidate grant by the agent module's own tool.
|
||||
|
||||
The module's own tools beside the verbs (milestone bus-and-switch, §37 to §44): every switch and why, the settings
|
||||
in force, usage read now, the switch rule applied now, every entry it keeps with its fill, its findings, the
|
||||
carry-over's record, and one recovery that says every binding, finding and access token again.
|
||||
in force, usage read now, the switch rule applied now, every entry it keeps with its fill, the conditions it
|
||||
said, the carry-over's record, and one recovery that says every binding, condition and access token again.
|
||||
|
||||
## 8. Settings
|
||||
|
||||
@@ -242,7 +243,7 @@ through mesh-config.
|
||||
| Milestone | What it builds | State |
|
||||
|---|---|---|
|
||||
| the manager and its consumer | §1 to §8 as first written: the module, its seat, its store database, adoption and the sealed hand-over (ADR 0183, ADR 0206, ADR 0209) | delivered; built as design [40](40-building-the-operators-agent-and-its-licence-manager.md) describes |
|
||||
| bus-and-switch | its kept data on the bus and its store database carried over and retired; every grant and token in the vault (ADR 0550 §3); bindings and holdings as events; the switch on its own usage, its warning and its condition; its tools. mesh-catalog's `claude-licence-manager`, `claude-code` and `mesh-vault` in one pull request, after design 47's milestone G | designed; its part is [`39-milestones/bus-and-switch.md`](39-milestones/bus-and-switch.md), with three open questions (Q1 to Q3) |
|
||||
| bus-and-switch | its kept data on the bus and its store database carried over and retired; every grant and token in the vault (ADR 0550 §3); bindings and holdings as events; the switch on its own usage, its warning and its condition; its tools. mesh-catalog's `claude-licence-manager`, `claude-code` and `mesh-vault` in one pull request, on what main holds today | designed; its part is [`39-milestones/bus-and-switch.md`](39-milestones/bus-and-switch.md), its four questions answered by the principles (A1 to A4) |
|
||||
|
||||
## What this does not settle
|
||||
|
||||
|
||||
Reference in New Issue
Block a user