@bongos/core 1.19.553 → 1.19.554
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
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.554",
|
|
6
|
+
"core_contract": "1.19.554",
|
|
7
|
+
"source_commit": "215fb366ac46d6cf833c1e4eee47e40a9052f39a",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-06T16:
|
|
9
|
+
"built_at": "2026-09-06T16:09:58.941Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 443,
|
|
13
13
|
"agent_docs_stubbed": 24,
|
|
14
14
|
"functional_verbatim": 2019,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2486,
|
|
20
|
+
"tree_sha256": "c3e20074c1896a0d3a40217b84ab0a534a63a1329a664c419b5e7d0f942db7af",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -1764,10 +1764,15 @@
|
|
|
1764
1764
|
"mode": "0000644",
|
|
1765
1765
|
"sha256": "38fa5f4ce3a91eaf3302527a4228ddb1951ea128e83b2ab8e0ad5def658e0a6d"
|
|
1766
1766
|
},
|
|
1767
|
+
{
|
|
1768
|
+
"path": "docs/adr/0250-strict-versioning-the-version-boundary-is-the-scope-gate.md",
|
|
1769
|
+
"mode": "0000644",
|
|
1770
|
+
"sha256": "da4549c19609d1f549e6f5a57b530c330765f1778f0a9b09a15a208f4575f986"
|
|
1771
|
+
},
|
|
1767
1772
|
{
|
|
1768
1773
|
"path": "docs/adr/README.md",
|
|
1769
1774
|
"mode": "0000644",
|
|
1770
|
-
"sha256": "
|
|
1775
|
+
"sha256": "f779c7dc040fd7096fa27e713c7045c44d69b1b7fa5fee107e6ae4d69ae11d6e"
|
|
1771
1776
|
},
|
|
1772
1777
|
{
|
|
1773
1778
|
"path": "docs/api-reference.md",
|
|
@@ -2652,7 +2657,7 @@
|
|
|
2652
2657
|
{
|
|
2653
2658
|
"path": "docs/module-api-changelog.md",
|
|
2654
2659
|
"mode": "0000644",
|
|
2655
|
-
"sha256": "
|
|
2660
|
+
"sha256": "e165ea406cc50e9f818a6cc6911a4cdb1ced5ec2be48b991a8e6246c3a22fb31"
|
|
2656
2661
|
},
|
|
2657
2662
|
{
|
|
2658
2663
|
"path": "docs/modules-contract.md",
|
|
@@ -7472,12 +7477,12 @@
|
|
|
7472
7477
|
{
|
|
7473
7478
|
"path": "package-lock.json",
|
|
7474
7479
|
"mode": "0000644",
|
|
7475
|
-
"sha256": "
|
|
7480
|
+
"sha256": "4618ee402fb7f815f739e31bd4b390f79ceed54dcd282eb70ea24b965ec1674b"
|
|
7476
7481
|
},
|
|
7477
7482
|
{
|
|
7478
7483
|
"path": "package.json",
|
|
7479
7484
|
"mode": "0000644",
|
|
7480
|
-
"sha256": "
|
|
7485
|
+
"sha256": "f61bdd8849dbf7bda1fc730194126667db2076b89faacfe1243fa150db4fb5ea"
|
|
7481
7486
|
},
|
|
7482
7487
|
{
|
|
7483
7488
|
"path": "public-docs/index.html",
|
|
@@ -9172,7 +9177,7 @@
|
|
|
9172
9177
|
{
|
|
9173
9178
|
"path": "src/module-api.js",
|
|
9174
9179
|
"mode": "0000644",
|
|
9175
|
-
"sha256": "
|
|
9180
|
+
"sha256": "2c50343fb6072eb5f1406904bd4e3c0e078019c6573e8e766cbc6662805dac6c"
|
|
9176
9181
|
},
|
|
9177
9182
|
{
|
|
9178
9183
|
"path": "src/module-loader/catalog.js",
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# 0250 — Strict versioning: the version boundary is the scope gate
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-09-06
|
|
5
|
+
- **Tasks:** [#1003589](https://cloudbongos.com/builders#/task/1003589) (this decision), seeded by the planning session [#1003586](https://cloudbongos.com/builders#/task/1003586). The implementation is BV1.R03–R30 under the same goal.
|
|
6
|
+
- **Goal:** [#1000086](https://cloudbongos.com/builders#/goal/1000086) — Strict versioning: a goal cannot outlive its tasks, and a version's goal set is closed.
|
|
7
|
+
- **Builds on:** [ADR 0086](<redacted>.md) (Project → Version → Goal → Criteria → Task — the hierarchy this decision finally closes at the top), [ADR 0015](<redacted>.md) (deps are load-bearing, the rank prefix is decorative), [ADR 0183](<redacted>.md) (a criterion satisfies itself when its tasks ship).
|
|
8
|
+
- **Amends:** [ADR 0157](<redacted>.md) — for one key, `version.create`. See §6.
|
|
9
|
+
|
|
10
|
+
## 1. The problem, measured
|
|
11
|
+
|
|
12
|
+
The hierarchy has a bottom and no top. Tasks close, criteria close, goals close —
|
|
13
|
+
and a version never does. Measured on the live instance:
|
|
14
|
+
|
|
15
|
+
| | 2026-09-04 (planning session) | 2026-09-06 (this ADR) |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Goals on BONGOS-V1 | 62 | 64 |
|
|
18
|
+
| Closed goals still holding unfinished tasks | 10, holding 28 | **14, holding 35** (both versions) |
|
|
19
|
+
| Open tasks across open goals | 502 | **523** |
|
|
20
|
+
| Versions `building` on one project | 2 | 2 (`BONGOS-V1`, `CB-V1`) |
|
|
21
|
+
|
|
22
|
+
The second column is not a re-count for its own sake. Two days of ordinary work
|
|
23
|
+
made every number worse, which is the argument: nothing in the system pushes
|
|
24
|
+
back, so the drift is not a backlog to clear once — it is the steady state.
|
|
25
|
+
|
|
26
|
+
"Closed goals still holding unfinished tasks" is the sharp one. Fourteen goals
|
|
27
|
+
report `achieved` or `archived` on every surface a person or an agent reads,
|
|
28
|
+
while thirty-five tasks inside them are still `ready`, `backlog`, `active`,
|
|
29
|
+
`completed` or `confirmed`. A goal that says it is done and is not is worse than
|
|
30
|
+
no goal at all: `/status` rolls it up as delivered, the board stops showing its
|
|
31
|
+
work, and the tasks become unreachable except by direct id.
|
|
32
|
+
|
|
33
|
+
## 2. The cause is mechanical, not social
|
|
34
|
+
|
|
35
|
+
No one decided any of this. Four gaps in the code produce all of it:
|
|
36
|
+
|
|
37
|
+
1. **`POST /goals` never reads the version's status** ([`modules/lifecycle/routes/goals.js`](../../modules/lifecycle/routes/goals.js)). It fetches the version row to validate the id and ignores `status`, so a goal can be added to a version that has been building for months. That is how a version's scope grows without anyone widening it.
|
|
38
|
+
2. **Goal achievement is computed from criteria alone** ([`db-goals.js`](../../modules/lifecycle/db-goals.js) `achieveGoalIfComplete`, and the cascade in [`done-when.js`](../../modules/lifecycle/done-when.js)). Neither writer consults `tasks`. Since [ADR 0183](<redacted>.md) criteria satisfy themselves on ship, the path from "the criteria I happened to link are satisfied" to `achieved` is fully automatic — and a task nobody linked to a criterion holds nothing open.
|
|
39
|
+
3. **`POST /goals/:id/archive` checks nothing at all.** Archiving is a single call that discards whatever the goal was holding, silently.
|
|
40
|
+
4. **No route ships a version.** Version status has only ever moved by hand-run SQL on the droplet; `scripts/gds/version-close.js` prints that the API cannot do it. A thing with no closing move does not close.
|
|
41
|
+
|
|
42
|
+
Each gap is small. Together they mean the version boundary — the one line that is
|
|
43
|
+
supposed to say *this much and no more* — has no enforcement anywhere.
|
|
44
|
+
|
|
45
|
+
## 3. Decision
|
|
46
|
+
|
|
47
|
+
**The version boundary is the scope gate.** A version's goal set is fixed when
|
|
48
|
+
the version starts building; a goal stays open until its work is genuinely
|
|
49
|
+
finished; and a version can reach done. Five rules:
|
|
50
|
+
|
|
51
|
+
**D1 — The goal set closes when the version starts building.** New goals may be
|
|
52
|
+
created freely on a version in `planning` — that is where the *next* version is
|
|
53
|
+
scoped, in advance and at leisure. `POST /goals` reads `version.status` and
|
|
54
|
+
refuses anything else with a named 409 an agent can act on.
|
|
55
|
+
|
|
56
|
+
**D2 — A goal cannot close while it holds non-terminal work.** A goal reaches
|
|
57
|
+
`achieved` only when its criteria are satisfied **and** no task in it is
|
|
58
|
+
non-terminal. Terminal means `shipped` or explicitly `abandoned` — the vocabulary
|
|
59
|
+
declared once in [`modules/lifecycle/task-status.js`](../../modules/lifecycle/task-status.js) ([task #1003588](https://cloudbongos.com/builders#/task/1003588)). `completed` and
|
|
60
|
+
`confirmed` are mid-ship states that can still fail a grade or strand, so they
|
|
61
|
+
hold the goal open. **Shipping is never blocked by this** — only the goal's flip
|
|
62
|
+
to `achieved` is. Both writers gain the check, and the cascade's copy runs on the ship
|
|
63
|
+
transaction's own client so it sees uncommitted flips.
|
|
64
|
+
|
|
65
|
+
**D3 — Killing a goal is one recorded decision.** Archiving a goal that still
|
|
66
|
+
holds open tasks returns those tasks and refuses until the caller says what
|
|
67
|
+
happens to each: abandon with the archive's reason, or move to a named surviving
|
|
68
|
+
goal. Archiving a goal with nothing open stays a single call.
|
|
69
|
+
|
|
70
|
+
**D4 — Every task belongs to a real, open goal.** `goal_id` becomes required at
|
|
71
|
+
creation, the per-version catch-all fallback is deleted, and no vector can put a
|
|
72
|
+
task into a closed goal.
|
|
73
|
+
|
|
74
|
+
**D5 — Exactly one version builds per project, and the next is scopeable while it
|
|
75
|
+
runs.** The server refuses a second `building` version. A version auto-closes
|
|
76
|
+
when its last non-maintenance goal achieves; an Archon may close one early, with
|
|
77
|
+
a per-goal disposition (roll forward as a successor goal, or abandon). The single
|
|
78
|
+
`planning` version promotes itself on close, so the project is never without a
|
|
79
|
+
live train.
|
|
80
|
+
|
|
81
|
+
### The override, and why it is not a loophole
|
|
82
|
+
|
|
83
|
+
D1 without an escape hatch would be a rule people route around instead of using —
|
|
84
|
+
the standard failure of a hard scope gate is a fake "hotfix version" cut to hold
|
|
85
|
+
the thing that did not fit. So: an **Archon may admit one named goal to a building
|
|
86
|
+
version, with a reason**, and the overrides are **counted and shown on the
|
|
87
|
+
version**. A visible counter is what keeps the exception honest; an override that
|
|
88
|
+
leaves no trace is just a slower version of no rule.
|
|
89
|
+
|
|
90
|
+
The maintenance goal is the other pressure valve. Each version carries one
|
|
91
|
+
auto-created maintenance goal, exempt from the auto-close count, whose open bugs
|
|
92
|
+
carry forward — because bugs arrive on their own schedule and must not need an
|
|
93
|
+
override to be filed.
|
|
94
|
+
|
|
95
|
+
## 4. What this is not
|
|
96
|
+
|
|
97
|
+
- **Not a new gate on shipping.** Nothing here can refuse a ship, block a claim, or park a task. Every rule acts on a *closing* move — goal achievement, goal archive, version close, goal/task creation. A builder's day is unchanged unless they try to close something that is not finished.
|
|
98
|
+
- **Not a re-litigation of [ADR 0183](<redacted>.md).** Criteria still satisfy themselves. D2 adds a second condition to the *goal's* flip, not a human gate in front of the criterion's.
|
|
99
|
+
- **Not a change to what `abandoned` means.** [Task 1002651](https://cloudbongos.com/builders#/task/1002651) already made abandoned terminal; D2 relies on that and adds nothing.
|
|
100
|
+
|
|
101
|
+
## 5. Rejected alternatives
|
|
102
|
+
|
|
103
|
+
- **Count `completed`/`confirmed` as finished.** It would close the visible symptom with one word and is wrong: those states can fail a grade, strand at `confirmed`, or be reconciled back. A goal closed on a task that never landed is the same lie in a shorter timeframe. (The goals **board** does bucket them as done — that is a progress ring, and `task-status.js` keeps the two questions apart on purpose.)
|
|
104
|
+
- **A nightly reconciler that reopens wrongly-closed goals.** Repairs the ledger after the fact and leaves the four gaps open, so the report can never be trusted between sweeps. Prevention is available here; detection is what you build when it is not.
|
|
105
|
+
- **A human review gate before a goal may close.** This is the queue [ADR 0183](<redacted>.md) deliberately dismantled. The check D2 needs is a fact the database already holds — asking a person to assert it is slower and less reliable than asking the table.
|
|
106
|
+
- **Allow concurrent building versions and scope them by module.** Tempting, because it is the state the instance is in. But two live trains on one project means every "what is in this version" question has two answers, and the goal set can never close on either. The `planning` version is the supported way to work ahead.
|
|
107
|
+
- **Retire versions entirely and let goals stand alone.** Genuinely considered: goals already carry criteria, membership and scope, so the version is arguably ceremony. Rejected because the version is the unit the owner sets scope in and the unit `limitations/<version>-shipped.md` archives — removing it would move the scope conversation somewhere with no home.
|
|
108
|
+
|
|
109
|
+
## 6. The one amendment to ADR 0157
|
|
110
|
+
|
|
111
|
+
[ADR 0157](<redacted>.md) lowered `version.create` to
|
|
112
|
+
`metic`, on the argument that **authoring delegates, disposition does not**:
|
|
113
|
+
creating a version confers no authority, so its siblings are `goal.create` and
|
|
114
|
+
`task.create`, and only `version.close` stays Archon. That argument was correct
|
|
115
|
+
about what a version *was*.
|
|
116
|
+
|
|
117
|
+
This decision changes what a version *is*. Once the version boundary carries the
|
|
118
|
+
scope gate, cutting a new version is no longer authoring — it is **the way around
|
|
119
|
+
D1**. A builder refused a goal on a building version needs only create a fresh
|
|
120
|
+
version to put it somewhere, and the gate evaporates without anyone overriding
|
|
121
|
+
anything. So `version.create` returns to the Archon floor, and the pairing
|
|
122
|
+
canonical-permissions has always described — close and rank as the powers that
|
|
123
|
+
never delegate — extends to create for the same reason it covered close: it is
|
|
124
|
+
now a disposition over scope, not a piece of authoring.
|
|
125
|
+
|
|
126
|
+
The rest of ADR 0157 stands; this touches exactly one key.
|
|
127
|
+
|
|
128
|
+
## 7. Consequences
|
|
129
|
+
|
|
130
|
+
- The live instance is **not** in the state these rules describe, and the rules cannot be enforced on data that violates them. Bringing it over is explicit work under criterion `sv-cutover-proven-live`: fold `CB-V1` into `BONGOS-V1`, disposition the closed goals holding open tasks, rehome the catch-all tasks, and scope `BONGOS-V2` in `planning`. Each invariant lands with a query proving it.
|
|
131
|
+
- `POST /goals`, `POST /tasks`, `PATCH /tasks/:id` and `POST /goals/:id/archive` gain refusals that did not exist. Every one is a **named** code, because the callers are agents and an unnamed 409 is an agent stopping to ask a human.
|
|
132
|
+
- Anything that created a task without a goal — capture, idea routing, the CLIs — has to name one. That is the point of D4, and it is the widest blast radius here.
|
|
133
|
+
- `/status` and the goal board start telling the truth about what is finished. Some goals that read `achieved` today will read `open` again after the cutover; that is the repair, not a regression.
|
package/docs/adr/README.md
CHANGED
|
@@ -337,6 +337,7 @@ This keeps the decision history honest and traceable.
|
|
|
337
337
|
| 0248 | [**A hub project’s owner is the provisioning row, carried on a port and bound to both halves of the catalog projection** ([task 1003580](https://cloudbongos.com/builders#/task/1003580) · goal 1000046, criterion `proj-invite-builders`). [ADR 0246](<redacted>.md) made a wizard-made project resolvable by `POST /projects/invite` and named the remainder: the route is gated `project.curate` (floor metic) with no own-scope arm, so the project’s own owner — a `xenos` on the hub — got 403 on their own project, blocking BV1.R27–R29 and the C6 proof. The arm admits a `project.curate` holder **OR** the owner, where owner means `provisioning_instances.owner_builder_id` — never the hub’s `owner_github_id` columns, which [ADR 0238](<redacted>.md) fixes as attribution. Three things worth carrying: the fact travels on a NEW port `provisioning.projectOwnedBy` answering a **boolean**, so the comparison stays on the plane that holds the row and no consumer handles an ownership id it could mis-compare; the port is asked about **both halves of the ADR 0246 projection** (slug AND origin), because a hand-seeded catalog row’s name is otherwise a squattable claim on that project’s membership directory; and ownership is asked BEFORE the target is resolved, so the refusal stays the untouched `requirePermission` 403 and cannot become an existence oracle over stealth projects ([ADR 0182](<redacted>.md) D2a). The atom stays spelled in the route’s own middleware list ([ADR 0210](<redacted>.md)’s rule), so the route still classifies `metic+archon`.](<redacted>.md) | onboarding / provisioning / project creation |
|
|
338
338
|
|
|
339
339
|
| 0249 | [**The invite step is the done panel’s first act, not a rail step — and the same widget is the way back** ([task 1002346](https://cloudbongos.com/builders#/task/1002346) · goal 1000046, criterion `proj-invite-builders` / C6). BV1.R27 asks for a skippable invite step *in creation*, but an invite cannot be one of the wizard’s numbered panels: `POST /projects/invite` takes `{ client_id, github_login }` where `client_id` **is the slug**, and per [ADR 0246](<redacted>.md) the catalog row that slug resolves is projected **at create** — so before the Create step there is nothing to invite anyone to, and a pre-Create panel could only queue names and replay them, inventing a second unaudited invite path whose failures land after the owner has left. **Decision: the step is the FIRST act of the DONE panel**, keyed on the created record (not `instanceId`, which `startProgress` sets only *after* `renderDone`), so it also returns on a resumed watch; the rail is NOT renumbered (a captured learning on this file: a renumbering couples in four places, and a step that can only run after the terminal step is not a step in the rail). The SAME widget is mounted again on the project’s own page beside *Its extras* — the [ADR 0245](<redacted>.md) argument, one implementation and two mounts, so the refusal vocabulary cannot drift. The widget answers neither “is anyone signed in” nor “is this their project”: each call site hands over a slug or nothing (the created record; the own-scoped manage read that 404s for everyone else), because a page that re-derived ownership would be a second, weaker copy of a server rule. **The 403 is quoted carefully and that is the point** — [ADR 0248](<redacted>.md) paid two equalised reads per request to make the refusal identical for a real, stealth, absent or malformed target ([ADR 0182](<redacted>.md) D2a), and “you lack permission on **this project**” would assert in copy the existence the route refuses to confirm, so the page says only “this account can’t invite people here” and a test pins that wording. `account_not_found` and `unknown_project` are said in the route’s own words (the latter as a moment, not a dead end — the row is projected at create); anything else falls through to the server’s own message. The pending line is drawn from the 201 row’s `membership_kind` and the typed login — **never the `github_id`** it carries, which names someone who has accepted nothing yet. Creation is never blocked: the project is filed before the step is shown, so every refusal leaves it created and the step re-tryable, and Skip sends nothing. Rejected: a rail step before or after Create; the manage page alone (R27 asks for it *in creation*); reading the real membership directory back (no own-scoped route exists, and the session receipt is honest about being one).](<redacted>.md) | provisioning / project creation |
|
|
340
|
+
| 0250 | [**Strict versioning — the version boundary is the scope gate** ([task 1003589](https://cloudbongos.com/builders#/task/1003589) · goal 1000086). The work hierarchy ([ADR 0086](<redacted>.md)) has a bottom and no top: tasks close, criteria close, goals close, and a version never does. Measured live — **14 goals reading `achieved`/`archived` while holding 35 unfinished tasks**, 523 open tasks across 37 open goals, and **two versions `building` on one project**; every number was worse than the planning session had recorded two days earlier, which is the argument: the drift is the steady state, not a backlog. The cause is four mechanical gaps, not a social one — `POST /goals` fetches the version row and never reads its `status`; goal achievement is computed from criteria alone and never consults `tasks` (both the `db-goals.js` writer and the `done-when.js` cascade); `POST /goals/:id/archive` checks nothing at all; and no route ships a version, so version status has only ever moved as hand-run SQL on the droplet. **Decision, five rules:** the goal set closes when a version starts `building` (new goals go on the `planning` version — where the next version is scoped in advance) with a named 409 an agent can act on; a goal reaches `achieved` only when its criteria are satisfied **and** no task in it is non-terminal, where terminal is `shipped`|`abandoned` per [task 1003588](https://cloudbongos.com/builders#/task/1003588)'s one vocabulary and the mid-ship `completed`/`confirmed` hold it open; archiving a goal that holds open tasks refuses until the caller dispositions each (abandon with the reason, or move to a named surviving goal); `goal_id` becomes required and the per-version catch-all fallback is deleted; and exactly one version builds per project, auto-closing when its last non-maintenance goal achieves, with the single `planning` version promoting itself so the project is never without a live train. **The escape hatches are deliberate and visible**: an Archon may admit one *named* goal to a building version with a reason, and the overrides are counted on the version (the standard failure of a hard scope gate is a fake hotfix version cut to hold what did not fit — an untraced override is just a slower version of no rule); plus one auto-created maintenance goal per version, exempt from the auto-close count, so bugs never need an override to be filed. **Nothing here can refuse a ship, a claim, or a task** — every rule acts on a *closing* move. Rejected: counting `completed`/`confirmed` as finished (they can fail a grade or strand — the goals **board** buckets them as done, but that is a progress ring, and `task-status.js` keeps the two questions apart); a nightly reconciler that reopens wrongly-closed goals (detection is what you build when prevention is unavailable, and it is available here); a human review gate before a goal may close (the queue [ADR 0183](<redacted>.md) dismantled — the check is a fact the table already holds); concurrent building versions scoped by module (two live trains means every "what is in this version" question has two answers); retiring versions entirely (the version is the unit the owner sets scope in and `limitations/<version>-shipped.md` archives). **Amends [ADR 0157](<redacted>.md) for exactly one key:** `version.create` returns to the Archon floor. 0157's "authoring delegates, disposition does not" was right about what a version *was* — but once the boundary carries the scope gate, cutting a new version is no longer authoring, it is the way *around* the gate, so create joins close as a disposition over scope.](<redacted>.md) | lifecycle / versioning / scope |
|
|
340
341
|
|
|
341
342
|
| 0246 | [**Catalog membership is a projection of the provisioning row, not a side effect of federation** ([task 1003576](https://cloudbongos.com/builders#/task/1003576) · goal 1000046, criterion `proj-detail-interview` / C3). The BV1.R18 proof ([task 1002337](https://cloudbongos.com/builders#/task/1002337)) demonstrated C3 against live and found its last clause — “then it publishes” — structurally false: the owner’s verdict reads `provisioning_instances` while the public map reads `platform_identity_projects`, and the ONLY bridge was `federateInstance → registerClient → upsertProject`, which `shouldFederate` refuses for anything but `co-tenant` (the wizard hard-codes `standalone`) and which runs ONCE, at provision time, through a COALESCE. So a wizard-made project was never catalogued at all and a co-tenant one that answered later never updated — `publishable: true`, `{"projects": []}`, forever. The same missing row is [task 1003580](https://cloudbongos.com/builders#/task/1003580)’s GAP 1: `POST /projects/invite` resolves against `platform_identity_sso_clients`, whose only writer is that same `registerClient`. Underneath is one conflation — federation mints CREDENTIALS and was incidentally the only thing that minted MEMBERSHIP, while [ADR 0182](<redacted>.md) D4 already fixed the map’s gate at IDENTITY, which has nothing to do with hosting topology. **Decision: the catalog row is a PROJECTION of the provisioning row, re-derived at every write that can change its inputs** — two halves, neither sufficient alone. (1) THE ROW SHAPE: every control-plane project gets both rows whatever its hosting shape, and the client row carries NO credential (`secret_hash = ''`, `redirect_uris = '{}'`), so **the shape is the enforcement** rather than a flag a caller could forget — `redirectUriAllowed` refuses `/sso/authorize`, `clientSecretValid` refuses `/sso/token`, `/sso/membership/check-in` and `/sso/activity/rollup`, and the one leg that checked NEITHER (`/sso/device/start`, which resolves a client from a user-typed origin) grew an explicit `clientIsCredentialed` guard. (2) THE TIMING: re-taken on create, on `PATCH …/detail`, on `PATCH …/settings` when the door changes, and on `POST …/domain` (which re-keys the row, retiring the abandoned origin — [ADR 0181](0181-teardown-state-gate-and-address-retention.md) §2’s leak, in the catalog instead of DNS). **NO MIGRATION** — `secret_hash` is `NOT NULL` and `''` satisfies it while hashing to nothing. `description` and `visibility` are ASSIGNED, not COALESCEd (clearing a description must UN-publish); `featured`/`tagline`/`art_url`/`template` are curation and stay untouched, so [ADR 0182](<redacted>.md) D3 holds. It grants nothing — the row is attribution per [ADR 0238](<redacted>.md), ownership stays `provisioning_instances.owner_builder_id` — and because it writes rows keyed on owner-supplied values (a slug, a BYO domain) each write carries its **claim rule in the ON CONFLICT action’s own WHERE**: the client is refreshed only `WHERE secret_hash = ''` (a real client’s origin, which binds the CLI mint audience, can never be rewritten) and the project only `WHERE owner_github_id = EXCLUDED.owner_github_id` (`NULL = anything` is not true, so the hand-seeded rows cannot be claimed). Writing `visibility` does not contradict [ADR 0182](<redacted>.md) D5: the hub authorizes nothing — the authority already ran on the provisioning plane ([ADR 0238](<redacted>.md)’s own-scoped, audited PATCH), and a project the control plane does NOT own has no provisioning row, so D5’s instance report (R08) stays the only channel for those. `tearing_down`/`torn_down` project `hidden`; a `no_address` instance is not catalogued at all. Travels on the kernel port `project.catalog`, the `project.publishGate` seam in the other direction ([ADR 0083](<redacted>.md) forbids the import). Rejected: extending `shouldFederate` to every shape (hands a real secret to instances whose web.env the platform does not write, and keeps the once-at-standup bug); bridging only at the publish moment (leaves the invite with nothing to resolve); registering only at standup (the bug itself); a `catalog_only` boolean column (needs a migration and is weaker than the shape); letting the bridge write `featured`; and backfilling the three existing catalog rows from code.](<redacted>.md) | provisioning / project creation |
|
|
342
343
|
|
|
@@ -1555,5 +1555,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1555
1555
|
landed since 1.19.551 with no explicit bump. run 34043771286. (task 1002620)
|
|
1556
1556
|
1.19.553 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1557
1557
|
landed since 1.19.552 with no explicit bump. run 34044208907. (task 1002620)
|
|
1558
|
+
1.19.554 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1559
|
+
landed since 1.19.553 with no explicit bump. run 34044540046. (task 1002620)
|
|
1558
1560
|
---------------------------------------------------------------------------
|
|
1559
1561
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.554",
|
|
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.554",
|
|
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.554",
|
|
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
|
@@ -49,7 +49,7 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
49
49
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
50
50
|
// the entry to that file. Look for a version's history there, not here.
|
|
51
51
|
// ---------------------------------------------------------------------------
|
|
52
|
-
const CORE_VERSION = '1.19.
|
|
52
|
+
const CORE_VERSION = '1.19.554'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
53
53
|
|
|
54
54
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
55
55
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|