@bongos/core 1.19.918 → 1.19.920
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 +88 -58
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +6 -0
- package/clients/bongos-client/index.cjs +6 -0
- package/clients/bongos-client/index.d.ts +8 -0
- package/clients/bongos-client/index.mjs +6 -0
- package/docs/adr/0319-a-removal-verdict-is-a-row-beside-the-question.md +69 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +134 -3
- package/docs/api-reference.md +8 -1
- package/docs/copy-inventory.md +15 -11
- package/docs/copy-registry.json +48 -12
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/deploy.html +1 -1
- package/modules/hall-ui/public/diagrams.html +1 -1
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/gate.html +1 -1
- package/modules/hall-ui/public/goal-inbox.js +102 -0
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +1 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/people.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/ranks.html +1 -1
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/style.css +12 -0
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/lifecycle/cascade.js +9 -1
- package/modules/lifecycle/migrations/lifecycle_009_removal_verdicts.sql +111 -0
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/removal-proposals.js +259 -0
- package/modules/lifecycle/routes/removal-proposals.js +140 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/hall-preview/refresh-fixtures.js +6 -0
- package/src/bongos/routes/me.js +9 -1
- package/src/module-api.js +1 -1
- package/tests/blockers_hall.mjs +16 -2
- package/tests/core_update_banner.mjs +28 -0
- package/tests/nav_permission_atoms.mjs +110 -0
- package/tests/removal_verdict.mjs +311 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# ADR 0319 — A removal verdict is a row beside the question, because both answers close it the same way
|
|
2
|
+
|
|
3
|
+
**Status:** accepted · **Date:** 2026-09-20 · **Task:** [task 1004095](https://cloudbongos.com/builders#/task/1004095) (goal 1000095 — BONGOS-V2)
|
|
4
|
+
|
|
5
|
+
**Related:** [ADR 0313](0313-a-removal-is-proposed-by-the-ship-and-confirmed-by-a-person.md) (the decision of record — the system proposes, a person confirms) · [ADR 0242](0242-a-cascade-is-a-declaration-table-on-the-event-that-already-exists.md) (the runner a rule is appended to, and may not fan out) · [ADR 0299](0299-the-nod-queue-stays-scoped-to-routed-filings.md) (why the nod queue is not widened) · [ADR 0312](0312-provenance-is-a-second-ideas-owned-edge-table.md) (the descent a confirmed removal dims)
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
[ADR 0313](0313-a-removal-is-proposed-by-the-ship-and-confirmed-by-a-person.md) settled that a ship which deletes an earlier shipped task's files files a **question**, and that a person answers it: Metic+, `task.promote`, on the existing awaiting-nod surface, with an unanswered proposal rotting on the normal clock. Task 1004090 built the question. Nothing could answer it.
|
|
10
|
+
|
|
11
|
+
Answering turns out to have one property that decides the whole design: **both answers close the proposal the same way.** The proposal is a question, not work — confirming leaves nothing to build, and so does dismissing. Either way the only sensible end state for that task row is `abandoned`, the ledger's one spelling of "finished with the project, not delivered" (`modules/lifecycle/task-status.js`).
|
|
12
|
+
|
|
13
|
+
So a status alone cannot be the record. "Yes, that work is gone" and "no, that was a refactor" would be the same row, and brightness — the live share of a descent, the next task in this chain — would have no way to tell the star it must dim from the one it must not. ADR 0313 already ruled out the obvious alternative: a `removed` task status, because `shipped` is terminal by design and a removal is "a fact about work's fate, recorded beside it, not a new terminal state that ship rollup would have to learn."
|
|
14
|
+
|
|
15
|
+
## Decision
|
|
16
|
+
|
|
17
|
+
### The verdict is its own row, and there is exactly one of them per proposal
|
|
18
|
+
|
|
19
|
+
`lifecycle_removal_verdicts` (migration `lifecycle_009`) holds `proposal_task_id` (**primary key**), `ship_task_id`, `verdict ∈ {confirmed, dismissed}`, `decided_by`, `decided_at`, `note`. It is the "beside it" ADR 0313 asked for. The verdict write and the status change commit in **one transaction**: a verdict with no closed task would be re-answerable, and a closed task with no verdict is precisely the erasure this exists to prevent.
|
|
20
|
+
|
|
21
|
+
The primary key on `proposal_task_id` is a decision, not a default. A second answer is **refused** (`ALREADY_DECIDED`, 409), never upserted and never treated as an idempotent success, because overwriting destroys the only record that a person answered differently. The proposal row is locked `FOR UPDATE` before any of the row-scoped checks, so two people pressing Confirm and Dismiss at the same moment cannot both read "undecided"; the second loses on the refusal rather than winning on write order. Amending a verdict is deliberately not built: if it is ever needed it is a second row and an append-only history, not an UPDATE.
|
|
22
|
+
|
|
23
|
+
`ALREADY_DECIDED` is checked **before** `PROPOSAL_CLOSED`, because an answered proposal is also abandoned — checking status first would tell someone "this is not an open question" when the useful answer is "it was dismissed, by builder 7, yesterday".
|
|
24
|
+
|
|
25
|
+
### The verdict is what this ships; the target list is not
|
|
26
|
+
|
|
27
|
+
The proposal names the affected tasks and paths as **prose in its body**, because a cascade files one task per rule per ship with no field for a target and ADR 0242 forbids widening the runner to fan out (owner decision, 2026-09-20, recorded in `cascade.js:removalProposalBody`). This ADR does not change that and does not parse it. A body is written for a person to read; a reader that silently depends on its line shape breaks the day somebody improves the wording.
|
|
28
|
+
|
|
29
|
+
What brightness gets from here is structured: the verdict, and `ship_task_id` — denormalised out of the proposal's `source_ref`, which encodes it as `cascade-removal-proposed-on-ship-<id>`, a string nothing can join on (the same complaint `ideas_007` makes about the same field). Brightness designs its own storage for targets when it needs one, which is where the owner put that decision.
|
|
30
|
+
|
|
31
|
+
A ref that cannot be parsed still yields an answerable proposal, with `ship_task_id` NULL. The verdict is the point; refusing to record one because a convenience column could not be filled would be the tail wagging the dog.
|
|
32
|
+
|
|
33
|
+
### The proposals get their own slice on the attention card, not a row in the nod queue
|
|
34
|
+
|
|
35
|
+
Spec decision 15 says "the existing awaiting-nod surface". The **surface** is the hall's one Needs-your-attention card; the **queue** is `listRoutedAwaitingNod`, which filters `origin = 'route'` on `ideas_task_lineage` and which [ADR 0299](0299-the-nod-queue-stays-scoped-to-routed-filings.md) decided — on live numbers — stays scoped to routed filings and is honestly named for that population. A removal proposal has no idea lineage at all, so widening that predicate to reach it would both break ADR 0299 and make an ideas-owned table answer a lifecycle question.
|
|
36
|
+
|
|
37
|
+
So: same card, same floor, separate slice (`#attention-removals`, straight after the nod), fed by its own routes.
|
|
38
|
+
|
|
39
|
+
### The permission is reused; the verb is not
|
|
40
|
+
|
|
41
|
+
`GET /removal-proposals` and `POST /removal-proposals/:id/decide` both gate on the **existing** `task.promote` atom — Metic+, the same floor as the nod, so a caller who can see the queue can act on it by construction rather than by coincidence. No new permission and no new page, as spec decision 15 requires.
|
|
42
|
+
|
|
43
|
+
A new *route* is nonetheless needed, and that is not a contradiction. The nod could reuse `POST /tasks/:id/promote` because promoting is exactly what a nod does; there is no existing route that means "that work is genuinely gone". `/tasks/:id/abandon` would close the question without recording which answer was given — the one thing this feature exists to produce.
|
|
44
|
+
|
|
45
|
+
The path is top-level (`/removal-proposals`, the `/override-requests` precedent) rather than `/tasks/removal-proposals`, because the manifest mounts the `tasks` route file first and its `/tasks/:id` would swallow a literal single segment declared in a later file — the trap `modules/ideas/routes/inbox.js` documents for `/inbox/awaiting-nod`, avoided here by not sharing the prefix.
|
|
46
|
+
|
|
47
|
+
### Rot is inherited, not rebuilt
|
|
48
|
+
|
|
49
|
+
An unanswered proposal sits at `backlog`, which is in `rot.TASK_ROTTABLE_STATUSES`, so it enters the existing rot feed on the project's clock with no code written for it — spec decision 14, and why that decision had no competing option. Answering it writes `abandoned`, which is terminal and not rottable, so it leaves the feed by the same mechanism. Both halves are pinned against the rot module's own exported vocabulary rather than asserted in a comment.
|
|
50
|
+
|
|
51
|
+
## Consequences
|
|
52
|
+
|
|
53
|
+
**A confirmed removal still changes nothing else.** No status on the removed work moves, no credit is clawed back, `shipped` stays terminal. The verdict is a fact recorded beside the work, and brightness is the only thing that will read it.
|
|
54
|
+
|
|
55
|
+
**A cascade task created before this shipped is answerable.** The predicate is `source = 'cascade'` plus the removal rule's ref prefix, both read from `cascade.js` rather than re-typed, so any proposal the rule has ever filed qualifies. In practice the set is empty: the rule could not fire in production until task 1004090's final commit.
|
|
56
|
+
|
|
57
|
+
**The rot feed shows these only to the owner.** A cascade-filed task has `created_by = NULL`, and `rottingFor` addresses a null-creator row solely to a caller holding `builder.roster.read` — the core_227 convention, deliberate rather than accidental, since there is no person a system-filed row could honestly be addressed to. The attention slice is not scoped that way: it shows every open proposal to every Metic+.
|
|
58
|
+
|
|
59
|
+
**A verdict cannot be corrected through the API.** Stated plainly because it will be noticed: the recovery for a mis-answered proposal is currently a row deletion by someone with SQL, and the right fix — should it ever be asked for — is an append-only amendment, not an UPDATE on this table.
|
|
60
|
+
|
|
61
|
+
## Alternatives rejected
|
|
62
|
+
|
|
63
|
+
- **The status alone as the record.** Both answers close the same way, so it records neither.
|
|
64
|
+
- **A `removed` task status, or reusing `merged_into_task_id`.** ADR 0313 rejected the first; the second is CHECK-constrained to `abandoned` rows and describes work cut *before* it landed.
|
|
65
|
+
- **Parsing the affected tasks out of the proposal body at confirm time.** Couples a machine read to prose written for a person, and the owner already placed that storage decision with brightness.
|
|
66
|
+
- **Widening the nod queue to carry removal proposals.** Breaks ADR 0299 and makes an ideas-owned lineage table answer a lifecycle question.
|
|
67
|
+
- **Two routes, `/confirm` and `/dismiss`.** The same act with opposite content; splitting it invites the halves to drift in what they close, record and refuse.
|
|
68
|
+
- **Upserting a second verdict, or treating it as an idempotent 200.** Destroys the record that a person answered differently.
|
|
69
|
+
- **A `LIKE 'prefix%'` match on `source_ref`.** A rule key may contain an underscore, and an underscore is a LIKE wildcard — a silent widening of the read that decides what may be answered.
|
package/docs/adr/README.md
CHANGED
|
@@ -409,4 +409,5 @@ This keeps the decision history honest and traceable.
|
|
|
409
409
|
| 0316 | [**The ignite floor is a scheduling dial, not the trusted tier** ([task 1004084](https://cloudbongos.com/builders#/task/1004084), goal 1000095 — BONGOS-V2). BV2 decision 3 makes the ignite floor — who may turn a quick idea straight into claimable work — a per-instance setting, and decision 16 adds the descent decay rate beside it, the two to share one home. Both the spec and the seeded task described the mechanism the same way: the floor IS the `METIC_PLUS` constant in `modules/ideas/routing.js`, so this is "a setting, not a refactor". Tracing the code before building refuted that: `METIC_PLUS` is spelled once but READ for four semantically different gates — the two `decideLanding` bug branches (land at `ready` or at `backlog`), `mayProposeCriterion` (who may file a `kind='criterion-proposal'`, a planning-scope write), and the protected-scope wall in `authorizeRoutedLanding` (who may route work into the permission / ship-grade / deploy / migration core). Only the first two are the ignite floor, so making the CONSTANT configurable would mean an instance setting its floor to `xenos` — exactly the "project of one wants every thought claimable" case the owner asked for — ALSO gaining the right to file criterion-proposals and to breach the protected core. The codebase already said so in three places nobody had read into the spec: the constant's own header ("It grants nothing — the server-side rank floor and the goal's scope wall are separate, load-bearing gates"), the protected-scope branch ("the two are layers, not alternatives"), and `tests/filing_abuse_matrix.mjs`, which models the widening as an ATTACK and pins the literal. Decision: a separate dial. `modules/ideas/config.js` + `config/ideas.neutral.json` carry both knobs on the `modules/government/config.js` precedence (env -> instance root `config/ideas.json` -> core root neutral; the ADR 0268 dual-root split matters, since one root reads the host pack out of `node_modules` where `npm ci` erases it). `igniteFloor` defaults to `metic`, resolving to exactly the `METIC_PLUS` set, and is threaded into `decideLanding` as a ready-made `trustedRanks` **Set** by the callers — `routing.js` is TOLD the floor and never reads config, staying pure and DB-free — while `METIC_PLUS` stays a FIXED constant for the other two gates, neither of which takes a floor argument (pinned structurally). `descentDecayRate` defaults to 0.5 and ships with no production caller, because R11 is its intended reader and depends on the unbuilt R08 — the same deliberate slice boundary `modules/government/config.js` documents for itself, and what decision 16 asked for. Everything fails to VANILLA rather than dead: a malformed pack, an unknown rank or an out-of-band rate falls back, and an unrecognised floor yields the DEFAULT set, never an empty one (failing closed would stop every ignition on a typo). Rejected: configuring `METIC_PLUS` as written (a security widening in a config file's clothes); splitting it into four named constants (the other three want the same fixed tier, and naming them invites configuring them); having `routing.js` read the config itself (makes the landing matrix impure and untestable at a different floor). Spec decision 3's "spelled in one place" describes the intent, not the mechanism; the spec was not edited and the task description was amended in flight.](0316-the-ignite-floor-is-not-the-trusted-tier.md) | ideas / permissions |
|
|
410
410
|
| 0315 | [**A plane with no proxy is refused, not given a no-op ingress** ([task 1004017](https://cloudbongos.com/builders#/task/1004017), goal 1000106 — BONGOS-V2). The piece [ADR 0306](0306-instance-supervision-is-a-seam-with-systemd-as-the-default.md) explicitly deferred: the self-host image ships no proxy, so provisioning step 6's `<slug>.caddy` snippet write and `systemctl reload caddy` cannot work there and a child instance gets no front door. **Two failure shapes, only one visible**: a DOMAINED child already half-refused, because `caddy adapt` fails with no binary and [ADR 0303](0303-provisioning-verifies-the-base-caddyfile-and-never-edits-it.md)'s own catch turns that into a gap — but the message told the operator to add a directive to `/etc/caddy/Caddyfile`, a file the container does not have and that ADR 0303 §1 forbids provisioning from creating; a DOMAINLESS child skipped that preflight entirely (it is gated on `inst.domain`), reached step 6 and died on the `sudo` write, an uncaught throw that bypasses the `{ok:false}` pending-retry path and hard-errors the instance on attempt one — a crash recorded where a refusal was the honest answer. Decision: `ingressPlaneGap(exec)` probes the plane with `caddy version` — one cheap unprivileged read, the same posture as `checkCaddyWiring` — and both standup legs refuse with `Ingress preflight failed: …`, deliberately NOT domain-gated, because the domainless child is the one 0303 could not see. **Only a missing binary proves there is no proxy**: under remote exec `boxExec` is `ssh <host> bash -s`, so an unreachable co-hosting box throws here too, and diagnosing a network blip as 'this plane has no proxy, provision elsewhere' would be a confident wrong answer that burns the intent's retry budget — exit 127 is read as proof, anything else answers 'could not confirm a reverse proxy on this plane' with the real cause leading. The three refusals are ordered **capacity → ingress → wiring**, cheapest and most fundamental first and all before any mutation, which moves 0303's preflight behind the ADR 0285 capacity measurement; both still refuse before anything changes, so only the precedence between two refusals moves. **No ingress seam and no no-op backend**, despite the obvious symmetry with 0306: a `none` implementation whose `writeSite`/`reload` are no-ops lets the standup SUCCEED while leaving the child unreachable — neither half of the task's done-when, and exactly the silently-broken instance it exists to prevent. Does not decide: what a child's front door IS on a container plane (a published port, a bundled proxy, an explicit localhost-only shape) — a future ADR that adds one replaces this refusal rather than extending it. Teardown is left alone on purpose: it already reloads `allowFail`, and refusing there would strand a row instead of cleaning it.](0315-a-plane-with-no-proxy-is-refused-not-given-a-no-op-ingress.md) | provisioning / ingress |
|
|
411
411
|
| 0317 | [**A plane that cannot give an instance its own account is refused, not given a shared uid** ([task 1004018](https://cloudbongos.com/builders#/task/1004018), goal 1000106 — BONGOS-V2). The last of the three container gaps [ADR 0306](0306-instance-supervision-is-a-seam-with-systemd-as-the-default.md) deferred, and the sibling of [ADR 0315](0315-a-plane-with-no-proxy-is-refused-not-given-a-no-op-ingress.md). [ADR 0281](0281-an-instance-identity-is-its-own-unix-account-and-pg-role.md) makes an instance's identity its own unix account, and the unit's `User=`/`Group=` is what stops one co-tenant reading a sibling's `/proc/<pid>/environ` — where every instance's `EnvironmentFile` carries its GitHub App private key and OAuth client secret (audit B3). `useradd` needs uid 0; the self-host image is `node:22-bookworm-slim` running `USER node` with **no sudo**, so there the account cannot be created at all. **Two failure shapes, neither honest**: today step 1c's `boxExec(ensureInstanceUserCmd(...))` is not `allowFail`, so it throws AFTER every preflight passed, AFTER the status flip to `provisioning`, and — for a standalone — after a repo pull and an `npm ci`, recording a crash with the box half-changed; and tomorrow it goes quiet, because a compose self-host only stops short of step 1c by ADR 0315's coincidence, and 0315 says in its own consequences that a future front door **replaces** that refusal. Decision: `accountPlaneGap(exec, { privileged })` in `provision-repo.js` probes with `${sudo}sh -c 'test "$(id -u)" = 0'` and `provisionInstance` refuses with `Account preflight failed: …` before any mutation. **The probe runs the SAME escalation the mutation will** (`PROVISION_SUDO=1` renders the same `sudo ` prefix `ensureInstanceUserCmd` uses), so a pass is evidence rather than inference. **`sh -c`, not a bare `test`, is load-bearing**: under sudo the outer unprivileged shell would expand `$(id -u)` first and answer about the wrong user, passing on exactly the plane this refuses — and for the same reason there is no pipeline, whose exit status is its last stage's. **Only a shell that ran and answered proves it**: `boxExec` is `ssh <host> bash -s` under remote exec, so exit 1 (`test` said no) and 127 (no sudo binary) are read as proof while ssh's own 255 answers "could not confirm" with the real cause leading — ADR 0315's rule applied to a different question. Ordered **third**, after capacity and ingress and before 0303's wiring check: the first three ask about the PLANE, 0303 asks about this instance's DOMAIN, and 0315's order is extended rather than rearranged. **Rejected: accepting it** — documenting a self-host container as single-tenant with a shared uid is a silent downgrade of the vector 0281 closed, observable nowhere on the running box, and "single-tenant" is not what a container hosting several child projects is; **an identity seam with a container implementation**, because nothing exists to put behind it and a seam whose second backend does nothing is 0315's no-op wearing a seam's clothes; **probing `useradd --help` / `command -v useradd`**, which succeed unprivileged on the very image this refuses; **`${sudo}id -u` compared against `0`**, which decides on captured stdout where every sibling preflight and every test fake treats exit status as the contract; and **refusing inside step 1c**, which keeps the refusal after the status flip. Not covered, deliberately: root-but-no-`useradd` (an Alpine-style plane fails loudly at step 1c as today), and teardown (0315's reason — a refusal there strands a row instead of cleaning it). No `PROVISION_ALLOW_SHARED_UID`: a runner either reaches root on this plane or it does not.](0317-a-plane-that-cannot-give-an-instance-its-own-account-is-refused.md) | provisioning / identity |
|
|
412
|
+
| 0319 | [**A removal verdict is a row beside the question, because both answers close it the same way** ([task 1004095](https://cloudbongos.com/builders#/task/1004095), goal 1000095 — BONGOS-V2). [ADR 0313](0313-a-removal-is-proposed-by-the-ship-and-confirmed-by-a-person.md) settled that a ship deleting an earlier shipped task’s files files a QUESTION and a person answers it; task 1004090 built the question and nothing could answer it. Building the answer surfaced the property that decides the design: a proposal is a question, not work, so CONFIRMING and DISMISSING both leave nothing to build and both end the task at `abandoned` — meaning a status alone records neither, and brightness could not tell the star it must dim from the one it must not. ADR 0313 had already rejected the obvious alternative (a `removed` status; `shipped` is terminal and much of the ledger leans on it — a removal is a fact recorded BESIDE work, not a new terminal state). Decision: `lifecycle_removal_verdicts` (migration `lifecycle_009`), PRIMARY KEY on `proposal_task_id` so one proposal holds one answer ever — a second decide is REFUSED (409), never upserted and never an idempotent 200, because overwriting destroys the record that a person answered differently; the row is locked `FOR UPDATE` first, so simultaneous Confirm and Dismiss cannot both read “undecided”, and `ALREADY_DECIDED` is checked BEFORE `PROPOSAL_CLOSED` because an answered proposal is also abandoned and the useful message is the one naming the earlier verdict. The verdict and the status change commit in ONE transaction. What this does NOT ship is the target list: the affected tasks and paths stay PROSE in the proposal body (the owner’s 2026-09-20 decision, since [ADR 0242](0242-a-cascade-is-a-declaration-table-on-the-event-that-already-exists.md) forbids widening the runner to fan out), and nothing here parses it — brightness designs that storage. What brightness does get is structured: the verdict plus `ship_task_id`, denormalised out of the `source_ref` that encodes it as a string nothing can join on. Surface: spec decision 15’s “existing awaiting-nod surface” is the CARD, not the QUEUE — [ADR 0299](0299-the-nod-queue-stays-scoped-to-routed-filings.md) keeps that queue scoped to routed filings and a removal proposal has no idea lineage at all, so it gets its own slice on the same card at the same floor. The PERMISSION is reused (`task.promote`, Metic+, read and write both) and the VERB is not: no existing route means “that work is genuinely gone”, and `/tasks/:id/abandon` would close the question without recording which answer was given. Top-level `/removal-proposals` rather than `/tasks/removal-proposals`, because the manifest mounts `tasks` first and its `/tasks/:id` swallows a later file’s literal segment. Rot is INHERITED: `backlog` is rottable and `abandoned` is not, so an unanswered proposal rots and an answered one leaves the feed with no code written for either — pinned against the rot module’s own exported vocabulary. Rejected: the status as the record; parsing the body at confirm time; widening the nod queue; separate `/confirm` and `/dismiss` routes; upserting a second verdict; a `LIKE ‘prefix%’` match (a rule key may contain an underscore, which is a LIKE wildcard).](0319-a-removal-verdict-is-a-row-beside-the-question.md) | ideas / lifecycle |
|
|
412
413
|
| 0318 | [**Listing a file for task affinity is not claiming to own it** ([task 1004110](https://cloudbongos.com/builders#/task/1004110), goal 1000095 — BONGOS-V2). The case [ADR 0314](0314-module-protection-is-per-path-not-per-module.md) named and left open, closed by neither of the two routes it predicted. `builder-settings` derived `protected` — Archon-only to scope a goal to — on exactly one glob, `src/bongos/routes/me.js`, caught by the blanket `src/bongos/routes/` prefix. **That glob was never an ownership claim:** `MODULE_GLOBS` listed it for task AFFINITY and said in the same comment that the `GET /me` aggregator stays a thin core facade, and `isProtected()` cannot tell the two apart. **Both predicted routes were measured and rejected.** Narrowing the blanket prefix: all eighteen files under `src/bongos/routes/` are protected by that prefix AND NOTHING ELSE, so narrowing unwalls `auth.js`, `builders.js`, `backup.js` and `core-update.js` together — now pinned as a tripwire test rather than left as a caution. Moving `me.js` into `modules/builder-settings/`: the aggregator resolves TEN module ports (`lifecycle`, `reward`, `onboarding`, `government`, `specialities`, `grade`, `box`, `art` ×2, `discord`) and `builder-settings` is one of them, so hosting it inside one module means resolving nine foreign ports from the inside — the `core -> module` edge [ADR 0083](0083-modular-architecture-strangler-build-plan.md) rule 1 forbids. Decision: **ownership follows what the file is** — `me.js` moves to `kernel`, which already owns `src/bongos/routes/security.js`. This is 0314 one step on: owning the command is not owning the authority, and **being listed next to a module is not owning it either**. **Rejected: a general affinity tier** in `MODULE_GLOBS` mirroring `isCommandPath` — the audit does not support it. Three other modules (`kernel`, `devsecops`, `methodology`) are also protected only by paths outside their own directory, but in each the foreign path genuinely IS the domain; `builder-settings` was the only module protected by a path its own source called core, so a new tier would be a new place for a hole to hide, built for a population of one. `protectedModules()` goes 9 -> 8. **The wall that matters does not move:** a sub-Metic builder touching `/me` is still refused by `checkPermissionPaths`, asserted directly rather than assumed. The sixteen still-unowned route files remain [task 1004116](https://cloudbongos.com/builders#/task/1004116), whose option (a) this de-risks.](0318-listing-a-file-for-task-affinity-is-not-owning-it.md) | permissions / module scope |
|
package/docs/api/openapi.json
CHANGED
|
@@ -213,6 +213,10 @@
|
|
|
213
213
|
"name": "public",
|
|
214
214
|
"description": "Endpoints under `/api/bongos/public`."
|
|
215
215
|
},
|
|
216
|
+
{
|
|
217
|
+
"name": "removal-proposals",
|
|
218
|
+
"description": "Endpoints under `/api/bongos/removal-proposals`."
|
|
219
|
+
},
|
|
216
220
|
{
|
|
217
221
|
"name": "scouting",
|
|
218
222
|
"description": "Endpoints under `/api/bongos/scouting`."
|
|
@@ -14126,6 +14130,106 @@
|
|
|
14126
14130
|
"security": []
|
|
14127
14131
|
}
|
|
14128
14132
|
},
|
|
14133
|
+
"/removal-proposals": {
|
|
14134
|
+
"get": {
|
|
14135
|
+
"operationId": "get_removal_proposals",
|
|
14136
|
+
"tags": [
|
|
14137
|
+
"removal-proposals"
|
|
14138
|
+
],
|
|
14139
|
+
"summary": "GET /removal-proposals",
|
|
14140
|
+
"description": "GET /removal-proposals — the removal questions nobody has answered yet. rank: metic+archon (task.promote). No client-side rank gate is needed or wanted in the hall: a non-triager's fetch 403s and the slice renders nothing. One gate, on the server, beats a client check that can disagree with it.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `task.promote` (all required).",
|
|
14141
|
+
"x-rank": "metic+archon",
|
|
14142
|
+
"x-source": "modules/lifecycle/routes/removal-proposals.js",
|
|
14143
|
+
"x-permissions": [
|
|
14144
|
+
"task.promote"
|
|
14145
|
+
],
|
|
14146
|
+
"responses": {
|
|
14147
|
+
"200": {
|
|
14148
|
+
"description": "Success.",
|
|
14149
|
+
"content": {
|
|
14150
|
+
"application/json": {
|
|
14151
|
+
"schema": {
|
|
14152
|
+
"$ref": "#/components/schemas/GetRemovalProposalsResponse"
|
|
14153
|
+
}
|
|
14154
|
+
}
|
|
14155
|
+
}
|
|
14156
|
+
},
|
|
14157
|
+
"400": {
|
|
14158
|
+
"$ref": "#/components/responses/BadRequest"
|
|
14159
|
+
},
|
|
14160
|
+
"401": {
|
|
14161
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
14162
|
+
},
|
|
14163
|
+
"403": {
|
|
14164
|
+
"$ref": "#/components/responses/Forbidden"
|
|
14165
|
+
}
|
|
14166
|
+
},
|
|
14167
|
+
"security": [
|
|
14168
|
+
{
|
|
14169
|
+
"builderSession": []
|
|
14170
|
+
}
|
|
14171
|
+
]
|
|
14172
|
+
}
|
|
14173
|
+
},
|
|
14174
|
+
"/removal-proposals/{id}/decide": {
|
|
14175
|
+
"post": {
|
|
14176
|
+
"operationId": "post_removal_proposals_id_decide",
|
|
14177
|
+
"tags": [
|
|
14178
|
+
"removal-proposals"
|
|
14179
|
+
],
|
|
14180
|
+
"summary": "POST /removal-proposals/:id/decide",
|
|
14181
|
+
"description": "POST /removal-proposals/:id/decide — confirm or dismiss ONE proposal. ONE ROUTE WITH AN ENUM rather than /confirm and /dismiss, because the two answers are the same act with opposite content and splitting them would invite the two halves to drift in what they close, what they record and what they refuse. The enum is validated against an allow-list in the domain layer and by the CHECK in lifecycle_009 — neither admits a value the other does not. rank: metic+archon (task.promote), the same atom as the read above.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `task.promote` (all required).",
|
|
14182
|
+
"x-rank": "metic+archon",
|
|
14183
|
+
"x-source": "modules/lifecycle/routes/removal-proposals.js",
|
|
14184
|
+
"x-permissions": [
|
|
14185
|
+
"task.promote"
|
|
14186
|
+
],
|
|
14187
|
+
"parameters": [
|
|
14188
|
+
{
|
|
14189
|
+
"name": "id",
|
|
14190
|
+
"in": "path",
|
|
14191
|
+
"required": true,
|
|
14192
|
+
"schema": {
|
|
14193
|
+
"type": "string"
|
|
14194
|
+
},
|
|
14195
|
+
"description": "Path parameter `id`."
|
|
14196
|
+
}
|
|
14197
|
+
],
|
|
14198
|
+
"requestBody": {
|
|
14199
|
+
"required": true,
|
|
14200
|
+
"content": {
|
|
14201
|
+
"application/json": {
|
|
14202
|
+
"schema": {
|
|
14203
|
+
"$ref": "#/components/schemas/PostRemovalProposalsIdDecideRequest"
|
|
14204
|
+
}
|
|
14205
|
+
}
|
|
14206
|
+
},
|
|
14207
|
+
"x-validated": true
|
|
14208
|
+
},
|
|
14209
|
+
"responses": {
|
|
14210
|
+
"200": {
|
|
14211
|
+
"description": "Success."
|
|
14212
|
+
},
|
|
14213
|
+
"400": {
|
|
14214
|
+
"$ref": "#/components/responses/ValidationFailed"
|
|
14215
|
+
},
|
|
14216
|
+
"401": {
|
|
14217
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
14218
|
+
},
|
|
14219
|
+
"403": {
|
|
14220
|
+
"$ref": "#/components/responses/Forbidden"
|
|
14221
|
+
},
|
|
14222
|
+
"404": {
|
|
14223
|
+
"$ref": "#/components/responses/NotFound"
|
|
14224
|
+
}
|
|
14225
|
+
},
|
|
14226
|
+
"security": [
|
|
14227
|
+
{
|
|
14228
|
+
"builderSession": []
|
|
14229
|
+
}
|
|
14230
|
+
]
|
|
14231
|
+
}
|
|
14232
|
+
},
|
|
14129
14233
|
"/scouting": {
|
|
14130
14234
|
"get": {
|
|
14131
14235
|
"operationId": "get_scouting",
|
|
@@ -20092,6 +20196,17 @@
|
|
|
20092
20196
|
"page"
|
|
20093
20197
|
]
|
|
20094
20198
|
},
|
|
20199
|
+
"GetRemovalProposalsResponse": {
|
|
20200
|
+
"type": "object",
|
|
20201
|
+
"properties": {
|
|
20202
|
+
"pending": {},
|
|
20203
|
+
"count": {}
|
|
20204
|
+
},
|
|
20205
|
+
"required": [
|
|
20206
|
+
"pending",
|
|
20207
|
+
"count"
|
|
20208
|
+
]
|
|
20209
|
+
},
|
|
20095
20210
|
"GetScoutingHandleResponse": {
|
|
20096
20211
|
"type": "object",
|
|
20097
20212
|
"properties": {
|
|
@@ -23988,6 +24103,22 @@
|
|
|
23988
24103
|
],
|
|
23989
24104
|
"additionalProperties": false
|
|
23990
24105
|
},
|
|
24106
|
+
"PostRemovalProposalsIdDecideRequest": {
|
|
24107
|
+
"type": "object",
|
|
24108
|
+
"properties": {
|
|
24109
|
+
"verdict": {
|
|
24110
|
+
"type": "string"
|
|
24111
|
+
},
|
|
24112
|
+
"note": {
|
|
24113
|
+
"type": "string",
|
|
24114
|
+
"maxLength": 2000
|
|
24115
|
+
}
|
|
24116
|
+
},
|
|
24117
|
+
"required": [
|
|
24118
|
+
"verdict"
|
|
24119
|
+
],
|
|
24120
|
+
"additionalProperties": false
|
|
24121
|
+
},
|
|
23991
24122
|
"PostSearchMarkRequest": {
|
|
23992
24123
|
"type": "object",
|
|
23993
24124
|
"properties": {
|
|
@@ -25421,9 +25552,9 @@
|
|
|
25421
25552
|
"description": "A required dependency/feature is not configured or is temporarily down."
|
|
25422
25553
|
}
|
|
25423
25554
|
},
|
|
25424
|
-
"x-endpoint-count":
|
|
25425
|
-
"x-schema-count":
|
|
25555
|
+
"x-endpoint-count": 403,
|
|
25556
|
+
"x-schema-count": 442,
|
|
25426
25557
|
"x-undocumented-bodies": 9,
|
|
25427
|
-
"x-response-schemas":
|
|
25558
|
+
"x-response-schemas": 301,
|
|
25428
25559
|
"x-generated-by": "scripts/gds/gen-api-docs.js"
|
|
25429
25560
|
}
|
package/docs/api-reference.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Bongos API reference
|
|
4
4
|
|
|
5
|
-
> **Generated from the live route files** — the route file is authoritative.
|
|
5
|
+
> **Generated from the live route files** — the route file is authoritative. 403 endpoints across 65 route files.
|
|
6
6
|
> Machine-readable spec: [`docs/api/openapi.json`](api/openapi.json) (OpenAPI 3.1). Rendered docs site: **`/docs`** (e.g. `cloudbongos.com/docs`).
|
|
7
7
|
|
|
8
8
|
Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust-boundary-server-enforced-permissions.md)): `public` < `any-builder` < `metic+archon` < `archon`.
|
|
@@ -558,6 +558,13 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust
|
|
|
558
558
|
| GET | `/api/bongos/public/uptime` | `public` | — | Uptime: current status + 90-day history bucketed by hour. |
|
|
559
559
|
| GET | `/api/bongos/public/versions` | `public` | — | read-only surface for the status dashboard (status.<apex>). |
|
|
560
560
|
|
|
561
|
+
## `removal-proposals` (2)
|
|
562
|
+
|
|
563
|
+
| Method | Path | Rank | Body | Description |
|
|
564
|
+
|---|---|---|---|---|
|
|
565
|
+
| GET | `/api/bongos/removal-proposals` | `metic+archon` | — | GET /removal-proposals — the removal questions nobody has answered yet. |
|
|
566
|
+
| POST | `/api/bongos/removal-proposals/:id/decide` | `metic+archon` | `verdict`, `note` | POST /removal-proposals/:id/decide — confirm or dismiss ONE proposal. |
|
|
567
|
+
|
|
561
568
|
## `scouting` (2)
|
|
562
569
|
|
|
563
570
|
| Method | Path | Rank | Body | Description |
|
package/docs/copy-inventory.md
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
> Deterministic on a given tree — regenerate and commit rather than hand-editing.
|
|
5
5
|
> Machine-readable twin: [`copy-registry.json`](copy-registry.json).
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**1832 strings** across 3 surfaces · 1589 distinct pieces of copy · 1832 certain, 0 heuristic · 166 shown in more than one place.
|
|
8
8
|
|
|
9
9
|
## Surfaces
|
|
10
10
|
|
|
11
11
|
| surface | lives at | files | strings |
|
|
12
12
|
|---|---|---|---|
|
|
13
|
-
| `builders-hall` | `modules/hall-ui/public` | 84 |
|
|
13
|
+
| `builders-hall` | `modules/hall-ui/public` | 84 | 1342 |
|
|
14
14
|
| `status-dashboard` | `modules/status-ui/public` | 4 | 100 |
|
|
15
15
|
| `landing` | `modules/public-landing/public` | 5 | 390 |
|
|
16
16
|
|
|
@@ -78,7 +78,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
78
78
|
| `996edffc1966` | 2x | Access requests | modules/hall-ui/public/watch.html:46, modules/hall-ui/public/watch.html:61 |
|
|
79
79
|
| … | | _126 more in the JSON registry_ | |
|
|
80
80
|
|
|
81
|
-
## `builders-hall` —
|
|
81
|
+
## `builders-hall` — 1342 strings
|
|
82
82
|
|
|
83
83
|
| id | conf | text | where |
|
|
84
84
|
|---|---|---|---|
|
|
@@ -125,6 +125,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
125
125
|
| `5723baf26b2d` | certain | A quick idea is a thought filed fast. It is not an unfinished Full Idea — only its author can offer it up for | `modules/hall-ui/public/idea.js:260` |
|
|
126
126
|
| `23dc5028183a` | certain | A quick report — title, what's going on, and where it belongs. Pick a goal and it lands there as a task the mo | `modules/hall-ui/public/ideas.html:60` |
|
|
127
127
|
| `a1f06e5c11ae` | certain | A quiet place to tend the words and art the project shows people. | `modules/hall-ui/public/studio.html:38` |
|
|
128
|
+
| `08c1f66fd572` | certain | A refactor, a move, or work already superseded | `modules/hall-ui/public/goal-inbox.js:336` |
|
|
128
129
|
| `244ea2a3564e` | certain | A role says how you work. A speciality says what you work on — and how you, as someone who knows the subject, | `modules/hall-ui/public/settings.html:253` |
|
|
129
130
|
| `eff6b977b1d2` | certain | A self-drawing map of the codebase: what each module is, and what depends on it. Redrawn from the code on ever | `modules/hall-ui/public/atlas.html:31` |
|
|
130
131
|
| `f3d6ed7c0d2c` | certain | A shipped task cannot be retired — it is immutable history now. | `modules/hall-ui/public/task.js:751` |
|
|
@@ -462,7 +463,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
462
463
|
| `4794a991de3d` | certain | Getting started | `modules/hall-ui/public/hall-widgets.js:59` |
|
|
463
464
|
| `a28b1a567f1a` | certain | GitHub username | `modules/hall-ui/public/hall-render.js:294` |
|
|
464
465
|
| `8e52f0649662` | certain | Give the category a label. | `modules/hall-ui/public/project-settings.js:189` |
|
|
465
|
-
| `79934b879a12` | certain | Give the nod | `modules/hall-ui/public/goal-inbox.js:
|
|
466
|
+
| `79934b879a12` | certain | Give the nod | `modules/hall-ui/public/goal-inbox.js:258` |
|
|
466
467
|
| `988e2c60df3d` | certain | Goal archived. | `modules/hall-ui/public/goals-render.js:1048` |
|
|
467
468
|
| `9e70797b7e97` | certain | Goal id | `modules/hall-ui/public/goals-render.js:830` |
|
|
468
469
|
| `529460e82058` | certain | Goal invitations to you | `modules/hall-ui/public/goal-inbox.js:113` |
|
|
@@ -621,7 +622,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
621
622
|
| `31e91ee6ce03` | certain | Name must be {…} characters or fewer. | `modules/hall-ui/public/settings.js:766` |
|
|
622
623
|
| `5e8199266a8a` | certain | Name unvouched | `modules/hall-ui/public/watch.js:374` |
|
|
623
624
|
| `3970d299c95a` | certain | Needs work | `modules/hall-ui/public/studio.js:162` |
|
|
624
|
-
| `7def635421ea` | certain | Needs your attention | `modules/hall-ui/public/goal-inbox.js:
|
|
625
|
+
| `7def635421ea` | certain | Needs your attention | `modules/hall-ui/public/goal-inbox.js:557` |
|
|
625
626
|
| `87f09d983f6c` | certain | New category label | `modules/hall-ui/public/project-settings.js:138` |
|
|
626
627
|
| `d427d9dab01c` | certain | New claims are paused: {…} confirmed {…} can't land yet, so {…} reward is pending. Rebase and re-ship, or rele | `modules/hall-ui/public/hall-render.js:674` |
|
|
627
628
|
| `7fcc2583c372` | certain | New custom rank | `modules/hall-ui/public/government.html:79` |
|
|
@@ -952,6 +953,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
952
953
|
| `282c3fa2d456` | certain | That is the wording it already has. Change something, or file a flag instead if you only want to mark it. | `modules/hall-ui/public/copy-desk.js:438` |
|
|
953
954
|
| `c0e28f30524d` | certain | That project's own standing, as reported by that project. | `modules/hall-ui/public/profile.js:314` |
|
|
954
955
|
| `e99d4a811e92` | certain | That request is no longer available. | `modules/hall-ui/public/profile-connections.js:270` |
|
|
956
|
+
| `34017119ce22` | certain | That work is genuinely gone | `modules/hall-ui/public/goal-inbox.js:336` |
|
|
955
957
|
| `6e5a324b2972` | certain | That's you | `modules/hall-ui/public/watch.js:803` |
|
|
956
958
|
| `50198c465225` | certain | The 16-value rank enum in three bands, which ranks are live, and the permissions each rank holds. | `modules/hall-ui/public/diagrams.html:59` |
|
|
957
959
|
| `c5e6bdd32fe3` | certain | The Board Room is open to the builders this project’s constitution seats on its board. | `modules/hall-ui/public/board-room.js:436` |
|
|
@@ -1266,7 +1268,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1266
1268
|
| `99f88f2af2ee` | certain | of work — a tangent, an investigation, an unprompted suggestion — instead of staying on your claimed task. It | `modules/hall-ui/public/settings.html:316` |
|
|
1267
1269
|
| `5e7a2220c6a9` | certain | of {…} | `modules/hall-ui/public/roadmap.js:288` |
|
|
1268
1270
|
| `759c12b5f983` | certain | oldest first | `modules/hall-ui/public/hall-widgets.js:134` |
|
|
1269
|
-
| `cbaf58cdbe35` | certain | open the queue | `modules/hall-ui/public/goal-inbox.js:
|
|
1271
|
+
| `cbaf58cdbe35` | certain | open the queue | `modules/hall-ui/public/goal-inbox.js:521` |
|
|
1270
1272
|
| `af3a8e0fe6a8` | certain | open your goals | `modules/hall-ui/public/goal-inbox.js:121` |
|
|
1271
1273
|
| `4f279d68175f` | certain | open, on the Security tab | `modules/hall-ui/public/watch.html:47` |
|
|
1272
1274
|
| `7b85e6494596` | certain | opens a new tab | `modules/hall-ui/public/palette.js:190` |
|
|
@@ -1375,7 +1377,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1375
1377
|
| `b3757ba3bb46` | certain | {…} · Stored encrypted; never shown again after saving. | `modules/hall-ui/public/settings.js:990` |
|
|
1376
1378
|
| `a6e4ed98fbea` | certain | {…} · revoke it any time under Active sessions. | `modules/hall-ui/public/settings.js:922` |
|
|
1377
1379
|
| `d73256fbc395` | certain | {…} · who votes, and how it {…} | `modules/hall-ui/public/board-room.js:357` |
|
|
1378
|
-
| `cab2a4296aec` | certain | {…} · {…} · {…}{…} | `modules/hall-ui/public/goal-inbox.js:
|
|
1380
|
+
| `cab2a4296aec` | certain | {…} · {…} · {…}{…} | `modules/hall-ui/public/goal-inbox.js:420` |
|
|
1379
1381
|
| `085f93f3f775` | certain | {…} — see all standings | `modules/hall-ui/public/hall-render.js:80` |
|
|
1380
1382
|
| `f75ee96a7511` | certain | {…} — total payout from shipped work. | `modules/hall-ui/public/profile.js:94` |
|
|
1381
1383
|
| `2a2728a4ec4f` | certain | {…}% of tasks complete | `modules/hall-ui/public/roadmap.js:63` |
|
|
@@ -1387,6 +1389,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1387
1389
|
| `499916045b19` | certain | {…}{…} asks to join | `modules/hall-ui/public/goal-inbox.js:69` |
|
|
1388
1390
|
| `97e27ab169ae` | certain | {…}{…} session{…} across {…} builder{…}. | `modules/hall-ui/public/sessions.js:338` |
|
|
1389
1391
|
| `3d74668f69e5` | certain | {…}{…} · {…} | `modules/hall-ui/public/task.js:653` |
|
|
1392
|
+
| `7f79bedecf60` | certain | {…}{…} — open it to see what went | `modules/hall-ui/public/goal-inbox.js:336` |
|
|
1390
1393
|
| `9588652a7aff` | certain | · follows your profile setting | `modules/hall-ui/public/settings.js:566` |
|
|
1391
1394
|
| `4a5ff4f662df` | certain | — ${…} of ${…} this month ({…}%). {…} | `modules/hall-ui/public/hall-render.js:517` |
|
|
1392
1395
|
| `a6ae9cbdee30` | certain | — all builders — | `modules/hall-ui/public/builders.js:713` |
|
|
@@ -1396,14 +1399,15 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1396
1399
|
| `da9962dc1729` | certain | — or open any diagram fullscreen. | `modules/hall-ui/public/diagrams.html:33` |
|
|
1397
1400
|
| `e5c5e94152b3` | certain | — that's all the tooling you need up front. | `modules/hall-ui/public/hall-render.js:890` |
|
|
1398
1401
|
| `392afc533191` | certain | — type | `modules/hall-ui/public/hall-render.js:825` |
|
|
1399
|
-
| `ff7514f48a7b` | certain | — untouched {…}+ days | `modules/hall-ui/public/goal-inbox.js:
|
|
1402
|
+
| `ff7514f48a7b` | certain | — untouched {…}+ days | `modules/hall-ui/public/goal-inbox.js:457` |
|
|
1400
1403
|
| `d3afa6d6dd9e` | certain | — your words, in your voice. Needed for a note or a follow-up. | `modules/hall-ui/public/studio.js:162` |
|
|
1401
1404
|
| `68b547166d48` | certain | — {…} confirmed {…} can't land yet, so {…} reward is pending (it lands on ship). Rebase + re-ship or release { | `modules/hall-ui/public/hall-render.js:558` |
|
|
1402
1405
|
| `5b9fa7cb4fb0` | certain | …and {…} more waiting — | `modules/hall-ui/public/goal-inbox.js:121` |
|
|
1403
|
-
| `b2a04abfead0` | certain | …{…} more awaiting a nod. | `modules/hall-ui/public/goal-inbox.js:
|
|
1404
|
-
| `
|
|
1406
|
+
| `b2a04abfead0` | certain | …{…} more awaiting a nod. | `modules/hall-ui/public/goal-inbox.js:278` |
|
|
1407
|
+
| `f16717b3e122` | certain | …{…} more awaiting an answer. | `modules/hall-ui/public/goal-inbox.js:358` |
|
|
1408
|
+
| `616fb9adf3cf` | certain | …{…} more going quiet. | `modules/hall-ui/public/goal-inbox.js:447` |
|
|
1405
1409
|
| `e35f11bdc5d7` | certain | …{…} more open ideas — triage clears them one at a time. | `modules/hall-ui/public/builders.js:895` |
|
|
1406
|
-
| `5f1d914ad45e` | certain | …{…} more — | `modules/hall-ui/public/goal-inbox.js:
|
|
1410
|
+
| `5f1d914ad45e` | certain | …{…} more — | `modules/hall-ui/public/goal-inbox.js:521` |
|
|
1407
1411
|
| `de8fa5223aee` | certain | ‹ Prev | `modules/hall-ui/public/hall-kit.js:74` |
|
|
1408
1412
|
| `d4b84b7fbdeb` | certain | ← All goals | `modules/hall-ui/public/goals.html:80` |
|
|
1409
1413
|
| `7926e30955d2` | certain | ← Back to goal | `modules/hall-ui/public/goals.html:184` |
|
package/docs/copy-registry.json
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"generator": "scripts/gds/copy-inventory.js",
|
|
4
4
|
"goal": 1000074,
|
|
5
5
|
"counts": {
|
|
6
|
-
"strings":
|
|
7
|
-
"unique_ids":
|
|
8
|
-
"certain":
|
|
6
|
+
"strings": 1832,
|
|
7
|
+
"unique_ids": 1589,
|
|
8
|
+
"certain": 1832,
|
|
9
9
|
"heuristic": 0,
|
|
10
10
|
"duplicate_ids": 166
|
|
11
11
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
15
|
"files": 84,
|
|
16
|
-
"strings":
|
|
16
|
+
"strings": 1342
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -2164,6 +2164,15 @@
|
|
|
2164
2164
|
"origin": "html-text",
|
|
2165
2165
|
"confidence": "certain"
|
|
2166
2166
|
},
|
|
2167
|
+
{
|
|
2168
|
+
"id": "08c1f66fd572",
|
|
2169
|
+
"surface": "builders-hall",
|
|
2170
|
+
"text": "A refactor, a move, or work already superseded",
|
|
2171
|
+
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
2172
|
+
"line": 336,
|
|
2173
|
+
"origin": "js-markup",
|
|
2174
|
+
"confidence": "certain"
|
|
2175
|
+
},
|
|
2167
2176
|
{
|
|
2168
2177
|
"id": "244ea2a3564e",
|
|
2169
2178
|
"surface": "builders-hall",
|
|
@@ -5202,7 +5211,7 @@
|
|
|
5202
5211
|
"surface": "builders-hall",
|
|
5203
5212
|
"text": "Give the nod",
|
|
5204
5213
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
5205
|
-
"line":
|
|
5214
|
+
"line": 258,
|
|
5206
5215
|
"origin": "js-markup",
|
|
5207
5216
|
"confidence": "certain"
|
|
5208
5217
|
},
|
|
@@ -6633,7 +6642,7 @@
|
|
|
6633
6642
|
"surface": "builders-hall",
|
|
6634
6643
|
"text": "Needs your attention",
|
|
6635
6644
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
6636
|
-
"line":
|
|
6645
|
+
"line": 557,
|
|
6637
6646
|
"origin": "js-markup",
|
|
6638
6647
|
"confidence": "certain"
|
|
6639
6648
|
},
|
|
@@ -9607,6 +9616,15 @@
|
|
|
9607
9616
|
"origin": "js-notice",
|
|
9608
9617
|
"confidence": "certain"
|
|
9609
9618
|
},
|
|
9619
|
+
{
|
|
9620
|
+
"id": "34017119ce22",
|
|
9621
|
+
"surface": "builders-hall",
|
|
9622
|
+
"text": "That work is genuinely gone",
|
|
9623
|
+
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
9624
|
+
"line": 336,
|
|
9625
|
+
"origin": "js-markup",
|
|
9626
|
+
"confidence": "certain"
|
|
9627
|
+
},
|
|
9610
9628
|
{
|
|
9611
9629
|
"id": "6e5a324b2972",
|
|
9612
9630
|
"surface": "builders-hall",
|
|
@@ -12438,7 +12456,7 @@
|
|
|
12438
12456
|
"surface": "builders-hall",
|
|
12439
12457
|
"text": "open the queue",
|
|
12440
12458
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
12441
|
-
"line":
|
|
12459
|
+
"line": 521,
|
|
12442
12460
|
"origin": "js-markup",
|
|
12443
12461
|
"confidence": "certain"
|
|
12444
12462
|
},
|
|
@@ -13419,7 +13437,7 @@
|
|
|
13419
13437
|
"surface": "builders-hall",
|
|
13420
13438
|
"text": "{…} · {…} · {…}{…}",
|
|
13421
13439
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13422
|
-
"line":
|
|
13440
|
+
"line": 420,
|
|
13423
13441
|
"origin": "js-markup",
|
|
13424
13442
|
"confidence": "certain"
|
|
13425
13443
|
},
|
|
@@ -13522,6 +13540,15 @@
|
|
|
13522
13540
|
"origin": "js-markup",
|
|
13523
13541
|
"confidence": "certain"
|
|
13524
13542
|
},
|
|
13543
|
+
{
|
|
13544
|
+
"id": "7f79bedecf60",
|
|
13545
|
+
"surface": "builders-hall",
|
|
13546
|
+
"text": "{…}{…} — open it to see what went",
|
|
13547
|
+
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13548
|
+
"line": 336,
|
|
13549
|
+
"origin": "js-markup",
|
|
13550
|
+
"confidence": "certain"
|
|
13551
|
+
},
|
|
13525
13552
|
{
|
|
13526
13553
|
"id": "9588652a7aff",
|
|
13527
13554
|
"surface": "builders-hall",
|
|
@@ -13608,7 +13635,7 @@
|
|
|
13608
13635
|
"surface": "builders-hall",
|
|
13609
13636
|
"text": "— untouched {…}+ days",
|
|
13610
13637
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13611
|
-
"line":
|
|
13638
|
+
"line": 457,
|
|
13612
13639
|
"origin": "js-markup",
|
|
13613
13640
|
"confidence": "certain"
|
|
13614
13641
|
},
|
|
@@ -13644,7 +13671,16 @@
|
|
|
13644
13671
|
"surface": "builders-hall",
|
|
13645
13672
|
"text": "…{…} more awaiting a nod.",
|
|
13646
13673
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13647
|
-
"line":
|
|
13674
|
+
"line": 278,
|
|
13675
|
+
"origin": "js-markup",
|
|
13676
|
+
"confidence": "certain"
|
|
13677
|
+
},
|
|
13678
|
+
{
|
|
13679
|
+
"id": "f16717b3e122",
|
|
13680
|
+
"surface": "builders-hall",
|
|
13681
|
+
"text": "…{…} more awaiting an answer.",
|
|
13682
|
+
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13683
|
+
"line": 358,
|
|
13648
13684
|
"origin": "js-markup",
|
|
13649
13685
|
"confidence": "certain"
|
|
13650
13686
|
},
|
|
@@ -13653,7 +13689,7 @@
|
|
|
13653
13689
|
"surface": "builders-hall",
|
|
13654
13690
|
"text": "…{…} more going quiet.",
|
|
13655
13691
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13656
|
-
"line":
|
|
13692
|
+
"line": 447,
|
|
13657
13693
|
"origin": "js-markup",
|
|
13658
13694
|
"confidence": "certain"
|
|
13659
13695
|
},
|
|
@@ -13671,7 +13707,7 @@
|
|
|
13671
13707
|
"surface": "builders-hall",
|
|
13672
13708
|
"text": "…{…} more —",
|
|
13673
13709
|
"file": "modules/hall-ui/public/goal-inbox.js",
|
|
13674
|
-
"line":
|
|
13710
|
+
"line": 521,
|
|
13675
13711
|
"origin": "js-markup",
|
|
13676
13712
|
"confidence": "certain"
|
|
13677
13713
|
},
|
|
@@ -2310,5 +2310,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2310
2310
|
landed since 1.19.916 with no explicit bump. run 35526196428. (task 1002620)
|
|
2311
2311
|
1.19.918 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2312
2312
|
landed since 1.19.917 with no explicit bump. run 35529342372. (task 1002620)
|
|
2313
|
+
1.19.919 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2314
|
+
landed since 1.19.918 with no explicit bump. run 35531198454. (task 1002620)
|
|
2315
|
+
1.19.920 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2316
|
+
landed since 1.19.919 with no explicit bump. run 35531720944. (task 1002620)
|
|
2313
2317
|
---------------------------------------------------------------------------
|
|
2314
2318
|
```
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
|
|
92
92
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
93
93
|
|
|
94
|
-
<script src="/builders/shell.js?v=2026-
|
|
94
|
+
<script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
|
|
95
95
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
96
96
|
<script src="/builders/palette.js" defer></script>
|
|
97
97
|
<script src="/builders/atlas.js?v=2026-09-02-index" defer></script>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
52
52
|
chrome exists before the page script runs. -->
|
|
53
|
-
<script src="/builders/shell.js?v=2026-
|
|
53
|
+
<script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
|
|
54
54
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
55
55
|
<script src="/builders/palette.js" defer></script>
|
|
56
56
|
<!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
62
62
|
|
|
63
|
-
<script src="/builders/shell.js?v=2026-
|
|
63
|
+
<script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
|
|
64
64
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
65
65
|
<script src="/builders/palette.js" defer></script>
|
|
66
66
|
<script src="/builders/dom-utils.js?v=2026-06-16-board"></script>
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
|
|
106
106
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
107
107
|
|
|
108
|
-
<script src="/builders/shell.js?v=2026-
|
|
108
|
+
<script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
|
|
109
109
|
<script src="/builders/palette.js" defer></script>
|
|
110
110
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
111
111
|
<script src="/builders/collab-lib.js?v=2026-09-19-agewords"></script>
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
|
|
178
178
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
179
179
|
|
|
180
|
-
<script src="/builders/shell.js?v=2026-
|
|
180
|
+
<script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
|
|
181
181
|
<script src="/builders/palette.js" defer></script>
|
|
182
182
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
183
183
|
<script src="/builders/hall-kit.js"></script>
|