@bongos/core 1.19.1055 → 1.19.1056
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 +19 -14
- package/docs/adr/0233-a-copy-proposal-is-a-task-carrying-a-patch.md +1 -0
- package/docs/adr/0341-the-page-is-the-unit-of-tweak-mode.md +211 -0
- package/docs/adr/README.md +1 -0
- package/docs/module-api-changelog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/module-api.js +1 -1
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.1056",
|
|
6
|
+
"core_contract": "1.19.1056",
|
|
7
|
+
"source_commit": "069ec293a36320e7bb8a9546282a2ef92bed10e4",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-28T05:
|
|
9
|
+
"built_at": "2026-09-28T05:31:55.050Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 543,
|
|
13
13
|
"agent_docs_stubbed": 26,
|
|
14
14
|
"functional_verbatim": 2511,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 3081,
|
|
20
|
+
"tree_sha256": "8984ba3615dc3744fe218ef870fafffbf77518b4db628e054010ca8cb40ed041",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -1627,7 +1627,7 @@
|
|
|
1627
1627
|
{
|
|
1628
1628
|
"path": "docs/adr/0233-a-copy-proposal-is-a-task-carrying-a-patch.md",
|
|
1629
1629
|
"mode": "0000644",
|
|
1630
|
-
"sha256": "
|
|
1630
|
+
"sha256": "8b4cf87eeccb6b9018ae6ff72d976b1cad4d6c0fd38b78ae9ebc44b1ff2223f7"
|
|
1631
1631
|
},
|
|
1632
1632
|
{
|
|
1633
1633
|
"path": "docs/adr/0234-idea-routing-capture-time-promotion-landing-matrix-homeless-inbox.md",
|
|
@@ -2164,10 +2164,15 @@
|
|
|
2164
2164
|
"mode": "0000644",
|
|
2165
2165
|
"sha256": "26bcf5464d93ebf3186831e3a2f9df0622b9e38dab9fd1daf9899c63a365c7a2"
|
|
2166
2166
|
},
|
|
2167
|
+
{
|
|
2168
|
+
"path": "docs/adr/0341-the-page-is-the-unit-of-tweak-mode.md",
|
|
2169
|
+
"mode": "0000644",
|
|
2170
|
+
"sha256": "ab4cefdfbe2505dabe75298a1f2ac98adee1dcbb205c3879d91813d242bdf60f"
|
|
2171
|
+
},
|
|
2167
2172
|
{
|
|
2168
2173
|
"path": "docs/adr/README.md",
|
|
2169
2174
|
"mode": "0000644",
|
|
2170
|
-
"sha256": "
|
|
2175
|
+
"sha256": "33c5508da42392b6ecc70ef3a0c1951714cb0a22551765c3e87213c1fc9bc253"
|
|
2171
2176
|
},
|
|
2172
2177
|
{
|
|
2173
2178
|
"path": "docs/api-reference.md",
|
|
@@ -2737,7 +2742,7 @@
|
|
|
2737
2742
|
{
|
|
2738
2743
|
"path": "docs/module-api-changelog.md",
|
|
2739
2744
|
"mode": "0000644",
|
|
2740
|
-
"sha256": "
|
|
2745
|
+
"sha256": "8c3a8c0069cd15d3b2ade2c2cc7c87a346c733cadd37ffc7c52a1f1ec22e3f6f"
|
|
2741
2746
|
},
|
|
2742
2747
|
{
|
|
2743
2748
|
"path": "docs/modules-contract.md",
|
|
@@ -8462,12 +8467,12 @@
|
|
|
8462
8467
|
{
|
|
8463
8468
|
"path": "package-lock.json",
|
|
8464
8469
|
"mode": "0000644",
|
|
8465
|
-
"sha256": "
|
|
8470
|
+
"sha256": "edc67014a2cb1e4b1b5d6e59d3da8cacb2b22c51c959bc28fc33e598b400e1c5"
|
|
8466
8471
|
},
|
|
8467
8472
|
{
|
|
8468
8473
|
"path": "package.json",
|
|
8469
8474
|
"mode": "0000644",
|
|
8470
|
-
"sha256": "
|
|
8475
|
+
"sha256": "7bf05c3c167f553ab3f5aa7f68adb070625e336e0ab8444d5f5ccd3609eff206"
|
|
8471
8476
|
},
|
|
8472
8477
|
{
|
|
8473
8478
|
"path": "public-docs/index.html",
|
|
@@ -8487,7 +8492,7 @@
|
|
|
8487
8492
|
{
|
|
8488
8493
|
"path": "release-notes.json",
|
|
8489
8494
|
"mode": "0000644",
|
|
8490
|
-
"sha256": "
|
|
8495
|
+
"sha256": "cf9eeae40e20a710b6342a6af31c2f29cd0044df79f221408a8f98c3531f1252"
|
|
8491
8496
|
},
|
|
8492
8497
|
{
|
|
8493
8498
|
"path": "scripts/bongos-mcp.js",
|
|
@@ -10567,7 +10572,7 @@
|
|
|
10567
10572
|
{
|
|
10568
10573
|
"path": "src/module-api.js",
|
|
10569
10574
|
"mode": "0000644",
|
|
10570
|
-
"sha256": "
|
|
10575
|
+
"sha256": "8419fdb2f19beaccf903a5f9d341ba6b0bf76b75d6f8e14c574e65e341f6a83e"
|
|
10571
10576
|
},
|
|
10572
10577
|
{
|
|
10573
10578
|
"path": "src/module-loader/catalog.js",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
- **Tasks:** [#1003114](https://cloudbongos.com/builders#/task/1003114) (R03 of goal 1000074, the artist's loop) — builds on [#1003112](https://cloudbongos.com/builders#/task/1003112) (R01, the registry) and [#1003113](https://cloudbongos.com/builders#/task/1003113) (R02, the flag and the queue)
|
|
6
6
|
- **Extends:** [ADR 0081](0081-tool-agnostic-design-layer.md) — the repo is the source of truth, with swappable tool adapters that land their output as a committed diff. This applies that contract to TEXT.
|
|
7
7
|
- **Extends:** [ADR 0178](0178-the-copy-desk-flag-and-queue.md) — which pinned that `copy_desk_flags` may never hold replacement text, and named this task as the one that would have to keep that true while adding an editing affordance.
|
|
8
|
+
- **Amended by:** [ADR 0341](0341-the-page-is-the-unit-of-tweak-mode.md) (task 1004332) — a proposal may also be a whole-page batch (the `page-tweak` blocks, D4); every rule below still holds for each of its lines.
|
|
8
9
|
- **Related:** 0083 (module system, ports), 0086 (goals), 0016 (authority resolved server-side), 0158 §2 (a verdict is advisory, never self-executing), 0162 (review gates retired — no human approval in the land path), 0197 (the ui-design module)
|
|
9
10
|
|
|
10
11
|
## Context
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# ADR 0341 — The page is the unit of Tweak Mode: one tweak task per page round, its status derived from the ledger
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted (the owner's decisions written down; the builder's picks for the gaps are listed in "Decided by the builder, owner to confirm")
|
|
4
|
+
- **Date:** 2026-09-27
|
|
5
|
+
- **Task:** [task 1004332](https://cloudbongos.com/builders#/task/1004332) (BV2.TW03, goal 1000095, working area 6). Spec: [`docs/specs/<redacted>.md`](../specs/<redacted>.md). Master design: [`docs/design/mocks/tweak-mode/`](../design/mocks/tweak-mode/README.md).
|
|
6
|
+
- **Deciders:** the owner, Masterqua (area owner), at the planning session of 2026-09-27 (meta task 1004312): the interview, then the prototype review. This ADR adds no decision the spec did not make, except the gaps listed at the end.
|
|
7
|
+
- **Amends:** [ADR 0233](0233-a-copy-proposal-is-a-task-carrying-a-patch.md) — a proposal was one occurrence; a page batch is every line of one page (§4 below). 0233's rules all still hold.
|
|
8
|
+
- **Replaces:** the `artist-review-on-ship` rule of [ADR 0242](0242-a-cascade-is-a-declaration-table-on-the-event-that-already-exists.md)'s cascade table (§9). The runner and the removal rule stay.
|
|
9
|
+
- **Reconciles:** [ADR 0241](0241-the-artist-gate-is-a-per-project-deploy-gate-that-reads-a-state.md) and [ADR 0162](0162-review-gates-retired-rank-consistent-ci-only.md) — the approval hold on a page tweak (§7).
|
|
10
|
+
- **Builds on:** the page inventory (task 1004314: `docs/page-inventory.json`, page id `<surface>:<page>`), [ADR 0178](0178-the-copy-desk-flag-and-queue.md) (the flag), [ADR 0172 per-craft compensation](0172-per-craft-compensation-ideator-credit-lane.md) (a craft's own credit lane), [ADR 0185](0185-spark-handoff-credit-split.md) (payees resolved server-side), [ADR 0096](0096-require-reward-before-workable.md) (a workable task carries a reward), [ADR 0120](0120-pay-on-land-and-builder-owned-rebase-gate.md) (pay on land).
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
An artist today reviews one ship at a time. The cascade files an "Artist review" task on every ship that touches copy or visuals. Of the 41 it filed, 34 were abandoned (33 with no note), 5 shipped and 2 sit in backlog. The owner's answer is Tweak Mode: the artist takes a whole page, rewrites every word of it as one document, submits, and a session applies it. The artist approves the rendered result before it ships, and every page carries a status, a count, a changelog and a changed-since marker.
|
|
15
|
+
|
|
16
|
+
The spec records sixteen decisions. The tasks that build them (TW04 to TW16) each need the same model underneath: what a tweak task is, where the words rest while they are being written, what state a page is in, and who is paid. This ADR is that model, so no later task has to decide it again.
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
### D1. The page is the unit, and its id comes from the page inventory
|
|
21
|
+
|
|
22
|
+
Every Tweak Mode record keys on a **page id** from `docs/page-inventory.json` (`builders:studio`, `landing:index`, `status:explore`, …). The route layer validates a page id against the inventory and refuses an unknown one with `unknown_page`. Surfaces and their sizes (the M in "N of M") come from the same file.
|
|
23
|
+
|
|
24
|
+
The server reads the inventory at runtime, so it must reach instances. **The task that first reads it on a server path (TW05) adds `docs/page-inventory.json`, and the page readings (D3), to the publish manifest**, the copy-registry precedent (ADR 0178).
|
|
25
|
+
|
|
26
|
+
### D2. One tweak task per page round; the task is the record
|
|
27
|
+
|
|
28
|
+
A **page tweak task** is an ordinary GDS task created by the claim route (D5):
|
|
29
|
+
|
|
30
|
+
| Field | Value |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `source` | `page-tweak` |
|
|
33
|
+
| `source_ref` | `page-tweak/<page_id>/r<n>`, where n is one plus the number of earlier page-tweak tasks for that page, whatever their status. It never repeats, so `POST /tasks`'s source-ref dedupe cannot hand back an old round. |
|
|
34
|
+
| `title` | `Tweak: <inventory title> (<page_id>)`, for example `Tweak: Home (landing:index)` |
|
|
35
|
+
| `kind` / `discipline` | `feature` / `artist` |
|
|
36
|
+
| `credits_reward` | `30`, the value every shipped artist review carried (§10). It also satisfies ADR 0096's reward gate. |
|
|
37
|
+
| `requires_rank` | none. An artist is a craft, not a rank (ADR 0178 §4, ADR 0233 §7). |
|
|
38
|
+
| version, goal | the version being built, and its catch-all goal (`allowCatchAll`), exactly as a copy proposal lands (`proposals.resolveTargetVersion`; `no_open_version` if nothing is being built) |
|
|
39
|
+
| `touches` | the page's `files` from the inventory |
|
|
40
|
+
|
|
41
|
+
**One open round per page.** A round is open until its task is `shipped` or `abandoned`. Finding or creating it and claiming it run in one transaction under `pg_advisory_xact_lock(hashtext('page-tweak/' || page_id))`, so two artists cannot both open a round.
|
|
42
|
+
|
|
43
|
+
### D3. The page reading is a committed, generated artifact
|
|
44
|
+
|
|
45
|
+
TW04's page reader writes **`docs/page-readings.json`**, generated and committed, the same family as the inventory and the copy registry. For each inventory page it lists the page's visible lines in page order, grouped by section (a page's default state; other states may add lines). Each line carries:
|
|
46
|
+
|
|
47
|
+
- `key`: `L0001`, `L0002`, … in page order. It is stable only within one reading, and the reading's hash says which one.
|
|
48
|
+
- `section`, `text` (with `{…}` holes, the registry's own convention);
|
|
49
|
+
- `placement`: `placed` (with `file`, `line`, `string_id` from `docs/copy-registry.json`), `shared` (shared-shell text such as the top bar and footer: the planning session's meaning of "same UI element") or `unplaced` (a string the registry cannot place).
|
|
50
|
+
|
|
51
|
+
Each page also carries `reading_hash`, over its non-shared lines, and `files_hash`, over the contents of its inventory `files`. Freshness is checked cheaply: a page whose `files_hash` no longer matches is stale, and ship-check names the command that re-reads it. **No browser is launched inside ship-check or fitness.** The re-read is the reader's own command.
|
|
52
|
+
|
|
53
|
+
### D4. The draft and the batch live on the task (amends ADR 0233)
|
|
54
|
+
|
|
55
|
+
ADR 0233 §2 stands: the words rest in a task description, never in a table, and nothing renders a task description as product copy. A page adds three fenced blocks. Each has one writer and one parser, in a pure file `modules/copy-desk/pages.js` (the `proposals.js` rules: no fs, no DB, no network):
|
|
56
|
+
|
|
57
|
+
- **```` ```page-tweak-draft ```` — the draft, while the artist writes.** `{ schema: 1, page_id, reading_hash, saved_at, lines: [{ key, section, before, after, placement }] }`. Only changed lines are stored. Autosave replaces the block in place. Only the holder of the page's writing claim (D5) may save.
|
|
58
|
+
- **```` ```page-tweak ```` — the batch, frozen at submit.** `{ schema: 1, page_id, reading_hash, submitted_at, lines: [{ key, section, before, after, placement, file, line, string_id }], unplaced_tasks: [task ids] }`. **Targets are resolved server-side from the current reading at submit, never from the request body** (0233 §7). A draft line whose `before` is no longer on the page is a named refusal, `target_gone`, on that line. Placeholder arithmetic (0233 §4) is checked per line at submit and again at apply.
|
|
59
|
+
- **```` ```page-tweak-applied ```` — written by the applying session (TW11).** `{ schema: 1, page_id, applied_at, reading_hash_after, lines_after: [text…], refused: [{ key, code }] }`, read from the regenerated `docs/page-readings.json` on the applier's branch. `lines_after` is the baseline that drift is measured from (D8).
|
|
60
|
+
- **```` ```page-tweak-sent-back ```` — one per send-back (D7).** `{ at, by, note }`, appended and never replaced.
|
|
61
|
+
|
|
62
|
+
**Unplaced lines** (spec decision 7): a rewrite of an `unplaced` line is taken out of the batch at submit. It files its own engineer task (`source: page-tweak-unplaced`, `source_ref: page-tweak-unplaced/<tweak task id>/<key>`, `backlog`, `discipline: engineer`) carrying the page, the line and the wording, and the page does not wait for it.
|
|
63
|
+
|
|
64
|
+
**Shared lines** are editable and travel in the batch like placed lines. They carry the margin note "changes this on every page" (TW10). A shared edit counts toward the page it was made on.
|
|
65
|
+
|
|
66
|
+
`copy-apply.js --batch` (TW11) applies every line of a `page-tweak` block using 0233's own rules: it re-derives each span, puts the `${expr}` holes back, and refuses by name (`target_gone`, `ambiguous_target`, `no_exact_span`, `placeholder_mismatch`, `path_outside_surface`). A refused line is listed in `refused`, never skipped silently. The batch lands with the lines that applied, and a refused line is shown to the artist at approval.
|
|
67
|
+
|
|
68
|
+
`modules/copy-desk/tests/copy_no_cms.mjs` **stays green by being amended in a reviewed diff**, which is what it exists for. Its write-verb list grows by exactly the routes in D11. Its migration list grows by `copy_desk_002_page_asks.sql` (D6). Its text-column list grows by `page_id`. `pages.js` joins `proposals.js` under the purity test. Nothing in the module's request path may require `copy-apply.js`.
|
|
69
|
+
|
|
70
|
+
### D5. The claim: artists claim, anyone asks
|
|
71
|
+
|
|
72
|
+
**Who may claim:** a builder whose craft includes artist (`builders.preferred_disciplines` contains `artist`, read server-side through the same reader `help-requests` uses, never from the request). Anyone else gets `not_an_artist` and is offered the ask.
|
|
73
|
+
|
|
74
|
+
**The claim** (`POST /copy-desk/pages/:pageId/claim`) finds the page's open round or creates it (D2) and takes a **web claim** on it: `bind_session=false`, no worktree, the kind of claim that migration core_204 already allows to coexist. The task goes to `active`. If another builder holds the round, the claim is refused with `page_held` and names the holder. If the caller already holds it, the claim returns the round (that is Resume). If the round is an unheld draft (released without submitting), the claim takes it over and the draft stays.
|
|
75
|
+
|
|
76
|
+
**"Take next"** is this same claim, on the page id the recommendation returned (D8), in one call.
|
|
77
|
+
|
|
78
|
+
### D6. A page ask is a flag with a page scope
|
|
79
|
+
|
|
80
|
+
A **page ask** (spec decision 9) is a row in the existing `copy_desk_flags` table, widened by `modules/copy-desk/migrations/copy_desk_002_page_asks.sql`. It is the copy-desk module's own table, so this is a module migration. `scope` gains `'page'`, a nullable `page_id text` is added, and the target check becomes: `string_id` is present exactly for `'string'` scope, and `page_id` exactly for `'page'` scope. The reason floor (`length(btrim(reason)) >= 10`) and the open, resolved and dismissed lifecycle are the flag's own. No column can hold replacement text (ADR 0178 §3 still holds).
|
|
81
|
+
|
|
82
|
+
- **Anyone may ask, at any rank** (`POST /copy-desk/pages/:pageId/asks`). A second open ask by the same builder on the same page returns the first one.
|
|
83
|
+
- An open ask is **resolved when the page's next tweak ships**, with `resolved_task_id` set to that tweak task. It is never auto-dismissed.
|
|
84
|
+
- "N people asked for it" counts the distinct `flagged_by` values among open page asks.
|
|
85
|
+
- Existing string and surface flags are kept and deleted by nothing. They do not count as asks.
|
|
86
|
+
|
|
87
|
+
### D7. The round's states, and approve and send-back
|
|
88
|
+
|
|
89
|
+
A round's state is **derived** from its task and never stored:
|
|
90
|
+
|
|
91
|
+
| State | Derived from |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `writing` | `active`, with a web claim held (by the artist) |
|
|
94
|
+
| `draft` | `ready`, a `page-tweak-draft` block present and no `page-tweak` block: an unheld draft that any artist may resume |
|
|
95
|
+
| `submitted` | `ready`, with a `page-tweak` block, and no `page-tweak-applied` block newer than the latest send-back: the `/tweak` queue |
|
|
96
|
+
| `applying` | `active`, with a CLI claim held (a worktree). A failed grade stays here, and the applier recovers it the ordinary way. |
|
|
97
|
+
| `waiting_for_artist` | `completed`, with the latest grade **passed** and the task a page tweak: the Approval queue |
|
|
98
|
+
| `landing` | `confirmed` |
|
|
99
|
+
| (closed) | `shipped` (the page's count goes up) or `abandoned` |
|
|
100
|
+
|
|
101
|
+
**The hold.** When a page-tweak task's grade passes, `applyGrade` records the grade but **does not promote `completed` to `confirmed`**. The task waits at `completed`. `ship.js` publishes the branch and its PR on that held pass (a grade-parked ship otherwise never publishes, and a later confirm would strand at `no_branch_no_tip`). It then prints that the page is waiting for the artist and exits. Readers that treat `completed` as a failed grade (strand-watch, `/grade-recover`, the board) must tell the two apart by the passed grade.
|
|
102
|
+
|
|
103
|
+
**Approve** (`POST /copy-desk/pages/:pageId/approve`, through the lifecycle port) performs the promotion the grade withheld: `completed` to `confirmed`, with `advanceToMerge`. The server then lands it exactly as it lands any confirmed task. Nothing ships without approval (spec decision 4).
|
|
104
|
+
|
|
105
|
+
**Send back** (`POST /copy-desk/pages/:pageId/send-back`) requires a note of at least 10 characters. It appends a `page-tweak-sent-back` block, releases the applier's claim, closes the unmerged PR and returns the task to `ready`, so the round reads `submitted` again: "It comes back to the queue once it is re-applied" (the mock). The author may amend the draft while the round waits, and the next applier's claim freezes it again.
|
|
106
|
+
|
|
107
|
+
Both are new transitions on the lifecycle port (`approveHeldTweak`, `sendBackHeldTweak`), owned by lifecycle because it owns the state machine. Copy-desk calls them through the port and never writes `tasks.status` itself.
|
|
108
|
+
|
|
109
|
+
**Why this hold is admissible under ADRs 0162 and 0241.** It passes ADR 0241's admissibility test. It is evaluated from state (a passed grade on a page-tweak task). It is the same for every builder. It has a named escape hatch: the project owner may approve through the existing override-request path, recorded and audit-logged. And it holds only work whose substance is the artist's own words: the applier transcribed a page the artist wrote, and the artist confirms the transcription. It is not a gate on a builder's own work.
|
|
110
|
+
|
|
111
|
+
### D8. Status, count, changelog, drift and the recommendation are all derived
|
|
112
|
+
|
|
113
|
+
**No status table** (planning decision). Everything below is a read over page-tweak tasks, their fenced blocks, `docs/page-readings.json` and the inventory.
|
|
114
|
+
|
|
115
|
+
- **Count per page:** the number of shipped page-tweak tasks for that page id. **Text count:** those whose batch changed text, which is every one while Tweak Mode is text only. **UI count:** those whose batch has `kind: 'ui'`. The field is reserved for visual tweaks, is absent today, and so reads 0.
|
|
116
|
+
- **Status per page:** `Untweaked` (count 0), `Text tweaked`, `UI tweaked`, or `Both`, shown with the count ("Text tweaked 2x").
|
|
117
|
+
- **Changelog:** one entry per shipped round: the artist (the payee, §10), `shipped_at`, the line count, and before and after per line, from the `page-tweak` block.
|
|
118
|
+
- **Drift, "changed since last tweak"** (spec decision 8): the page's current non-shared reading lines, compared as a multiset of texts with `lines_after` of its latest shipped round. The lines that differ are the changed-since lines ("4 lines changed since your last pass"). The ships that changed them are the shipped tasks since that round whose `touches` meet the page's files. A tweaked page keeps its count while it drifts.
|
|
119
|
+
- **Artist Review Status, per surface:** "N of M tweaked". N is the surface's pages with count ≥ 1, and M is its pages in the inventory. Drift does not lower N. Surfaces are listed landing, builders hall, status (the mock's order).
|
|
120
|
+
- **The artist's tally:** credits is the sum of the artist's `tweak.page_approved:` credit rows (§10). Pages tweaked is the number of shipped rounds they authored. This week is those credits booked in the trailing seven days.
|
|
121
|
+
- **The recommendation** (TW07): candidates are inventory pages with no open round held or in flight by someone else. A page the caller holds is Resume, not a recommendation. The tiers, in this order: **asked for** (by distinct askers, most first, then the oldest open ask), then **changed since the last tweak** (most changed lines first), then **untweaked** (surface order landing, builders, status, then inventory order). Tweaked pages with no drift come last. Each candidate carries its why ("2 people asked for it", "4 lines changed since your last pass", "untweaked"). The first is the recommendation, and the next few are "pick another".
|
|
122
|
+
|
|
123
|
+
### D9. The artist-review cascade is replaced, not moved
|
|
124
|
+
|
|
125
|
+
The `artist-review-on-ship` entry is **removed** from `CASCADES` in `modules/lifecycle/cascade.js` (TW14). The runner, the removal rule and the idea cascades stay. **A ship that changes a page's words then writes nothing.** Drift is derived (D8), so a changed page reads "changed since last tweak" and rises in the recommendation on its own. `hasCopyOrVisualWork` stays exported, because the grader's advisory reader lens is scoped by it (ADR 0298). The two artist review tasks already in backlog stay as ordinary work, and no data is deleted. Task 1004078 is merged into TW14, since with no review cards it has nothing left to fix.
|
|
126
|
+
|
|
127
|
+
### D10. What the reviews paid, and what an approved page pays
|
|
128
|
+
|
|
129
|
+
**What the reviews paid, from the ledger.** An artist review task was an ordinary task: `kind: unclassified` (×1.0 in `KIND_MULTIPLIERS`) and `credits_reward: 30` (all five shipped reviews carried 30, set at triage). It paid through the two ordinary streams (ADR 0054, ADR 0146):
|
|
130
|
+
|
|
131
|
+
1. The **per-task estimate**, `credits_reward × kind multiplier` = 30, to the shipper on land (`awardLandingCredit`). This is **withheld under `cost-plus-only`, which is this instance's mode**, so all five shipped reviews show `credits_awarded: 0`.
|
|
132
|
+
2. The **cost-plus session reward**, `round(true_cost_usd × 1.20)`, to whichever session shipped it.
|
|
133
|
+
|
|
134
|
+
An artist working in the studio runs no session, so stream 2 can never reach them. Paying "what the reviews paid" literally on this instance would pay a studio artist nothing.
|
|
135
|
+
|
|
136
|
+
**The rule.** An approved page pays its artist the review's priced value, **`credits_reward × KIND_MULTIPLIERS[kind]`, which is 30 credits for a `feature` round at 30**. It is booked on its own **artist credit lane** (the ADR 0172 per-craft precedent): a policy key `reward.artistCredit`, default true, not suppressed by `cost-plus-only`, just as the ideator lane is not.
|
|
137
|
+
|
|
138
|
+
- **When:** at the land (the `shipped` transition), inside the lifecycle's ship transaction through the `reward` port (ADR 0120: pay on land).
|
|
139
|
+
- **Payee, resolved server-side** (ADR 0185): the builder whose web claim on the task was released by the submit that froze the shipped batch, read from the `claims` table. Never the caller, and never a field in the description, which the task's editors could change.
|
|
140
|
+
- **Reason key:** `tweak.page_approved:<task id>`, idempotent per (builder, reason).
|
|
141
|
+
- **The applier keeps its normal build reward** (the cost-plus session reward, on land, unchanged). **The shipper's per-task estimate is not paid on a page-tweak task**, because the artist lane takes that stream, so the total is conserved in both reward modes.
|
|
142
|
+
- **Self-applied** (the artist also ran `/tweak`): one artist row plus their own session reward. Nothing is doubled.
|
|
143
|
+
- The tally (D8) reads these rows.
|
|
144
|
+
|
|
145
|
+
### D11. The routes, in one place
|
|
146
|
+
|
|
147
|
+
All are on the copy-desk module (it `consumes: ["lifecycle"]`; no new port is provided). Reads first, then writes:
|
|
148
|
+
|
|
149
|
+
| Route | Task | What |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `GET /copy-desk/pages` | TW05 | every page's status, count and drift, plus the per-surface rollup |
|
|
152
|
+
| `GET /copy-desk/pages/:pageId` | TW05 | one page: status, changelog, changed-since lines, the open round and its state |
|
|
153
|
+
| `GET /copy-desk/tally` | TW05 | the caller's tally |
|
|
154
|
+
| `GET /copy-desk/next` | TW07 | the recommendation and the alternatives |
|
|
155
|
+
| `GET /copy-desk/pages/:pageId/draft.docx` | TW09 | the download |
|
|
156
|
+
| `POST /copy-desk/pages/:pageId/claim` | TW06 | claim, resume, take next |
|
|
157
|
+
| `POST /copy-desk/pages/:pageId/asks` | TW06 | ask for a page |
|
|
158
|
+
| `PUT /copy-desk/pages/:pageId/draft` | TW08 | autosave (the holder only) |
|
|
159
|
+
| `POST /copy-desk/pages/:pageId/draft.docx` | TW09 | the upload, merged into the draft |
|
|
160
|
+
| `POST /copy-desk/pages/:pageId/submit` | TW08 | freeze and queue |
|
|
161
|
+
| `POST /copy-desk/pages/:pageId/approve` | TW12 | the held pass goes on to land |
|
|
162
|
+
| `POST /copy-desk/pages/:pageId/send-back` | TW12 | back to the queue with a note |
|
|
163
|
+
|
|
164
|
+
The lifecycle port gains what these need: `updateTaskDescription`, `webClaimTask`, `releaseClaim`, `approveHeldTweak` and `sendBackHeldTweak`. Each lands with the task that first calls it.
|
|
165
|
+
|
|
166
|
+
### D12. The Word round trip
|
|
167
|
+
|
|
168
|
+
**Download** renders the draft over the page reading as a `.docx` with **no new dependency**. A .docx is a zip, and a small writer and reader on `node:zlib` in `modules/copy-desk/docx.js` (pure over Buffers) is the cheapest viable option (spec: no paid service). The file holds:
|
|
169
|
+
|
|
170
|
+
- the page title as the title, and each section as a Heading 2;
|
|
171
|
+
- one paragraph per line, in page order, showing the draft's `after` (or the current text if the line is unchanged). Each paragraph is wrapped in a plain-text content control (`w:sdt`) tagged with the line's `key`;
|
|
172
|
+
- `shared` and `unplaced` lines shown in their own paragraph style with a Word comment ("changes this on every page", "goes to an engineer");
|
|
173
|
+
- custom document properties carrying `page_id` and `reading_hash`.
|
|
174
|
+
|
|
175
|
+
**Upload** maps paragraphs back to lines **by tag**. If the editor stripped the tags, it maps by position, with the same section headings and the same number of lines per section. The changed texts merge into the draft (never straight to the site), and the merge is shown to the artist in the editor. The upload is refused, with the lines named, when:
|
|
176
|
+
|
|
177
|
+
- `structure_changed`: lines were **added** (untagged or surplus paragraphs), **dropped** (a key missing) or **reordered** (the keys out of page order), or a section heading changed. The refusal lists each moved line by section and its first words;
|
|
178
|
+
- `wrong_page`: the file's `page_id` is not the page;
|
|
179
|
+
- `reading_moved`: the file's `reading_hash` is not the draft's, because the page changed since the download. The artist is asked to download again, so no stale line is merged by position.
|
|
180
|
+
|
|
181
|
+
## Decided by the builder, owner to confirm
|
|
182
|
+
|
|
183
|
+
The spec does not answer these. Each is the choice most faithful to it, and each is a small change here if the owner rules otherwise. (The TW03 task text said a gap should become a blocker. The owner's overnight rule for this chain says to pick and list, so they are listed rather than filed.)
|
|
184
|
+
|
|
185
|
+
1. **The artist's pay on this instance** (D10): 30 credits per approved page on a new artist lane that `cost-plus-only` does not suppress. The literal reading, the reviews' stream as it actually paid here, is zero for a studio artist.
|
|
186
|
+
2. **The hold sits on `completed` with a passed grade**, not on a new task status (D7). A new status would touch every reader of the state machine.
|
|
187
|
+
3. **Who approves:** the author, or any other artist-craft builder (so an absent author does not strand a page), with the project owner's override as the escape hatch.
|
|
188
|
+
4. **Send-back returns the round to the apply queue**, the mock's words, and the author may amend the draft while it waits. The applier's sent-back session is unpaid, because its ship never lands (pay on land).
|
|
189
|
+
5. **Page asks widen `copy_desk_flags`** rather than adding a table (D6), and open string flags do not count as asks.
|
|
190
|
+
6. **Shared-shell lines are editable in a page's batch** and count toward that page (D4).
|
|
191
|
+
7. **Drift excludes shared lines**, so a shell change does not mark all 30 hall pages changed (D8).
|
|
192
|
+
8. **Recommendation ties and order** (D8). The mock's sample list puts an untweaked page before a changed one. The spec's order (asked, changed, untweaked) wins, and the mock's list is sample data.
|
|
193
|
+
9. **The Approval queue shows the renders `/tweak` attached**, images of the applied branch, never a DOM built from the batch. That keeps ADR 0233's "nothing renders a task description as product copy" true. The renders need named slots on task visuals (TW11). Today there is one image per task.
|
|
194
|
+
10. **Round numbering and titles** (D2), the **one-open-round lock** (D2), and **no rank floor** on a tweak task.
|
|
195
|
+
11. **The Word file's shape** (D12): content-control tags, and refusing on `reading_moved` rather than merging by position.
|
|
196
|
+
12. **The readings ship with the core** and are refreshed by `files_hash`, with no browser in ship-check (D1, D3).
|
|
197
|
+
|
|
198
|
+
## Consequences
|
|
199
|
+
|
|
200
|
+
- TW04 to TW16 are cut against this ADR. Each task's description names the sections that govern it.
|
|
201
|
+
- `copy_no_cms.mjs` changes in reviewed diffs (TW06, TW08, TW09, TW12), and each diff has to justify what it adds to the lists.
|
|
202
|
+
- The artist gate (ADR 0241) holds nothing new. With the cascade gone, `strict` would hold a deploy only on the two review tasks still in backlog. The gate is `off` for now (spec decision 13). Task 1004107 stays open for when it returns.
|
|
203
|
+
- Strand and recovery tools learn one new fact: `completed` with a passed grade on a page tweak is waiting for the artist, not a failure.
|
|
204
|
+
|
|
205
|
+
## Rejected
|
|
206
|
+
|
|
207
|
+
- **A page status table.** The planning session ruled it out. A table would be a second record of what the task ledger already says.
|
|
208
|
+
- **A `copy_desk_drafts` table.** ADR 0178's forbidden column with extra steps (0233 §2).
|
|
209
|
+
- **Keeping the per-ship review beside Tweak Mode.** Spec decision 10: it is replaced.
|
|
210
|
+
- **Letting the artist's approval gate any ship other than a page tweak.** ADR 0241's reason still holds.
|
|
211
|
+
- **A paid .docx service or a heavy library.** The format is a zip of XML, and node already reads zips.
|
package/docs/adr/README.md
CHANGED
|
@@ -432,3 +432,4 @@ This keeps the decision history honest and traceable.
|
|
|
432
432
|
| 0338 | [**Modules travel through a Bongos-hosted store, and an acquired module keeps working forever** ([task 1003782](https://cloudbongos.com/builders#/task/1003782), goal 1000091 — working area 5, owner Will). A module had no transport of its own: it reached an instance only inside the core (ADR 0107 §6) or in the instance's repo. Weighed npm packages, a Bongos-hosted store and git refs against the four things area 5's criteria require — entitlement before install, a per-version score, a tester channel, and a delist that stops distribution. npm and git refs fail entitlement and delist by construction. **D1:** Cloud Bongos hosts the store; a module version is a tarball + hash manifest in the same format `package-core.js` already emits (same `isPublishable()` gate), registry rows on the control-plane DB, artifacts in the already-paid DO Spaces bucket, served by the control plane only after an entitlement + channel check; free modules take the same path at price zero; install/update verify hashes on arrival; modules' own npm deps still travel via npm (ADR 0138). No new fixed cost. **D2 (owner):** delisting, author withdrawal or deletion removes a module from the catalog and stops new acquisitions and updates, but **an acquired module keeps working forever** — the store never disables or removes an installed copy and a delist never revokes an entitlement; the holder is told no more updates will come. Security issues are not a special case; a remote kill switch would need a superseding ADR. Rejected: npm, git refs, remote disable on delist.](0338-modules-travel-through-a-bongos-hosted-store.md) | modules / distribution / economy |
|
|
433
433
|
| 0339 | [**A project's owner upgrades it, and a hosted project hands off to the hub's door** ([task 1004174](https://cloudbongos.com/builders#/task/1004174), goal 1000090 — the owner's ruling of 2026-09-26 that only a project's owner upgrades it). Amends ADR 0293 D5. **D1:** the deploy door's preview and move routes admit the OWNER of a tenant row (`co-tenant`/`cloud-host`) for that row, asked first; every other caller meets the unmodified `core.pin.move` gate, so a non-owner's refusal is unchanged whether or not the id exists and the widening can only admit. **D2:** the platform's own row stays atom-only. **D3:** `GET /core-update` names `<hub>/deploy?project=<slug>` on a federated project, and the hosted hall's banner and rail hand off there (the dead local Deploy item is gone); the hub's `/deploy` shell admits a project owner through `alsoAdmits: 'isProjectOwner'`. **D4:** the intent's actor records the door (`api:owner:<id>` / `api:archon:<id>`), both attributed on the `core_upgrades` ledger. Rejected: a tenant-side API that performs the upgrade, granting the system atom to owners, owners moving the platform row. ADR 0328 Phase 1 (task 1004159) should fold this gate into `requireProjectOwner`.](0339-a-projects-owner-upgrades-it-and-a-hosted-project-hands-off-to-the-hub-door.md) | provisioning / deploy door / permissions |
|
|
434
434
|
| 0340 | [**Every hall takes its update from Settings, and /deploy is the platform hall's page** ([task 1004296](https://cloudbongos.com/builders#/task/1004296), goal 1000090 — the owner's 2026-09-25 asks: a Settings version panel "just like iOS", and /deploy only in the Bongos hall). Amends ADR 0339 D3 and the reach of ADR 0293's page. **D1:** Settings → Software update in the core — the running core, the newest release the runner's own channel rule allows, and what each version in between carries from the newest package's release notes; every signed-in builder reads it, and "up to date" is said only from an answer. **D2:** its Update button leads to the door (local /deploy, the hub's /deploy?project=, or a `bongos upgrade` sentence), drawn only for holders of core.pin.move. **D3:** /deploy 302s to Settings on a hall without the provisioning module. **D4:** the banner links Settings everywhere but the platform; the rail is no longer re-pointed; /core-update drops deploy_url. **D5:** the registry reader moved into the core (module-api readPackageRegistry). Rejected: a second copy of the two-step door, gating the panel on the atom, a "moved" page.](0340-every-hall-takes-its-update-from-settings-and-deploy-is-the-platform-halls-page.md) | deploy door / settings / core update |
|
|
435
|
+
| 0341 | [**The page is the unit of Tweak Mode: one tweak task per page round, its status derived from the ledger** ([task 1004332](https://cloudbongos.com/builders#/task/1004332), goal 1000095 — BV2.TW03, the owner's Tweak Mode decisions of 2026-09-27 written down). Amends ADR 0233 (a proposal becomes a page batch); replaces the artist-review-on-ship cascade rule. **D1-D3:** every record keys on a page id from docs/page-inventory.json; a round is a `page-tweak` task (`source_ref page-tweak/<page>/r<n>`, 30c, catch-all goal, one open round per page under an advisory lock); TW04 commits docs/page-readings.json (line keys, placed/shared/unplaced, reading and files hashes, no browser in ship-check). **D4:** the draft, the frozen batch, the apply record and each send-back are fenced blocks on the task, parsed by a pure pages.js; unplaced rewrites file their own engineer task; copy_no_cms.mjs grows in reviewed diffs. **D5-D6:** artist-craft builders claim with a web claim; anyone asks, and a page ask is a copy_desk_flags row with scope page. **D7:** round states are derived; a passed grade on a page tweak holds at completed until the artist approves (the confirm) or sends it back to the apply queue, with the owner override as the escape hatch. **D8:** count, status, changelog, drift (against the last round's lines_after), N of M per surface, the tally and the recommendation order are all reads. **D9:** the cascade rule is removed; hasCopyOrVisualWork stays for the reader lens. **D10:** the reviews paid 30c per task on the estimate stream (withheld under cost-plus-only) plus the session's cost-plus; an approved page pays its artist 30c on a new artist lane that cost-plus-only does not suppress, payee from the claims table, applier keeps its session reward. **D11-D12:** the route table; a dependency-free .docx with content-control line tags, refused as structure_changed, wrong_page or reading_moved. Twelve builder picks are listed for the owner to confirm.](0341-the-page-is-the-unit-of-tweak-mode.md) | copy desk / artist loop / economy |
|
|
@@ -2597,5 +2597,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2597
2597
|
landed since 1.19.1053 with no explicit bump. run 36378326588. (task 1002620)
|
|
2598
2598
|
1.19.1055 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2599
2599
|
landed since 1.19.1054 with no explicit bump. run 36380323033. (task 1002620)
|
|
2600
|
+
1.19.1056 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2601
|
+
landed since 1.19.1055 with no explicit bump. run 36382270561. (task 1002620)
|
|
2600
2602
|
---------------------------------------------------------------------------
|
|
2601
2603
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.1056",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.1056",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.1056",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -7631,5 +7631,11 @@
|
|
|
7631
7631
|
"id": "1004314",
|
|
7632
7632
|
"text": "Every page the project serves now has a name that stays put. A new generated list names all 37 pages across the builders hall (30), the landing site (5) and the status site (2). Each entry gives the page a short id such as bui"
|
|
7633
7633
|
}
|
|
7634
|
+
],
|
|
7635
|
+
"1.19.1056": [
|
|
7636
|
+
{
|
|
7637
|
+
"id": "1004332",
|
|
7638
|
+
"text": "Tweak Mode now has one written plan that every later step builds on. It says how a page is taken, where the artist's words rest while they write, how a page shows as tweaked or changed since, how the Word download and upload w"
|
|
7639
|
+
}
|
|
7634
7640
|
]
|
|
7635
7641
|
}
|
package/src/module-api.js
CHANGED
|
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
71
71
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
72
72
|
// the entry to that file. Look for a version's history there, not here.
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
74
|
-
const CORE_VERSION = '1.19.
|
|
74
|
+
const CORE_VERSION = '1.19.1056'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
75
75
|
|
|
76
76
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
77
77
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|