@bongos/core 1.19.891 → 1.19.893
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/.bongos-core.json +24 -19
- package/docs/adr/0296-a-role-speciality-is-data-over-three-existing-surfaces.md +1 -1
- package/docs/adr/0310-a-speciality-offers-skills-and-the-adopter-chooses-them.md +81 -0
- package/docs/adr/README.md +1 -0
- package/docs/module-api-changelog.md +4 -0
- package/modules/government/protected-surfaces.json +6 -1
- package/modules/specialities/migrations/specialities_001_specialities.sql +1 -1
- package/modules/specialities/specialities.js +7 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/agents_sync.mjs +17 -2
- package/tests/government_protected_surfaces.mjs +51 -5
- package/tests/module-scope-map.mjs +10 -2
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.893",
|
|
6
|
+
"core_contract": "1.19.893",
|
|
7
|
+
"source_commit": "99f7250a5cc9e39e16d1869281d1fd2660a466ad",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-19T22:26:15.995Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 498,
|
|
13
13
|
"agent_docs_stubbed": 26,
|
|
14
14
|
"functional_verbatim": 2272,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2796,
|
|
20
|
+
"tree_sha256": "1ffd95b16bdf5a9986639f6ea5452817996a270f6274f1195d3330fc915fe900",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -1972,7 +1972,7 @@
|
|
|
1972
1972
|
{
|
|
1973
1973
|
"path": "docs/adr/0296-a-role-speciality-is-data-over-three-existing-surfaces.md",
|
|
1974
1974
|
"mode": "0000644",
|
|
1975
|
-
"sha256": "
|
|
1975
|
+
"sha256": "894a648aef61f34914e8ec9040fd241d012ce089f9a32c9fbc9e302c297e8912"
|
|
1976
1976
|
},
|
|
1977
1977
|
{
|
|
1978
1978
|
"path": "docs/adr/0297-a-tasks-creator-may-retire-it.md",
|
|
@@ -2039,10 +2039,15 @@
|
|
|
2039
2039
|
"mode": "0000644",
|
|
2040
2040
|
"sha256": "2aabad9f28113f3e6087b4e850d46a232aaa03c68ce9e793d04b2263e06998f5"
|
|
2041
2041
|
},
|
|
2042
|
+
{
|
|
2043
|
+
"path": "docs/adr/0310-a-speciality-offers-skills-and-the-adopter-chooses-them.md",
|
|
2044
|
+
"mode": "0000644",
|
|
2045
|
+
"sha256": "9c8eff827f7c676cb8fb73475383812746d31c9ca35e2ec4ccb3845c48d2cd63"
|
|
2046
|
+
},
|
|
2042
2047
|
{
|
|
2043
2048
|
"path": "docs/adr/README.md",
|
|
2044
2049
|
"mode": "0000644",
|
|
2045
|
-
"sha256": "
|
|
2050
|
+
"sha256": "ca64ff12d272ebf2a1b7f6752193ad464c6b7c03118c6f9555a58d8b4a0f6e0c"
|
|
2046
2051
|
},
|
|
2047
2052
|
{
|
|
2048
2053
|
"path": "docs/api-reference.md",
|
|
@@ -2607,7 +2612,7 @@
|
|
|
2607
2612
|
{
|
|
2608
2613
|
"path": "docs/module-api-changelog.md",
|
|
2609
2614
|
"mode": "0000644",
|
|
2610
|
-
"sha256": "
|
|
2615
|
+
"sha256": "adada9ba37f56cd06bbb497540a86710002658b4160a9c0e31f5a3ae36fb1ddb"
|
|
2611
2616
|
},
|
|
2612
2617
|
{
|
|
2613
2618
|
"path": "docs/modules-contract.md",
|
|
@@ -4372,7 +4377,7 @@
|
|
|
4372
4377
|
{
|
|
4373
4378
|
"path": "modules/government/protected-surfaces.json",
|
|
4374
4379
|
"mode": "0000644",
|
|
4375
|
-
"sha256": "
|
|
4380
|
+
"sha256": "3c70a4abe22d8720a57da14e1f6dbc659cc57b066c1c811c8130f20d6d586e20"
|
|
4376
4381
|
},
|
|
4377
4382
|
{
|
|
4378
4383
|
"path": "modules/government/ranks.js",
|
|
@@ -7212,7 +7217,7 @@
|
|
|
7212
7217
|
{
|
|
7213
7218
|
"path": "modules/specialities/migrations/specialities_001_specialities.sql",
|
|
7214
7219
|
"mode": "0000644",
|
|
7215
|
-
"sha256": "
|
|
7220
|
+
"sha256": "21532391b179024f92eee8589576f612ed786b93e8ba69b817069c3ecc1fa393"
|
|
7216
7221
|
},
|
|
7217
7222
|
{
|
|
7218
7223
|
"path": "modules/specialities/migrations/specialities_002_offered.sql",
|
|
@@ -7232,7 +7237,7 @@
|
|
|
7232
7237
|
{
|
|
7233
7238
|
"path": "modules/specialities/specialities.js",
|
|
7234
7239
|
"mode": "0000644",
|
|
7235
|
-
"sha256": "
|
|
7240
|
+
"sha256": "4d6d29e4391ab1dcc6659e88ec858dc0276aa9f2b0e6d84eafa1b9f65c04d9be"
|
|
7236
7241
|
},
|
|
7237
7242
|
{
|
|
7238
7243
|
"path": "modules/status-ui/CLAUDE.md",
|
|
@@ -7707,12 +7712,12 @@
|
|
|
7707
7712
|
{
|
|
7708
7713
|
"path": "package-lock.json",
|
|
7709
7714
|
"mode": "0000644",
|
|
7710
|
-
"sha256": "
|
|
7715
|
+
"sha256": "4439b27f55c654d0758f704b1225fb25509949f048e9292b5c54af9c047c59a2"
|
|
7711
7716
|
},
|
|
7712
7717
|
{
|
|
7713
7718
|
"path": "package.json",
|
|
7714
7719
|
"mode": "0000644",
|
|
7715
|
-
"sha256": "
|
|
7720
|
+
"sha256": "3c2e6c91f071304b4464dfa845786eb5037631d1dff4cdad6c9560201dfc9bb6"
|
|
7716
7721
|
},
|
|
7717
7722
|
{
|
|
7718
7723
|
"path": "public-docs/index.html",
|
|
@@ -9717,7 +9722,7 @@
|
|
|
9717
9722
|
{
|
|
9718
9723
|
"path": "src/module-api.js",
|
|
9719
9724
|
"mode": "0000644",
|
|
9720
|
-
"sha256": "
|
|
9725
|
+
"sha256": "f5e01142842ee5a6d8c2cb8870eb8bd41e9de63d4e3621a07a69fc2c095597c7"
|
|
9721
9726
|
},
|
|
9722
9727
|
{
|
|
9723
9728
|
"path": "src/module-loader/catalog.js",
|
|
@@ -9892,7 +9897,7 @@
|
|
|
9892
9897
|
{
|
|
9893
9898
|
"path": "tests/agents_sync.mjs",
|
|
9894
9899
|
"mode": "0000644",
|
|
9895
|
-
"sha256": "
|
|
9900
|
+
"sha256": "2e161bf9ef98425974d8295c8227688cd6fde55314fb3b9413df55542d5a109a"
|
|
9896
9901
|
},
|
|
9897
9902
|
{
|
|
9898
9903
|
"path": "tests/agents_validate.mjs",
|
|
@@ -11397,7 +11402,7 @@
|
|
|
11397
11402
|
{
|
|
11398
11403
|
"path": "tests/government_protected_surfaces.mjs",
|
|
11399
11404
|
"mode": "0000644",
|
|
11400
|
-
"sha256": "
|
|
11405
|
+
"sha256": "fd0cca1a62b8c2ab3bd556aabf4f6949bb6affb66c2fb1fd6258bccfffebc744"
|
|
11401
11406
|
},
|
|
11402
11407
|
{
|
|
11403
11408
|
"path": "tests/government_rank_sync.mjs",
|
|
@@ -12197,7 +12202,7 @@
|
|
|
12197
12202
|
{
|
|
12198
12203
|
"path": "tests/module-scope-map.mjs",
|
|
12199
12204
|
"mode": "0000644",
|
|
12200
|
-
"sha256": "
|
|
12205
|
+
"sha256": "d55b193ee9dcba85a9d51f108352de17f453dfb413b9b391028c9d8f7ff7fab0"
|
|
12201
12206
|
},
|
|
12202
12207
|
{
|
|
12203
12208
|
"path": "tests/module_api.mjs",
|
|
@@ -27,7 +27,7 @@ The owner's phrasing is that specialities "are more or less MODULES", and the pu
|
|
|
27
27
|
|---|---|
|
|
28
28
|
| craft → playbook | `discipline-modes.json` |
|
|
29
29
|
| the per-session interaction contract | `behavior_prefs.interaction` → `GET /me` → the Conductor hook |
|
|
30
|
-
| declared skills |
|
|
30
|
+
| declared skills | **not built by this ADR** — see [ADR 0310](0310-a-speciality-offers-skills-and-the-adopter-chooses-them.md), which decides the shape (a speciality OFFERS skills; the adopter enables them one at a time, walked through each) and leaves the build to its own tasks |
|
|
31
31
|
| **the knowledge bundle** | **new — `speciality_documents` + `speciality_learnings`** |
|
|
32
32
|
|
|
33
33
|
Only the fourth is new machinery. In particular **a speciality contract rides the existing injection path rather than adding a second one**: `GET /me` gains a slice, the Conductor relays it, and the composition stays server-side exactly as it does for the interaction profile.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# ADR 0310 — A speciality OFFERS skills; the adopter turns them on, one at a time, having been shown what each does
|
|
2
|
+
|
|
3
|
+
**Status:** accepted · **Date:** 2026-09-19 · **Task:** [#1003978](https://cloudbongos.com/builders#/task/1003978) · **Goal:** 1000095 · **Supersedes nothing; completes [ADR 0296](0296-a-role-speciality-is-data-over-three-existing-surfaces.md)**
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
[ADR 0296](0296-a-role-speciality-is-data-over-three-existing-surfaces.md) defines a speciality as *"a named bundle of knowledge, skills and an interaction contract that sits UNDER a craft"*, and names three ingredients over three existing surfaces. **Two of the three shipped.** `contract_md` is the interaction contract; `specialities_documents` + `specialities_learnings` are the knowledge.
|
|
8
|
+
|
|
9
|
+
The third never existed. There is no skills column, no skills field on the draft, nothing in `validateDraft`, nothing in the routes, nothing in the injected contract. `grep -i skill` across the module's schema, domain and routes returns only the header sentences describing a thing that is not there — in the ADR, in the migration header, in `specialities.js`, and in the module's `CLAUDE.md` table.
|
|
10
|
+
|
|
11
|
+
That is prose asserting behaviour the code does not have, and it is worse here than usual because it is the feature's own definition. Task 1003978 filed it, and deliberately withheld an implementation: *"Do NOT start by adding a `skills` column. Decide (2) first"* — (2) being whether naming a skill grants anything.
|
|
12
|
+
|
|
13
|
+
**Why it matters rather than being a doc typo.** The point of a speciality is that an expert's way of working arrives in a session. Two-thirds of that is context and tone; the third is *what the session can DO*. An economics speciality that brings a costing skill is materially different from one that only changes how Claude talks. Without it, a speciality changes framing and never capability.
|
|
14
|
+
|
|
15
|
+
## The owner's ruling (2026-09-19)
|
|
16
|
+
|
|
17
|
+
Asked whether specialities should carry skills at all, and if so how:
|
|
18
|
+
|
|
19
|
+
> *"Specialities should carry skills but these should be user based, giving people edges in the beginning and then eventually being able to share these specialities with others. But also, if say two people are economist ideator specialists, then they decide which skills they want to use and which are relevant to them. It does not become super blanketed."*
|
|
20
|
+
|
|
21
|
+
Asked whether an adopted speciality's skills should arrive switched on or off:
|
|
22
|
+
|
|
23
|
+
> *"They should be off, and you should walk through what comes with each, people need to learn the system and understand things instead of preventing learning."*
|
|
24
|
+
|
|
25
|
+
Two rulings, and the second is the sharper one. It is not a safe default chosen to avoid surprise — it is a **position about learning**. The moment of adoption is the moment a person is most willing to find out what a tool does, and handing them a working toolkit silently spends that moment. Being walked through each skill and choosing it is how the system gets understood.
|
|
26
|
+
|
|
27
|
+
## Decision
|
|
28
|
+
|
|
29
|
+
### 1. A speciality DECLARES candidate skills. It grants nothing.
|
|
30
|
+
|
|
31
|
+
This answers task 1003978's question (2), and the answer is the strong one: **naming a skill can never confer it.** Skills are materialised into a checkout by other machinery (`claude-materialize.js`), and permission is rank-gated server-side with no caching ([ADR 0016](<redacted>.md)). A speciality may say *"in this speciality, reach for `/recall` and `/status` first"*; it may not hand anyone a capability they did not already have.
|
|
32
|
+
|
|
33
|
+
The task warned that a column *"would imply an authority it must not have"*. It does not, given point 2 — because the declaration is a menu, and a menu is not an entitlement. What makes that safe is not the storage shape but the fact that enabling a skill still passes every existing gate, unchanged.
|
|
34
|
+
|
|
35
|
+
### 2. The adopter chooses, per person, and starts with nothing on
|
|
36
|
+
|
|
37
|
+
Two layers, because one layer is the blanket the owner ruled against:
|
|
38
|
+
|
|
39
|
+
| Layer | Holds | Why there |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| `specialities_catalog` | the skills this speciality OFFERS | the speciality is the thing being shared |
|
|
42
|
+
| `specialities_adoptions` | the skills THIS builder turned on | already per-builder, already the row an adoption creates |
|
|
43
|
+
|
|
44
|
+
Two people holding the same economist ideator speciality share its knowledge and its contract, and need not share a single enabled skill. That is the ruling, and the second table is where it becomes true rather than documented.
|
|
45
|
+
|
|
46
|
+
**Default: nothing enabled.** An adoption begins with an empty set.
|
|
47
|
+
|
|
48
|
+
### 3. Adoption is a walkthrough, not a toggle list
|
|
49
|
+
|
|
50
|
+
The consequence of the default that carries the actual intent. Adopting a speciality must **present each offered skill in turn — what it does, when its author reaches for it, what it costs** — and let the person enable it there, then. A settings page with eleven unchecked boxes satisfies the letter of "off by default" and defeats the ruling entirely.
|
|
51
|
+
|
|
52
|
+
The bar: **a person who has finished adopting a speciality can say what each skill in it does.** If the flow does not produce that, it is not built yet, whatever the schema says.
|
|
53
|
+
|
|
54
|
+
### 4. Sharing needs no new mechanism
|
|
55
|
+
|
|
56
|
+
Already shipped in `specialities_catalog`: `owner_kind` (`builder` | `project`), `visibility` (`private` → `project` → `public`), `offered`, `sellable`. The owner's *"edges in the beginning and then eventually being able to share"* is exactly that ladder, and it exists. Skills ride it for free: a speciality shared with its offered set intact is shared whole, and each new adopter still starts from nothing enabled and walks through.
|
|
57
|
+
|
|
58
|
+
## What this is NOT
|
|
59
|
+
|
|
60
|
+
- **Not a grant of authority.** Repeated because it is the invariant most likely to erode.
|
|
61
|
+
- **Not a way to declare a skill that does not exist.** A speciality may name only skills the instance actually has, or adoption offers a walkthrough of something that will 404 at the moment a person most needs it.
|
|
62
|
+
- **Not blanketed by craft.** A speciality's skills belong to the speciality, never to the craft above it.
|
|
63
|
+
- **Not authored by us.** [ADR 0296](0296-a-role-speciality-is-data-over-three-existing-surfaces.md): the deliverable is the mechanism, not a catalogue of specialities.
|
|
64
|
+
|
|
65
|
+
## Consequences
|
|
66
|
+
|
|
67
|
+
**The four documentation sites stop lying today, and do not start lying differently.** They currently claim a shipped ingredient. Until the build lands they must say *declared, decided, not yet built*, pointing here — the ADR, the migration header, `specialities.js`, and the module `CLAUDE.md`.
|
|
68
|
+
|
|
69
|
+
**A skill's own description becomes load-bearing.** The walkthrough is only as good as the text it shows, and skill descriptions were written to be *matched* by a model, not *read* by a newcomer. Whoever builds the walkthrough will find this.
|
|
70
|
+
|
|
71
|
+
**Adoption gets slower, on purpose.** That is the cost the ruling accepts.
|
|
72
|
+
|
|
73
|
+
## Build order
|
|
74
|
+
|
|
75
|
+
Deliberately separate tasks, because the middle one is where the ruling lives and it should not be a sub-bullet of a schema change:
|
|
76
|
+
|
|
77
|
+
1. **Schema + validation** — the offered set on the catalog, the enabled set on the adoption, `validateDraft`, the routes. Refuses a skill the instance does not have.
|
|
78
|
+
2. **The walkthrough** — the adoption flow that presents each skill and takes a decision. The ruling's real deliverable.
|
|
79
|
+
3. **Injection** — the enabled set reaches the session through the contract the Conductor already injects; a line naming the skills this builder leans on, per [ADR 0296](0296-a-role-speciality-is-data-over-three-existing-surfaces.md)'s pattern of using surfaces that exist.
|
|
80
|
+
|
|
81
|
+
Nothing is built by this ADR. It is the decision task 1003978 asked to be made first.
|
package/docs/adr/README.md
CHANGED
|
@@ -401,3 +401,4 @@ This keeps the decision history honest and traceable.
|
|
|
401
401
|
| 0307 | [**Autobongos: a deterministic supervisor, agentic workers** ([task 1003900](https://cloudbongos.com/builders#/task/1003900), goal 1000119). An always-on machine works the queue unattended inside owner-allowlisted goals. Most of the loop already existed wired to the wrong trigger — `sequence.js`'s ten-reason halt matrix (ADR 0153), an open blocker as the hold state, the Windows-aware headless spawn seam, `overnight-builder` — but the trigger was cron rather than capacity, and the **supervisor was itself a Claude session**. Decided: the supervisor is a **program**, only the worker is a session; the gauge is the statusLine's `rate_limits.five_hour`/`seven_day`, which per Anthropic's own note is absent until a session's first API response and so **cannot be read cold** (a statusline script persists it, a stale file is *unknown*, never *headroom*); **one session per task, metering not estimating** (a successor cannot be claimed until its predecessor ships, so a batch produces the same order while risking a half-done window, and a fresh session avoids a checkout going stale behind main); **completion is read from the ledger, never the session** (a worker that stopped needing a decision returns `subtype: success`, identical to one that finished); **no terminal stop, only speeds** (three failures → a probing state that self-heals; the only true stop is the server-side kill switch); the machine holds **no push credential**, so a grader outage stops the run rather than shipping unreviewed. The load-bearing finding was **tested before it was decided**: a worker told plainly to stop and ask instead settled four judgement calls and wrote them to disk — competent, and fatal to any design that waits for raised hands. Hence **gate before, record after, nothing in between**: the halt matrix refuses ambiguous work at pick time, and the worker's verdict must declare what the spec did not determine. Rejected: per-call `--permission-prompt-tool` (a thousand weak gates, and one slow supervisor stalls every worker), `--max-budget-usd` (caps dollars; on a subscription the constraint is capacity, so the bound is wall-clock), extending `overnight-builder`, and a terminal stop. Deliberately not built: mid-flight question answering, portability, per-task estimation.](0307-autobongos-a-deterministic-supervisor-and-agentic-workers.md) | autonomy / sessions |
|
|
402
402
|
| 0308 | [**An eleventh working area: Autobongos** ([task 1003900](https://cloudbongos.com/builders#/task/1003900), goal 1000119). Amends [ADR 0264](0264-the-ten-working-areas.md), which nine days earlier collapsed 38 goals into **ten** areas held until 5,000 builders. Area 11 is Autobongos ([goal 1000119](https://cloudbongos.com/builders#/goal/1000119), owner Lars). **Area 6 was the obvious home and was rejected**: area 6 is about a human's experience of the platform, and this area has no human in it — which limits to spend, which fences hold unsupervised, how it recovers with nobody at the keyboard. Those get less attention next to work with a user in front of it, not better answers. It **reopens** goal 1000028 (*Autonomy groundwork*), archived in the 38→10 cut with no successor and carried as `succeeds_goal_id`, so the lineage is in the ledger rather than the prose — and ADR 0264 §3 had already left "fold, keep as an eleventh, or close" as a live owner question. Admitting it cost something recorded: BONGOS-V2 is `building`, so its goal set is closed (ADR 0250) and `POST /goals` refuses `version_not_planning` — an Archon reopened it with a stored `admit_reason`, which is the cut holding well enough that adding to it left evidence. Does not decide: the ten-area discipline stands (one exception, written reason, 5,000-builder horizon unchanged), 0264 §3's other two open rows, or a second goal for the area.](0308-an-eleventh-working-area.md) | methodology / goals |
|
|
403
403
|
| 0309 | [**Module enablement is a database row, above the committed pack and below env** ([task 1002857](https://cloudbongos.com/builders#/task/1002857), goal 1000091). The hall Modules-tab toggle ([ADR 0107](0107-module-upstreaming-contribution-to-core-catalog.md) §9) wrote `config/modules.json` and reloaded the cache, so it looked like it worked — but that file is the committed instance pack and the pull-deploy runs `git reset --hard origin/main`, so the toggle reverted on the next deploy, **silently**. Enablement moves to `module_enablement_overrides` (migration `core_248`), read as one precedence layer: neutral pack → instance pack → **DB override** → env → default. **Above the pack** because the pack is what a deploy restores, so an override below it would be erased by the event it exists to survive; **below env** because `<PREFIX>_MODULE_<KEY>` is the incident knob and must not need a reachable database. **Auto-committing to `main` stays rejected** ([ADR 0016](<redacted>.md)): durability belongs in the DB, not in an HTTP handler that writes to the default branch. Two open design points settled: the **scope key** is the resolved instance pack path (the only axis two instances sharing one database differ on, so one sibling's toggle cannot flip the other's module), and the DB is read into a **snapshot** refreshed at boot and on write rather than inline, because `loadModules()` is synchronous and making it async would ripple into every consumer of a module flag. An unreachable database is a **no-op** — no row means "no opinion", not "off". Does not decide: whether a toggle should also restart the process so remounting is immediate.](0309-durable-module-enablement-db-override.md) | modules / ops |
|
|
404
|
+
| 0310 | [**A speciality OFFERS skills; the adopter turns them on, one at a time, having been shown what each does** ([task 1003978](https://cloudbongos.com/builders#/task/1003978), goal 1000095). Completes [ADR 0296](0296-a-role-speciality-is-data-over-three-existing-surfaces.md), which defined a speciality as knowledge + skills + an interaction contract and shipped two of the three: `grep -i skill` across the module's schema, domain and routes returns only the header sentences describing a thing that is not there — prose asserting behaviour the code does not have, and the feature's own definition at that. Task 1003978 withheld an implementation until the authority question was answered, and the answer is the strong one: **naming a skill GRANTS NOTHING**. Skills are materialised by `claude-materialize.js` and permission is rank-gated server-side with no caching ([ADR 0016](<redacted>.md)); a speciality offers a menu, and a menu is not an entitlement. **Two layers, because one layer is the blanket the owner ruled against**: the catalog holds what a speciality OFFERS, the already-per-builder `specialities_adoptions` row holds what THIS builder turned on — so two economist ideators share a speciality's knowledge and contract and need not share one enabled skill. **The default is nothing enabled, and that is a position about LEARNING rather than a safe default**: the owner's ruling is that adoption must *walk through what comes with each* because *people need to learn the system*. A settings page of unchecked boxes satisfies the letter and defeats the intent, so the bar is a user test — a person who has finished adopting can say what each skill does. Sharing needs no new mechanism: `owner_kind` / `visibility` / `offered` / `sellable` already ship the private→project→public ladder, and skills ride it. Builds nothing; the three-task order (schema, walkthrough, injection) is filed, and the four documentation sites that claimed a shipped ingredient now say decided-not-built.](0310-a-speciality-offers-skills-and-the-adopter-chooses-them.md) | specialities / methodology |
|
|
@@ -2256,5 +2256,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2256
2256
|
landed since 1.19.889 with no explicit bump. run 35470624284. (task 1002620)
|
|
2257
2257
|
1.19.891 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2258
2258
|
landed since 1.19.890 with no explicit bump. run 35471575521. (task 1002620)
|
|
2259
|
+
1.19.892 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2260
|
+
landed since 1.19.891 with no explicit bump. run 35471773244. (task 1002620)
|
|
2261
|
+
1.19.893 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2262
|
+
landed since 1.19.892 with no explicit bump. run 35473299957. (task 1002620)
|
|
2259
2263
|
---------------------------------------------------------------------------
|
|
2260
2264
|
```
|
|
@@ -90,6 +90,11 @@
|
|
|
90
90
|
{ "glob": "CLAUDE.md", "group": "docs", "why": "the methodology core" },
|
|
91
91
|
{ "glob": ".claude/hooks/", "group": "automation", "why": "local automation that could weaken a gate" },
|
|
92
92
|
{ "glob": ".claude/settings.json", "group": "automation", "why": "local automation that could weaken a gate" },
|
|
93
|
-
{ "glob": "config/scheduled-routines.json", "group": "automation", "why": "task 1002500. The ROUTINE SWITCHBOARD: per routine it decides enabled, cadence and requiresAutonomy — so an edit here can arm overnight-builder (autonomous claim+ship) or silence main-audit, the post-merge trust-boundary backstop ADR 0043 relies on. Sibling of .claude/hooks/ and .claude/settings.json above: the routine BODIES under .claude/scheduled-tasks/ are the autonomy module's source surface and stay unprotected, because protectedness derives per module and walling them is a separate policy decision (the .claude/agents/ precedent recorded in tests/government_protected_surfaces.mjs). This file is kernel-owned territory (module-scope-map's kernel key carries 'config/'), which is already protected, so registering it widens no module wall. The set is computed, not listed: the routine-switch tripwire in tests/government_protected_surfaces.mjs re-detects every config/*.json declaring routine switches and reds if one is unregistered." }
|
|
93
|
+
{ "glob": "config/scheduled-routines.json", "group": "automation", "why": "task 1002500. The ROUTINE SWITCHBOARD: per routine it decides enabled, cadence and requiresAutonomy — so an edit here can arm overnight-builder (autonomous claim+ship) or silence main-audit, the post-merge trust-boundary backstop ADR 0043 relies on. Sibling of .claude/hooks/ and .claude/settings.json above: the routine BODIES under .claude/scheduled-tasks/ are the autonomy module's source surface and stay unprotected, because protectedness derives per module and walling them is a separate policy decision (the .claude/agents/ precedent recorded in tests/government_protected_surfaces.mjs). This file is kernel-owned territory (module-scope-map's kernel key carries 'config/'), which is already protected, so registering it widens no module wall. The set is computed, not listed: the routine-switch tripwire in tests/government_protected_surfaces.mjs re-detects every config/*.json declaring routine switches and reds if one is unregistered." },
|
|
94
|
+
{
|
|
95
|
+
"glob": "modules/agents/",
|
|
96
|
+
"group": "automation",
|
|
97
|
+
"why": "task 1004006, owner decision 2026-09-19. Who may EDIT the agents module. This is the policy decision the note at the head of tests/government_protected_surfaces.mjs reserved when task 1002492 backed out '.claude/agents/' — not a scope fix and not a side effect. The module fires subagents unattended and SPENDS REAL MONEY doing it (~$0.90 a fire, a per-agent monthly ceiling in agents_004, folded into the global MTD autonomy brake), so editing what may fire and what it may cost is Metic work. The 1002492 note's stated cost was that it walls goals 1000038/39/40; all three are ARCHIVED and re-derived on 2026-09-19 there are ZERO live goals scoped to agents. Of the nine live tasks in the module six already require Metic; the three that did not are the agent secret-file permission rule, the run-ledger column allowlist and the model-provider port — security and spend surfaces that ADR 0043 already keeps sub-Metic builders out of in spirit. Protectedness is DERIVED PER MODULE (module-scope-map isProtected -> touchesOverlap), so this one glob is what flips the whole module, which is the intent here rather than the accident it was in 1002492."
|
|
98
|
+
}
|
|
94
99
|
]
|
|
95
100
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
-- specialities_001_specialities.sql — role sub-specialities: a named bundle of
|
|
2
|
-
-- context, skills and an interaction contract that sits UNDER a craft
|
|
2
|
+
-- context, skills (ADR 0310 — decided 2026-09-19, NOT built: no column here holds them) and an interaction contract that sits UNDER a craft
|
|
3
3
|
-- (task 1003855, goal 1000095; ADR 0296).
|
|
4
4
|
--
|
|
5
5
|
-- WHAT A SPECIALITY IS, AND WHY IT IS DATA RATHER THAN CODE. The owner's framing
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// modules/specialities/specialities.js — role SUB-SPECIALITIES: a named bundle of
|
|
2
|
-
// knowledge, skills and an interaction contract that sits UNDER a craft
|
|
2
|
+
// knowledge, skills (ADR 0310 — decided, NOT yet built) and an interaction contract that sits UNDER a craft
|
|
3
3
|
// (task 1003855, goal 1000095; ADR 0296).
|
|
4
4
|
//
|
|
5
5
|
// WHAT THIS IS FOR. A craft says how you work (engineer / ideator / artist / ui).
|
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
// resolved into GET /me and injected by the
|
|
15
15
|
// Conductor hook — a speciality contract rides
|
|
16
16
|
// that SAME path, it does not add a second one
|
|
17
|
-
// • declared skills
|
|
17
|
+
// • declared skills NOT BUILT. Decided in ADR 0310 (task 1003978)
|
|
18
|
+
// and deliberately not implemented there: a speciality
|
|
19
|
+
// will OFFER skills, and each adopter enables them one
|
|
20
|
+
// at a time after being shown what each does. Nothing
|
|
21
|
+
// in this file, the schema or the routes carries it yet —
|
|
22
|
+
// `grep -i skill` here finds only prose.
|
|
18
23
|
// The genuinely new part is the knowledge bundle, which is why the migration adds
|
|
19
24
|
// tables for it and this file carries the rules over them.
|
|
20
25
|
//
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.893",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.893",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.893",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/src/module-api.js
CHANGED
|
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
71
71
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
72
72
|
// the entry to that file. Look for a version's history there, not here.
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
74
|
-
const CORE_VERSION = '1.19.
|
|
74
|
+
const CORE_VERSION = '1.19.893'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
75
75
|
|
|
76
76
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
77
77
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/agents_sync.mjs
CHANGED
|
@@ -328,7 +328,20 @@ t('the wall is wired to the REAL protected-surface registry', () => {
|
|
|
328
328
|
assert.equal(realMatcher('modules/government/catalog.js'), true, 'the authority surface');
|
|
329
329
|
assert.equal(realMatcher('migrations/001_init.sql'), true, 'schema');
|
|
330
330
|
assert.equal(realMatcher('.claude/hooks/pre-push.js'), true, 'local automation');
|
|
331
|
-
|
|
331
|
+
// A module's own code is not protected BY DEFAULT — the contrast case that
|
|
332
|
+
// proves this wall discriminates rather than blanketing everything.
|
|
333
|
+
//
|
|
334
|
+
// This used to cite modules/agents/lib/validate.js, which stopped being a
|
|
335
|
+
// valid example on 2026-09-19 when task 1004006 walled the agents module at
|
|
336
|
+
// Metic on the owner's decision. The case still needs an unprotected module
|
|
337
|
+
// file; modules/ideas/ is one, and the assertion below re-states WHY it is the
|
|
338
|
+
// example rather than leaving the next reader to rediscover that the old one
|
|
339
|
+
// turned. (A fixture chosen because it is negative can become positive, and
|
|
340
|
+
// the test then looks untouched by the change that broke it.)
|
|
341
|
+
assert.equal(realMatcher('modules/ideas/routing.js'), false, "a module's own code is not protected by default");
|
|
342
|
+
assert.equal(realMatcher('modules/agents/lib/validate.js'), true,
|
|
343
|
+
'the agents module IS protected since task 1004006 — deliberately, and it is '
|
|
344
|
+
+ 'no longer usable as the unprotected example above');
|
|
332
345
|
// `.claude/agents/` itself is deliberately NOT in the registry — see the note
|
|
333
346
|
// on ADDED_SINCE_R101 in tests/government_protected_surfaces.mjs. Protecting
|
|
334
347
|
// it would rank-wall the whole agents module by derivation, which is a
|
|
@@ -358,8 +371,10 @@ t('a path scope onto the authority surface is withheld a rank AND flagged', () =
|
|
|
358
371
|
t('an unprotected path scope still reconciles normally', () => {
|
|
359
372
|
// The wall is a floor, not a ban — a tightening that refused every path scope
|
|
360
373
|
// would pass the test above and break the feature.
|
|
374
|
+
// modules/ideas/ rather than modules/agents/lib/ — the latter became a
|
|
375
|
+
// PROTECTED path in task 1004006, so it can no longer stand for "unprotected".
|
|
361
376
|
const d = sync.parseAgentFile(
|
|
362
|
-
'---\nname: reader\ntrigger: on-demand\nscope_paths: [modules/
|
|
377
|
+
'---\nname: reader\ntrigger: on-demand\nscope_paths: [modules/ideas/]\n---\nbody', {});
|
|
363
378
|
assert.equal(sync.declaresProtectedScope(d, [], realMatcher), false);
|
|
364
379
|
const p = sync.planOne(d, {
|
|
365
380
|
authorRank: 'metic', allowedModules: ['agents'], isProtectedPath: realMatcher,
|
|
@@ -240,6 +240,29 @@ const ADDED_SINCE_R101 = [
|
|
|
240
240
|
+ 'edit which slipped past the pre-push hook and the grader — the one ADR 0043 '
|
|
241
241
|
+ 'layer that does not depend on the builder cooperating.',
|
|
242
242
|
},
|
|
243
|
+
// task 1004006, owner decision 2026-09-19. THE POLICY DECISION the note at the head of
|
|
244
|
+
// this file reserved when task 1002492 backed out `.claude/agents/`: who may EDIT the
|
|
245
|
+
// agents module. Landed deliberately, as that note required — the entry here, the
|
|
246
|
+
// publish-manifest check (already satisfied: modules/agents/ is on PUBLISH_ALLOWLIST,
|
|
247
|
+
// verified rather than assumed), and the switchboard tripwire below re-aimed rather
|
|
248
|
+
// than deleted.
|
|
249
|
+
//
|
|
250
|
+
// The note's stated cost was that it walls goals 1000038/39/40. All three are ARCHIVED,
|
|
251
|
+
// and re-derived on 2026-09-19 there are ZERO live goals scoped to agents — so that
|
|
252
|
+
// specific cost is gone, which is exactly why the note said to re-derive rather than
|
|
253
|
+
// trust it. What replaced it: the module now spends real money unattended (~$0.90 a
|
|
254
|
+
// fire, per-agent monthly ceiling in agents_004, folded into the global MTD brake).
|
|
255
|
+
{
|
|
256
|
+
after: 'config/scheduled-routines.json',
|
|
257
|
+
glob: 'modules/agents/',
|
|
258
|
+
why: 'the module that fires subagents unattended and SPENDS to do it. Editing what '
|
|
259
|
+
+ 'may fire, and what it may cost, is Metic work. Of the nine live tasks in the '
|
|
260
|
+
+ 'module six already required Metic; the three that did not are the agent '
|
|
261
|
+
+ 'secret-file permission rule, the run-ledger column allowlist and the '
|
|
262
|
+
+ 'model-provider port — security and spend surfaces ADR 0043 already keeps '
|
|
263
|
+
+ 'sub-Metic builders out of in spirit, so the real cost of this entry is three '
|
|
264
|
+
+ 'tasks that arguably should not have been open.',
|
|
265
|
+
},
|
|
243
266
|
];
|
|
244
267
|
|
|
245
268
|
// Surfaces RENAMED since R101 — still protected, under a new path. Distinct from
|
|
@@ -731,11 +754,34 @@ test('registering the switchboard widened no module wall', () => {
|
|
|
731
754
|
assert.ok(protectedNow.includes('kernel'),
|
|
732
755
|
'kernel owns config/ and was already protected before this entry; if it is not, the '
|
|
733
756
|
+ 'premise that registering a config/ file widens nothing has changed.');
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
757
|
+
// task 1004006 RE-AIMED THIS, and did not delete it. The assertion used to be
|
|
758
|
+
// `!includes('agents')`, and its job was never "agents must stay open" — it was "the
|
|
759
|
+
// SWITCHBOARD entry must not have reached the agents module as a side effect". That job
|
|
760
|
+
// still exists; agents is now protected for its OWN declared reason (the owner's
|
|
761
|
+
// 2026-09-19 decision), which is the separate policy act the old message pointed at.
|
|
762
|
+
//
|
|
763
|
+
// So the check becomes: agents is protected, and it is protected BY ITS OWN GLOB. If
|
|
764
|
+
// the switchboard entry — or any future config/ entry — were what reached the module,
|
|
765
|
+
// surfaceFor would name that file instead, and this still fails.
|
|
766
|
+
assert.ok(protectedNow.includes('agents'),
|
|
767
|
+
'the agents module is no longer protected — task 1004006 registered modules/agents/ '
|
|
768
|
+
+ 'on the owner\'s decision (the module fires subagents unattended and spends to do '
|
|
769
|
+
+ 'it). If the entry was removed, that is a policy reversal and belongs in the note '
|
|
770
|
+
+ 'at the head of this file, not a silent deletion.');
|
|
771
|
+
const agentsSurface = ppc.surfaceFor('modules/agents/lib/validate.js');
|
|
772
|
+
assert.equal(agentsSurface && agentsSurface.glob, 'modules/agents/',
|
|
773
|
+
'the agents module is protected by SOMETHING OTHER than its own glob — a config/ or '
|
|
774
|
+
+ 'switchboard entry has widened into it, which is the second-order effect this test '
|
|
775
|
+
+ 'exists to catch. The deliberate entry and an accidental one look identical in '
|
|
776
|
+
+ 'protectedModules(); only the matching glob tells them apart.');
|
|
777
|
+
// 1002492's rejection still stands: the module SOURCE is walled, agent DEFINITION files
|
|
778
|
+
// are not. Walling those was a scope fix that the scope_paths wall in
|
|
779
|
+
// modules/agents/lib/validate.js already does properly, and it is not what 1004006
|
|
780
|
+
// decided.
|
|
781
|
+
assert.ok(!ppc.surfaceFor('.claude/agents/historian.md'),
|
|
782
|
+
'.claude/agents/ became protected — task 1002492 considered and rejected that, and '
|
|
783
|
+
+ 'task 1004006 walled the module SOURCE without reopening it. An agent definition '
|
|
784
|
+
+ 'is scoped by validate.js, not by a rank floor on the file.');
|
|
739
785
|
assert.ok(ppc.surfaceFor('scripts/gds/conductor-dispatch.js'),
|
|
740
786
|
'the autonomy module was protected through conductor-dispatch.js before this entry. '
|
|
741
787
|
+ 'If that glob is gone, re-derive which modules this registry walls before trusting '
|
|
@@ -145,8 +145,16 @@ test('agents joined the roster (task 1002736) — the territory goals 1000038/39
|
|
|
145
145
|
const globs = scope.globsForModules(['agents']);
|
|
146
146
|
assert.ok(globs.includes('modules/agents/'), 'agents owns its module directory');
|
|
147
147
|
assert.ok(globs.includes('.claude/agents/'), 'the committed definitions are the module source surface');
|
|
148
|
-
// Derived, not hand-set
|
|
149
|
-
|
|
148
|
+
// Derived, not hand-set. This flipped to TRUE in task 1004006, when the owner
|
|
149
|
+
// walled the module at Metic (2026-09-19): modules/agents/ is now a registered
|
|
150
|
+
// protected surface because the module fires subagents unattended and spends
|
|
151
|
+
// real money doing it. The assertion below is the point of this pair and is
|
|
152
|
+
// UNCHANGED — `protected` must still be the faithful derivation rather than a
|
|
153
|
+
// literal, which is what would break if someone hand-set the flag to match.
|
|
154
|
+
assert.equal(scope.isProtectedModule('agents'), true,
|
|
155
|
+
'the agents module lost its Metic floor — task 1004006 registered modules/agents/ '
|
|
156
|
+
+ 'on the owner\'s decision. A reversal is a policy act and belongs in the note at '
|
|
157
|
+
+ 'the head of tests/government_protected_surfaces.mjs.');
|
|
150
158
|
assert.equal(
|
|
151
159
|
scope.scopeForModule('agents').protected,
|
|
152
160
|
scope.isProtected(globs),
|