@bongos/core 1.19.484 → 1.19.486
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 +102 -52
- 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 +7 -0
- package/clients/bongos-client/index.mjs +6 -0
- package/docs/adr/0232-rot-is-derived-not-swept-and-water-is-the-only-new-verb.md +155 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +147 -3
- package/docs/api-reference.md +7 -4
- package/docs/copy-inventory.md +169 -178
- package/docs/copy-registry.json +295 -405
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/migrations/core_231_project_settings_and_attention_water.sql +163 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +6 -0
- package/modules/hall-ui/public/goal-inbox.js +119 -1
- package/modules/hall-ui/public/not-ready.html +14 -10
- package/modules/hall-ui/public/not-ready.states.json +14 -0
- package/modules/hall-ui/public/pair.css +17 -67
- package/modules/hall-ui/public/pair.html +8 -1
- package/modules/hall-ui/public/pair.states.json +14 -0
- package/modules/hall-ui/public/panel.css +333 -0
- package/modules/hall-ui/public/profile.css +85 -0
- package/modules/hall-ui/public/profile.html +12 -5
- package/modules/hall-ui/public/profile.states.json +15 -0
- package/modules/hall-ui/public/settings.css +119 -336
- package/modules/hall-ui/public/settings.html +28 -19
- package/modules/hall-ui/public/settings.js +44 -35
- package/modules/hall-ui/public/settings.states.json +16 -0
- package/modules/hall-ui/public/style.css +5 -2
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/rot.js +210 -0
- package/modules/lifecycle/routes/rot.js +160 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/fitness-checks-vocabulary.js +0 -1
- package/scripts/gds/fitness.js +9 -0
- package/src/bongos/project-settings.js +193 -0
- package/src/module-api.js +29 -1
- package/tests/hall_settings_world.mjs +274 -0
- package/tests/join_application_note.mjs +7 -15
- package/tests/module-scope-map.mjs +1 -1
- package/tests/module_api.mjs +1 -0
- package/tests/rot_inbox.mjs +532 -0
- package/modules/hall-ui/public/my-projects.css +0 -62
- package/modules/hall-ui/public/my-projects.html +0 -73
- package/modules/hall-ui/public/my-projects.js +0 -153
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# 0232 — Rot is derived, not swept; and `water` is the only new verb
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-09-02
|
|
5
|
+
- **Tasks:** [#1003279](https://cloudbongos.com/builders#/task/1003279)
|
|
6
|
+
- **Goal:** [#1000072](https://cloudbongos.com/builders#/goal/1000072) — Human legibility, criterion `legibility-sort-and-staleness`.
|
|
7
|
+
- **Completes:** [ADR 0226](<redacted>.md), which split the *claim* timer out of this task and recorded that "1003279 stays open for its inbox half". This is that half.
|
|
8
|
+
- **Builds on:** [#1003278](https://cloudbongos.com/builders#/task/1003278) (the shared *task* staleness vocabulary the boards use — §3 below is why this reuses its definition rather than writing a second one).
|
|
9
|
+
- **Unblocks:** [#1003280](https://cloudbongos.com/builders#/task/1003280), whose settings page needs the timer to be "live, DB-backed" and warns "do not fork a second store". §4 is that store.
|
|
10
|
+
|
|
11
|
+
## 1. What the owner decided, and what was rejected
|
|
12
|
+
|
|
13
|
+
Goal 1000072's rotting half asks that "untouched-for-X triggers a configured action
|
|
14
|
+
rather than a silent decay". The owner's decision (2026-08-25) fixed *what the action
|
|
15
|
+
is*: a **needs-attention item** — one line in the hall's existing inbox, addressed to
|
|
16
|
+
whoever created the thing and to the owner, offering four human verbs: **prune, kill,
|
|
17
|
+
prioritise, water** (water = leave it, and reset the clock). Nothing changes status on
|
|
18
|
+
its own; a human decides.
|
|
19
|
+
|
|
20
|
+
Two alternatives were rejected, and the reasons matter because both are the obvious
|
|
21
|
+
implementation:
|
|
22
|
+
|
|
23
|
+
| Rejected | Why |
|
|
24
|
+
|---|---|
|
|
25
|
+
| Auto-demote a stale thing to a review queue | It mutates real work with no human in the loop, and a wrong timer then silently freezes it. The failure is invisible: nobody notices work that stopped being offered. |
|
|
26
|
+
| A nightly digest | A notification is not an action. It gets scrolled past, and a digest you scroll past is indistinguishable from the silent decay it was meant to fix. |
|
|
27
|
+
|
|
28
|
+
## 2. The decision that shaped everything else: **rot is derived, not swept**
|
|
29
|
+
|
|
30
|
+
The task's own build note asked for "a poller/routine that is idempotent (one open item
|
|
31
|
+
per stale thing, not one per sweep)". That requirement is real — an inbox that accretes
|
|
32
|
+
a duplicate line per sweep is unusable within a week — but the sweep is not what
|
|
33
|
+
delivers it, and building one would have made the requirement *harder* to satisfy.
|
|
34
|
+
|
|
35
|
+
**There is no `attention_items` table and no poller.** A line is a `SELECT`: the thing's
|
|
36
|
+
rot clock compared against the timer, evaluated at read time. Everything the owner's
|
|
37
|
+
note asks for then falls out of arithmetic rather than out of code that must be written
|
|
38
|
+
correctly and then stay correct:
|
|
39
|
+
|
|
40
|
+
- *exactly one item per stale thing* — a row either satisfies the predicate or it does
|
|
41
|
+
not; there is nothing to duplicate.
|
|
42
|
+
- *the item clears when the thing is touched* — a real edit moves `updated_at`.
|
|
43
|
+
- *the item clears when it is decided* — prune/kill/prioritise change `status`, and the
|
|
44
|
+
status filter stops matching.
|
|
45
|
+
- *water resets the clock* — the water mark is a term in the clock.
|
|
46
|
+
|
|
47
|
+
There is also no window in which the inbox and the database disagree, no reconciliation,
|
|
48
|
+
and nothing to garbage-collect. **A stored item would have been a cache of a query whose
|
|
49
|
+
inputs it could not observe** — and keeping that cache honest is precisely the class of
|
|
50
|
+
bug the sweep was being asked to avoid.
|
|
51
|
+
|
|
52
|
+
The cost, stated plainly: the rot list is computed per request rather than materialised.
|
|
53
|
+
That is affordable here because the predicate is indexed (`idx_tasks_attention_rot`,
|
|
54
|
+
`idx_goals_attention_rot` in migration `core_231`) and the read is capped. If the corpus
|
|
55
|
+
ever outgrows it, the fix is a materialised view over the same predicate — still not an
|
|
56
|
+
items table, because the derivation is the part worth keeping.
|
|
57
|
+
|
|
58
|
+
## 3. What can rot — and the exclusions, which are the load-bearing part
|
|
59
|
+
|
|
60
|
+
Rot is **work nobody has picked up**, not work in flight. Tasks at `backlog` or `ready`;
|
|
61
|
+
goals at `open`. Every exclusion is excluded because *another surface already owns that
|
|
62
|
+
question*, and two surfaces asking one question with two different timers is exactly the
|
|
63
|
+
failure [ADR 0226](<redacted>.md) was
|
|
64
|
+
written to end:
|
|
65
|
+
|
|
66
|
+
| Excluded | Who owns it instead |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `active` | the **claim-silence timer** (`src/stale-timer.js`) — the sweeper releases it, the Archon roster reports it |
|
|
69
|
+
| `blocked` | the blocker, and `/blocker-review` as its cadence |
|
|
70
|
+
| `completed`, `confirmed` | the ship pipeline — the publish reconciler and the "Stranded ships" line |
|
|
71
|
+
| `shipped`, `abandoned` | terminal; nothing rots after it is done |
|
|
72
|
+
|
|
73
|
+
`active` is the one that would have been easy to get wrong, and the one that matters
|
|
74
|
+
most: a claimed-but-quiet task is *already* reported, in hours, by a surface that acts
|
|
75
|
+
on it. Reporting it again here, in days, would have put the same rot in two inboxes
|
|
76
|
+
under two numbers — re-creating the 6h-vs-24h split one tier up. `tests/rot_inbox.mjs`
|
|
77
|
+
asserts the exclusion directly, so a later "why isn't my active task listed?" cannot be
|
|
78
|
+
fixed by deleting the reason.
|
|
79
|
+
|
|
80
|
+
**The clock is not a new definition of "touched".** `modules/hall-ui/public/task-lib.js`
|
|
81
|
+
already publishes one for the boards (task 1003278): `max(updated_at, shipped_at)`,
|
|
82
|
+
folding in `shipped_at` because a ship path can stamp it without touching `updated_at`.
|
|
83
|
+
This adds a third term and nothing else:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
tasks: GREATEST(updated_at, COALESCE(shipped_at, updated_at), COALESCE(attention_watered_at, updated_at))
|
|
87
|
+
goals: GREATEST(updated_at, COALESCE(attention_watered_at, updated_at))
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Had the server invented its own two-term clock, the board and the inbox would disagree
|
|
91
|
+
about which rows are stale. Two tests pin this: one asserts the domain's expression
|
|
92
|
+
appears **verbatim** in the migration's index expressions (drift there is silent — the
|
|
93
|
+
query simply stops using the index), and one asserts `task-lib.js` still defines
|
|
94
|
+
"touched" the way the server assumes.
|
|
95
|
+
|
|
96
|
+
## 4. The timer is DB-backed, and it is a *second* timer on purpose
|
|
97
|
+
|
|
98
|
+
ADR 0226's whole subject is that one question must have one answer. So adding a second
|
|
99
|
+
timer needs a defence, and it is that these are **two questions**:
|
|
100
|
+
|
|
101
|
+
| | claim-silence (`src/stale-timer.js`) | rot (this ADR) |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| measures | a builder who went quiet | work nobody picked up |
|
|
104
|
+
| unit | hours | days |
|
|
105
|
+
| configured in | env (a redeploy) | `project_settings` (a live knob) |
|
|
106
|
+
| consequence | the machine **releases** the claim, alone | the machine **asks** a human |
|
|
107
|
+
|
|
108
|
+
The store is a generic key/value table rather than a column per knob, because task
|
|
109
|
+
1003280 puts five fundamentals on one page and a knob-per-migration would make that page
|
|
110
|
+
a schema change every time. `project_settings` is created here, unseeded: **no row means
|
|
111
|
+
"never configured, use the code default"**, which is what lets a fresh instance boot on
|
|
112
|
+
an empty table and what lets the settings page tell "never touched" apart from
|
|
113
|
+
"deliberately set to the default value".
|
|
114
|
+
|
|
115
|
+
Precedence mirrors ADR 0226 exactly — `override` (a per-request lens, never written
|
|
116
|
+
back) > the DB row > the code default — and so does the **fail posture**, on the
|
|
117
|
+
reporting side of that ADR's asymmetry: a value that fails its range check degrades to
|
|
118
|
+
the default **with a receipt**, which the inbox renders in its own heading. A typo shows
|
|
119
|
+
up on the screen of the person who can fix it instead of 500-ing the inbox they were
|
|
120
|
+
using to find the problem. One case is deliberately *not* treated as a bad value: an
|
|
121
|
+
unreachable database is a failure to read, not a misconfiguration, and it must not blame
|
|
122
|
+
the operator for something they did not type.
|
|
123
|
+
|
|
124
|
+
## 5. Three of the four verbs add no authority to the system
|
|
125
|
+
|
|
126
|
+
Only `water` is a new route. `prioritise`, `prune` and `kill` reuse `POST
|
|
127
|
+
/tasks/:id/promote`, `/demote` and `/abandon` (and `/goals/:id/archive`) — the precedent
|
|
128
|
+
the nod slice set in this same card (task 1003066), and the reason three quarters of
|
|
129
|
+
this feature introduces no new permission at all.
|
|
130
|
+
|
|
131
|
+
The verbs are **served with each row** rather than hardcoded in the client. That is not
|
|
132
|
+
decoration: the tier→verb mapping already differs (a goal gets two verbs, because
|
|
133
|
+
promote/demote are task-lifecycle transitions a goal has no equivalent of), and a
|
|
134
|
+
hardcoded list in the hall would be a second copy of that mapping, free to drift into
|
|
135
|
+
rendering a button the server refuses. A test asserts every advertised verb names a
|
|
136
|
+
route that actually exists.
|
|
137
|
+
|
|
138
|
+
`water` itself is gated on **ownership OR the atom**: the creator may always water their
|
|
139
|
+
own line, and `builder.roster.read` — "may read another builder's record off the roster",
|
|
140
|
+
the atom `/builders/:id/active-claims` already carries — covers the owner. That check
|
|
141
|
+
runs *inside* the handler rather than as middleware, because a creator holding no atom
|
|
142
|
+
must still get through, and a gate mounted in front would refuse them before ownership
|
|
143
|
+
is known. It fails closed: no governance port means no permission (ADR 0016).
|
|
144
|
+
|
|
145
|
+
## 6. Consequences
|
|
146
|
+
|
|
147
|
+
- The hall's needs-attention card gains one slice, `#attention-rot`, placed last: every
|
|
148
|
+
slice above it is somebody waiting on you *now*, while rot has already waited weeks.
|
|
149
|
+
- `project_settings` exists and has exactly one key. Task 1003280 adds rows, not tables.
|
|
150
|
+
- `tasks` and `goals` each gain two nullable columns. No backfill: never-watered is the
|
|
151
|
+
honest state for every existing row.
|
|
152
|
+
- The rot list is per-request. §2 names the escape hatch if that ever stops being cheap.
|
|
153
|
+
- **A future third timer needs its own ADR.** §4 is the template for defending one: name
|
|
154
|
+
the question, the unit, the store and the consequence, and show they differ from both
|
|
155
|
+
of the existing two. If they do not, it is not a new timer — it is a second answer.
|
package/docs/adr/README.md
CHANGED
|
@@ -318,5 +318,6 @@ This keeps the decision history honest and traceable.
|
|
|
318
318
|
| 0230 | [**A look skill is a composition grammar; the palette is the pack and the texture is a library entry** ([task 1003327](https://cloudbongos.com/builders#/task/1003327) · goal 1000083, criterion `uidesign-skills-in-house`). Four groups of the taste-skill rebuild spec ship a *look*, not a method, and upstream each one shipped a palette and a set of faces — which is the one thing the fifteen-token contract forbids (ADR 0196, 0219). Decisions: (1) a look skill ships **structure, rhythm, type roles, component anatomy, motion and bans, and no colours** — every colour a `var()` or `color-mix()` of the fifteen, the palette the instance's pack, the texture a named entry in `modules/ui-design/styles/`, and no skill adds a face. (2) The brutalist "one substrate per project" becomes an **archetype** deciding which mode the surface is composed *for*, while the other mode ships complete and measured; "never mixed in one interface" is the part that was about craft. (3) `gpt-taste`'s prompt-seeded **random** selection becomes a deterministic one derived from the route and bumped against sibling surfaces, written into a plan block before any markup with the hero arithmetic, the grid-density proof and the measured contrast pairs — a roll nobody can reproduce is not a decision. (4) `stitch-design-taste` **transcribes** the world instead of authoring one: the platform's own `DESIGN.md` frontmatter shape, the thirteen diffed from the pack rather than retyped, and its universal bans stated as defaults the instance overrules. (5) The upstream's seeded placeholder photography is **dropped, not softened** (ADR 0222); (6) motion is CSS inside the Kill Switch and what cannot live there is named *not available*; (7) the pre-flight is the kit and `tells.js` is its mechanical half, so a look skill adds no reviewer of its own. Rejected: shipping each look as a pack, a sixteenth token for a substrate, a fixed seed behind the same roll.](<redacted>.md) | ui-design / skills |
|
|
319
319
|
| 0231 | [**The `/style` session: an owner authors a look inside the contract, and a module skill declares one of two origins** ([task 1003475](https://cloudbongos.com/builders#/task/1003475) · goal 1000083, criterion `uidesign-skills-in-house`). The library ships three looks to choose from; the owner's ask was to *make* one. A look is a small, strict object (thirteen colours, two font stacks, a `DESIGN.md`, one specimen), so what was missing was the loop between the ends. Decisions: (1) `/style` is a **bounded four-step session** - SEARCH, CREATE, PROVE, ADOPT - and nothing else. (2) SEARCH **renders before it asks**: the nearest two entries shown through the kit (`/` and the hall's light tier), then two or three questions about *feel*, the light-only fact of a pack stated up front, and adapt-or-draft declared in one sentence before a file is written. (3) CREATE stays inside the contract or refuses - never a fork of a sheet, **never a sixteenth token** (a radius, a shadow, a glass are a sheet's derived tier; a look that needs one is an ADR). (4) PROVE is **arithmetic, not taste**: the pack floor, the three token suites under the pack, `check-mock`, the renders, with a failure-to-fix table so the retune is a lookup, and the four green lines pasted as evidence. (5) ADOPT hands the owner one step and **never writes `config/branding.json`** - the host boundary is theirs (ADR 0062 §7) - but does file the catalogue rows under the claimed task. (6) A module skill declares **exactly one of two origins**, `## Rebuilt from` (ADR 0198 / 0220) or `## First party` (no upstream; the authoring task, this ADR, and why it exists), and the pins branch on which - a fabricated spec citation would destroy the provenance the section exists for. (7) The session's first output is its own proof: `blueprint`, the library's first cool look, cleared the pack floor and all three suites on the first measurement. Rejected: a fabricated Rebuilt-from, a session that repaints the live instance, filing the catalogue rows "later", a palette generated from a reference image.](<redacted>.md) | ui-design / skills |
|
|
320
320
|
| 0226 | [**One stale-claim timer; reporting degrades where enforcement refuses** ([task 1003476](https://cloudbongos.com/builders#/task/1003476), goal 1000072, criterion `legibility-sort-and-staleness`). "How long may a claim go silent" had FOUR answers and none of them was the project's: the sweeper that actually releases a claim read `GDS_STALE_CLAIM_HOURS` (else 6h); `GET /builders/roster` and `GET /builders/:id/active-claims` each hardcoded 24h in byte-identical blocks; and the two `session_pulses` helpers defaulted to 30 minutes. The 6-vs-24 pair is the harm: the roster is the screen an Archon opens to decide whether to intervene, and for **eighteen hours** after the sweeper had released a claim, stamped its session log `[auto-released:stale]` and returned the task to `ready`, that screen still showed it healthy — while the roster's own comment claimed to "mirror" the sweeper, true of the definition and false of the number. Decision: ONE resolver, `src/stale-timer.js` (a KERNEL file, the `instance-config.js`/`branding.js` category — host config a module reads, never hardcodes), reached by modules through the doorway and by core routes and the CLI directly. Precedence: explicit override (`--threshold-hours`, `?stale_hours=` — a LENS, labelled `query`, never a reconfiguration) → `<PREFIX>_STALE_CLAIM_HOURS` via `instance-config.resolveEnv`, whose legacy prefix chain still honours the live `GDS_STALE_CLAIM_HOURS` so **nothing in a running deployment has to change** (and which is the channel ADR 0190's runner already patches into `web.env`) → **6**, the SWEEP's historical number and deliberately not the roster's 24, because the sweep is the surface that acts and unifying on 24 would have made every project three times more patient with a dead claim under cover of a refactor. Bound 1..720h — the range `/builders/roster` has published since V3.R14; one timer, one bound. Every surface that prints the timer prints its PROVENANCE (`stale_hours_source`, the sweep header, the auto-release note): a number without it is exactly what let 6 and 24 coexist unnoticed. **The fail posture is asymmetric and that is the decision**: the resolver reports an unusable value rather than throwing, then a surface that only READS degrades to the default and names the bad var (500-ing the roster an Archon opened to find the problem would hide the problem), while the SWEEP refuses to run at all (it releases other people's claims and stamps their logs; no reload undoes that) — a surface that only reads may guess and say so, a surface that acts may not guess. Both routes now share one `staleHoursFor(req, res)`; two copies of a literal is how the drift happened twice inside one file. Rejected: a new `config/stale.json` tier (the env route already IS the per-project channel); making 24 the shared default; throwing from the resolver (forces a second reader — the original bug); folding in the boards' 14/30/60-DAY task buckets (a different question — days, about a task, a browsing lens with no enforcement behind it); a DB row + settings UI (a reasonable next step, but the defect was four numbers, not a missing knob — a row added before the readers agreed would have been a fifth).](<redacted>.md) | GDS / claims + ops |
|
|
321
|
+
| 0232 | [**Rot is derived, not swept; and `water` is the only new verb** ([task 1003279](https://cloudbongos.com/builders#/task/1003279) · goal 1000072, criterion `legibility-sort-and-staleness`). The inbox half [ADR 0226](<redacted>.md) split out and left open. Owner decision (2026-08-25): a goal or task past the timer raises a **needs-attention line** — not an automatic status change (it mutates real work with no human in the loop, and a wrong timer then silently freezes it) and not a nightly digest (a notification is not an action, and one you scroll past is indistinguishable from the silent decay it was meant to fix). The build note asked for an idempotent poller; the decision is that **there is no poller and no items table** — a line is a `SELECT` comparing the thing's rot clock to the timer, so "exactly one item per stale thing, not one per sweep" is true by arithmetic rather than by deduplication code that must be written correctly and then stay correct, and clearing falls out for free (an edit moves `updated_at`, a decision moves `status`, watering moves the mark). A stored item would have been a cache of a query whose inputs it could not observe. **The exclusions are the load-bearing part**: rot is work nobody picked up (`backlog`/`ready`, and `open` goals), and `active` is deliberately excluded because the claim-silence timer already reports it in hours — listing it here in days would put one rot in two inboxes under two numbers, the 6h-vs-24h split one tier up; `blocked`, `completed`/`confirmed` and the terminal states are excluded for the same reason, each naming the surface that owns it. The clock is **not a new definition of touched** — it is `task-lib.js`'s published `max(updated_at, shipped_at)` (task 1003278) plus a water mark, pinned by a test asserting the domain expression appears verbatim in migration `core_231`'s index expressions, because drift there is silent. The timer is a **second** timer and defends itself as a different question (days vs hours, DB vs env, asks a human vs releases a claim), stored in a new unseeded generic `project_settings` table — no row means "never configured", which is what lets a settings page tell that apart from "set to the default" — with ADR 0226's precedence and its reporting-degrades fail posture, and an unreachable DB explicitly not treated as a bad value. Three of the four verbs (**prioritise/prune/kill**) reuse `promote`/`demote`/`abandon`/`archive`, the task-1003066 precedent, so they add no permission; only `water` is new, gated on ownership OR `builder.roster.read` inside the handler (a creator holding no atom must still get through). Verbs are **served with each row** rather than hardcoded, since a goal legitimately gets two where a task gets four. Rejected: an items table, a sweep, a digest, auto-demotion, and a fourth verb on goals that would render a dead button.](<redacted>.md) | GDS / legibility + hall |
|
|
321
322
|
|
|
322
323
|
> ⚠️ **Numbering collisions are now machine-enforced, not narrated.** Twenty numbers are shared by two ADRs each — assigned in parallel sessions before anything checked. The files keep their filenames (renumbering would break every existing citation, and a number once assigned is never reused), and this table disambiguates each pair as `NNNN-a` / `NNNN-b`. The authoritative list is `LEGACY_DUPLICATE_ADRS` in [`scripts/gds/adr-namespace.js`](../../scripts/gds/adr-namespace.js), frozen by exact filename: a **new** collision — or a third file joining a legacy number, or a rename of either half — hard-fails the `unit` CI gate, as does an index row that points at no file or an ADR with no row. The list may only shrink. This paragraph used to narrate the collisions one by one and had fallen eight behind reality; see [ADR 0195](<redacted>.md) for why the check exists and why the pairs are grandfathered rather than renumbered.
|
package/docs/api/openapi.json
CHANGED
|
@@ -6298,6 +6298,51 @@
|
|
|
6298
6298
|
]
|
|
6299
6299
|
}
|
|
6300
6300
|
},
|
|
6301
|
+
"/goals/{id}/water": {
|
|
6302
|
+
"post": {
|
|
6303
|
+
"operationId": "post_goals_id_water",
|
|
6304
|
+
"tags": [
|
|
6305
|
+
"goals"
|
|
6306
|
+
],
|
|
6307
|
+
"summary": "POST /goals/:id/water",
|
|
6308
|
+
"description": "POST /goals/:id/water — reset a rotting goal's clock.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
6309
|
+
"x-rank": "any-builder",
|
|
6310
|
+
"x-source": "modules/lifecycle/routes/rot.js",
|
|
6311
|
+
"parameters": [
|
|
6312
|
+
{
|
|
6313
|
+
"name": "id",
|
|
6314
|
+
"in": "path",
|
|
6315
|
+
"required": true,
|
|
6316
|
+
"schema": {
|
|
6317
|
+
"type": "string"
|
|
6318
|
+
},
|
|
6319
|
+
"description": "Path parameter `id`."
|
|
6320
|
+
}
|
|
6321
|
+
],
|
|
6322
|
+
"responses": {
|
|
6323
|
+
"200": {
|
|
6324
|
+
"description": "Success."
|
|
6325
|
+
},
|
|
6326
|
+
"400": {
|
|
6327
|
+
"$ref": "#/components/responses/BadRequest"
|
|
6328
|
+
},
|
|
6329
|
+
"401": {
|
|
6330
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
6331
|
+
},
|
|
6332
|
+
"403": {
|
|
6333
|
+
"$ref": "#/components/responses/Forbidden"
|
|
6334
|
+
},
|
|
6335
|
+
"404": {
|
|
6336
|
+
"$ref": "#/components/responses/NotFound"
|
|
6337
|
+
}
|
|
6338
|
+
},
|
|
6339
|
+
"security": [
|
|
6340
|
+
{
|
|
6341
|
+
"builderSession": []
|
|
6342
|
+
}
|
|
6343
|
+
]
|
|
6344
|
+
}
|
|
6345
|
+
},
|
|
6301
6346
|
"/government/assignments": {
|
|
6302
6347
|
"get": {
|
|
6303
6348
|
"operationId": "get_government_assignments",
|
|
@@ -7364,6 +7409,44 @@
|
|
|
7364
7409
|
]
|
|
7365
7410
|
}
|
|
7366
7411
|
},
|
|
7412
|
+
"/inbox/rotting": {
|
|
7413
|
+
"get": {
|
|
7414
|
+
"operationId": "get_inbox_rotting",
|
|
7415
|
+
"tags": [
|
|
7416
|
+
"inbox"
|
|
7417
|
+
],
|
|
7418
|
+
"summary": "GET /inbox/rotting",
|
|
7419
|
+
"description": "GET /inbox/rotting — what is quietly rotting, for this caller. rank: any-builder — own-scoped by default. The builder id comes from the authenticated session and is never read off the request, so a caller can only ever widen the result by HOLDING an atom, never by naming a different builder. A Xenos sees exactly the things they created and nothing else. ?rot_days= is a LENS, not a setting: it answers \"what would 7 days show me?\" without reconfiguring the project, and is never written back. The response reports the timer and where the number came from, so a reader can always tell a lens apart from the project's real answer (the src/stale-timer.js receipt convention — a number with no provenance is what let 6h and 24h coexist).\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
7420
|
+
"x-rank": "any-builder",
|
|
7421
|
+
"x-source": "modules/lifecycle/routes/rot.js",
|
|
7422
|
+
"responses": {
|
|
7423
|
+
"200": {
|
|
7424
|
+
"description": "Success.",
|
|
7425
|
+
"content": {
|
|
7426
|
+
"application/json": {
|
|
7427
|
+
"schema": {
|
|
7428
|
+
"$ref": "#/components/schemas/GetInboxRottingResponse"
|
|
7429
|
+
}
|
|
7430
|
+
}
|
|
7431
|
+
}
|
|
7432
|
+
},
|
|
7433
|
+
"400": {
|
|
7434
|
+
"$ref": "#/components/responses/BadRequest"
|
|
7435
|
+
},
|
|
7436
|
+
"401": {
|
|
7437
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
7438
|
+
},
|
|
7439
|
+
"403": {
|
|
7440
|
+
"$ref": "#/components/responses/Forbidden"
|
|
7441
|
+
}
|
|
7442
|
+
},
|
|
7443
|
+
"security": [
|
|
7444
|
+
{
|
|
7445
|
+
"builderSession": []
|
|
7446
|
+
}
|
|
7447
|
+
]
|
|
7448
|
+
}
|
|
7449
|
+
},
|
|
7367
7450
|
"/inbox/score": {
|
|
7368
7451
|
"post": {
|
|
7369
7452
|
"operationId": "post_inbox_score",
|
|
@@ -15191,6 +15274,51 @@
|
|
|
15191
15274
|
]
|
|
15192
15275
|
}
|
|
15193
15276
|
},
|
|
15277
|
+
"/tasks/{id}/water": {
|
|
15278
|
+
"post": {
|
|
15279
|
+
"operationId": "post_tasks_id_water",
|
|
15280
|
+
"tags": [
|
|
15281
|
+
"tasks"
|
|
15282
|
+
],
|
|
15283
|
+
"summary": "POST /tasks/:id/water",
|
|
15284
|
+
"description": "POST /tasks/:id/water — reset a rotting task's clock.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
15285
|
+
"x-rank": "any-builder",
|
|
15286
|
+
"x-source": "modules/lifecycle/routes/rot.js",
|
|
15287
|
+
"parameters": [
|
|
15288
|
+
{
|
|
15289
|
+
"name": "id",
|
|
15290
|
+
"in": "path",
|
|
15291
|
+
"required": true,
|
|
15292
|
+
"schema": {
|
|
15293
|
+
"type": "string"
|
|
15294
|
+
},
|
|
15295
|
+
"description": "Path parameter `id`."
|
|
15296
|
+
}
|
|
15297
|
+
],
|
|
15298
|
+
"responses": {
|
|
15299
|
+
"200": {
|
|
15300
|
+
"description": "Success."
|
|
15301
|
+
},
|
|
15302
|
+
"400": {
|
|
15303
|
+
"$ref": "#/components/responses/BadRequest"
|
|
15304
|
+
},
|
|
15305
|
+
"401": {
|
|
15306
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
15307
|
+
},
|
|
15308
|
+
"403": {
|
|
15309
|
+
"$ref": "#/components/responses/Forbidden"
|
|
15310
|
+
},
|
|
15311
|
+
"404": {
|
|
15312
|
+
"$ref": "#/components/responses/NotFound"
|
|
15313
|
+
}
|
|
15314
|
+
},
|
|
15315
|
+
"security": [
|
|
15316
|
+
{
|
|
15317
|
+
"builderSession": []
|
|
15318
|
+
}
|
|
15319
|
+
]
|
|
15320
|
+
}
|
|
15321
|
+
},
|
|
15194
15322
|
"/versions": {
|
|
15195
15323
|
"get": {
|
|
15196
15324
|
"operationId": "get_versions",
|
|
@@ -16307,6 +16435,22 @@
|
|
|
16307
16435
|
"open_count"
|
|
16308
16436
|
]
|
|
16309
16437
|
},
|
|
16438
|
+
"GetInboxRottingResponse": {
|
|
16439
|
+
"type": "object",
|
|
16440
|
+
"properties": {
|
|
16441
|
+
"rows": {},
|
|
16442
|
+
"count": {},
|
|
16443
|
+
"scope": {},
|
|
16444
|
+
"timer": {},
|
|
16445
|
+
"cutoff": {},
|
|
16446
|
+
"ok": {
|
|
16447
|
+
"type": "boolean"
|
|
16448
|
+
},
|
|
16449
|
+
"tier": {},
|
|
16450
|
+
"id": {},
|
|
16451
|
+
"watered_at": {}
|
|
16452
|
+
}
|
|
16453
|
+
},
|
|
16310
16454
|
"GetInboxSparksResponse": {
|
|
16311
16455
|
"type": "object",
|
|
16312
16456
|
"properties": {
|
|
@@ -21505,9 +21649,9 @@
|
|
|
21505
21649
|
"description": "A required dependency/feature is not configured or is temporarily down."
|
|
21506
21650
|
}
|
|
21507
21651
|
},
|
|
21508
|
-
"x-endpoint-count":
|
|
21509
|
-
"x-schema-count":
|
|
21652
|
+
"x-endpoint-count": 344,
|
|
21653
|
+
"x-schema-count": 375,
|
|
21510
21654
|
"x-undocumented-bodies": 9,
|
|
21511
|
-
"x-response-schemas":
|
|
21655
|
+
"x-response-schemas": 256,
|
|
21512
21656
|
"x-generated-by": "scripts/gds/gen-api-docs.js"
|
|
21513
21657
|
}
|
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. 344 endpoints across 56 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/<redacted>.md)): `public` < `any-builder` < `metic+archon` < `archon`.
|
|
@@ -220,7 +220,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
220
220
|
| GET | `/api/bongos/github/repos` | `any-builder` | — | acts only on the caller's OWN GitHub identity (the token they granted). |
|
|
221
221
|
| POST | `/api/bongos/github/repos` | `any-builder` | `name` | creates a repo in the caller's OWN account. |
|
|
222
222
|
|
|
223
|
-
## `goals` (
|
|
223
|
+
## `goals` (31)
|
|
224
224
|
|
|
225
225
|
| Method | Path | Rank | Body | Description |
|
|
226
226
|
|---|---|---|---|---|
|
|
@@ -250,6 +250,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
250
250
|
| POST | `/api/bongos/goals/:id/tasks` | `any-builder` | `title`, `description`, `touches`, `est_minutes`, `priority`, `kind`, `discipline`, `value_summary`, `parent_task_id` | POST /goals/:id/tasks — bounded task authoring within a goal's scope (BV1.R60 / ADR 0086 §3). |
|
|
251
251
|
| POST | `/api/bongos/goals/:id/transfer` | `any-builder` | `builder_id` | POST /goals/:id/transfer — hand the goal to a new owner (task 1735). |
|
|
252
252
|
| PATCH | `/api/bongos/goals/:id/visibility` | `any-builder` | — | PATCH /goals/:id/visibility — make a goal public\|private (task 1952 / ADR 0112). |
|
|
253
|
+
| POST | `/api/bongos/goals/:id/water` | `any-builder` | — | POST /goals/:id/water — reset a rotting goal's clock. |
|
|
253
254
|
| GET | `/api/bongos/goals/inbox` | `any-builder` | — | GET /goals/inbox — the caller's private-goals inbox (task 1952 / ADR 0112): { invitations: invites addressed to me (accept/decline); join… |
|
|
254
255
|
| POST | `/api/bongos/goals/reorder` | `metic+archon` | `version_id`, `ordered_ids` | POST /goals/reorder — Archon drag-to-reorder: set the priority order of every goal WITHIN one version (task 1760). |
|
|
255
256
|
| GET | `/api/bongos/goals/rollups` | `any-builder` | — | GET /goals/rollups?version= — one small row per goal: readiness counters, the last-activity timestamp, the craft set, and the next claima… |
|
|
@@ -294,7 +295,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
294
295
|
| GET | `/api/bongos/help-requests/for-me` | `any-builder` | — | The addressed read — C01's answer to "and fires a notification". |
|
|
295
296
|
| GET | `/api/bongos/help-requests/mine` | `any-builder` | — | your own asks, so you can settle them. |
|
|
296
297
|
|
|
297
|
-
## `inbox` (
|
|
298
|
+
## `inbox` (16)
|
|
298
299
|
|
|
299
300
|
| Method | Path | Rank | Body | Description |
|
|
300
301
|
|---|---|---|---|---|
|
|
@@ -310,6 +311,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
310
311
|
| DELETE | `/api/bongos/inbox/:id/spark` | `any-builder` | — | Withdraw the Big Idea Spark mark — the reversibility half of R144. |
|
|
311
312
|
| GET | `/api/bongos/inbox/awaiting-nod` | `metic+archon` | — | GET /inbox/awaiting-nod — the NOD QUEUE (task 1003066, BV1.R05). |
|
|
312
313
|
| GET | `/api/bongos/inbox/by-builder/:id` | `any-builder` | — | THE IDEA TRAIL: every idea one builder filed, newest first, with what became of each (task 1003281, goal 1000072, criterion legibility-ac… |
|
|
314
|
+
| GET | `/api/bongos/inbox/rotting` | `any-builder` | — | GET /inbox/rotting — what is quietly rotting, for this caller. |
|
|
313
315
|
| POST | `/api/bongos/inbox/score` | `any-builder` | `field_values`, `template_key` | |
|
|
314
316
|
| GET | `/api/bongos/inbox/sparks` | `any-builder` | — | GET /inbox/sparks — THE SPARK QUEUE (task 1002921 / BV1.R145, goal 1000062). |
|
|
315
317
|
| GET | `/api/bongos/inbox/template` | `any-builder` | — | ----------------------------------------------------------------------- The two reads the hall's Full Idea composer is built from (task 1… |
|
|
@@ -559,7 +561,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
559
561
|
|---|---|---|---|---|
|
|
560
562
|
| GET | `/api/bongos/task-visuals/:name` | `public` | — | the WIDEST path this route can take, declared honestly. |
|
|
561
563
|
|
|
562
|
-
## `tasks` (
|
|
564
|
+
## `tasks` (31)
|
|
563
565
|
|
|
564
566
|
| Method | Path | Rank | Body | Description |
|
|
565
567
|
|---|---|---|---|---|
|
|
@@ -588,6 +590,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
588
590
|
| POST | `/api/bongos/tasks/:id/visual` | `any-builder` | `image_b64`, `content_type`, `alt` | POST /tasks/:id/visual — attach (or replace) this task's ship-time visual. |
|
|
589
591
|
| DELETE | `/api/bongos/tasks/:id/visual` | `any-builder` | — | DELETE /tasks/:id/visual — remove it again. |
|
|
590
592
|
| POST | `/api/bongos/tasks/:id/vote` | `metic+archon` | `vote` | POST /tasks/:id/vote { vote: 1 \| -1 \| 0 } — cast/change/clear a peer vote on a confirmed/shipped task. |
|
|
593
|
+
| POST | `/api/bongos/tasks/:id/water` | `any-builder` | — | POST /tasks/:id/water — reset a rotting task's clock. |
|
|
591
594
|
| GET | `/api/bongos/tasks/claimable` | `any-builder` | — | NOTE: /tasks/claimable must come BEFORE /tasks/:id or Express will route 'claimable' to the :id param. |
|
|
592
595
|
| GET | `/api/bongos/tasks/newcomer-floor` | `any-builder` | — | GET /tasks/newcomer-floor — supply gauge for the newcomer queue (task 614). |
|
|
593
596
|
| POST | `/api/bongos/tasks/newcomer-restock` | `metic+archon` | — | POST /tasks/newcomer-restock (task 1168) — bring every evergreen newcomer chore's standing pool up to its configured floor. |
|