@bongos/core 1.19.817 → 1.19.819
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 +48 -28
- package/.claude/skills/strand-fix/SKILL.md +11 -2
- package/.gitattributes +9 -0
- package/docs/adr/0298-the-reader-lens-is-advisory-and-waits-on-a-voice-guide.md +121 -0
- package/docs/adr/README.md +1 -0
- package/docs/module-api-changelog.md +5 -1
- package/docs/recipes/gds-db-backup.md +46 -0
- package/modules/discord/bug-attachments.js +36 -1
- package/modules/lifecycle/cascade.js +18 -5
- package/modules/lifecycle/tests/cascade.mjs +11 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/bug-attachment-audit.js +143 -0
- package/scripts/gds/capture.js +19 -0
- package/scripts/gds/db-backup-nightly.sh +37 -1
- package/scripts/gds/linkify-refs.js +9 -1
- package/scripts/gds/run-unit-tests.js +8 -1
- package/scripts/gds/skill-preflight.js +28 -4
- package/src/module-api.js +1 -1
- package/tests/bug_attachment_audit.mjs +122 -0
- package/tests/cli_surface.mjs +31 -2
- package/tests/linkify_refs.mjs +17 -0
- package/tests/module_api_lazy.mjs +32 -1
- package/tests/routine_capture_kind.mjs +147 -0
- package/tests/skill_preflight.mjs +45 -0
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.819",
|
|
6
|
+
"core_contract": "1.19.819",
|
|
7
|
+
"source_commit": "b56f301dfa5e889c7f8e46624eb564de4bed3510",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-17T09:33:00.702Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 490,
|
|
13
13
|
"agent_docs_stubbed": 26,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2220,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2736,
|
|
20
|
+
"tree_sha256": "235bf7bed8d6ad67aa73deddbbb1dc9a87fa6a697935f24b52bf2cdf5c299139",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -247,7 +247,7 @@
|
|
|
247
247
|
{
|
|
248
248
|
"path": ".claude/skills/strand-fix/SKILL.md",
|
|
249
249
|
"mode": "0000644",
|
|
250
|
-
"sha256": "
|
|
250
|
+
"sha256": "938a3a8ee5bfb25036f03519fc27de795ba88bd7ed8b33a7f6e31639023b4304"
|
|
251
251
|
},
|
|
252
252
|
{
|
|
253
253
|
"path": ".claude/skills/worktree-clean/SKILL.md",
|
|
@@ -302,7 +302,7 @@
|
|
|
302
302
|
{
|
|
303
303
|
"path": ".gitattributes",
|
|
304
304
|
"mode": "0000644",
|
|
305
|
-
"sha256": "
|
|
305
|
+
"sha256": "2d993829478b5f8931e5a8248818b70279055d1af643e51f07de6161eab593aa"
|
|
306
306
|
},
|
|
307
307
|
{
|
|
308
308
|
"path": "CLAUDE.md",
|
|
@@ -1974,10 +1974,15 @@
|
|
|
1974
1974
|
"mode": "0000644",
|
|
1975
1975
|
"sha256": "4e6d5268c58e905c86acc1bbd4c66778e3695ecd37cbc1c9e0f1448cd67f26a9"
|
|
1976
1976
|
},
|
|
1977
|
+
{
|
|
1978
|
+
"path": "docs/adr/0298-the-reader-lens-is-advisory-and-waits-on-a-voice-guide.md",
|
|
1979
|
+
"mode": "0000644",
|
|
1980
|
+
"sha256": "8fcac086f276e57b59ba635cdf34600bf78bfb69d80177d43065dfc6776b0611"
|
|
1981
|
+
},
|
|
1977
1982
|
{
|
|
1978
1983
|
"path": "docs/adr/README.md",
|
|
1979
1984
|
"mode": "0000644",
|
|
1980
|
-
"sha256": "
|
|
1985
|
+
"sha256": "14c4e2e04bba4a0438375364a88e41eca44983ab0fb2c3ecd13bf0190f0ad151"
|
|
1981
1986
|
},
|
|
1982
1987
|
{
|
|
1983
1988
|
"path": "docs/api-reference.md",
|
|
@@ -2542,7 +2547,7 @@
|
|
|
2542
2547
|
{
|
|
2543
2548
|
"path": "docs/module-api-changelog.md",
|
|
2544
2549
|
"mode": "0000644",
|
|
2545
|
-
"sha256": "
|
|
2550
|
+
"sha256": "671ac9949acbb7120d9a6b3acf43082dba90d2c5119afb61b7cd215fbbec2fdc"
|
|
2546
2551
|
},
|
|
2547
2552
|
{
|
|
2548
2553
|
"path": "docs/modules-contract.md",
|
|
@@ -2687,7 +2692,7 @@
|
|
|
2687
2692
|
{
|
|
2688
2693
|
"path": "docs/recipes/gds-db-backup.md",
|
|
2689
2694
|
"mode": "0000644",
|
|
2690
|
-
"sha256": "
|
|
2695
|
+
"sha256": "b3d6a3e3a400bf593b0f2d671f35cb1a60279f495c8763d311f8cdb68a6d603a"
|
|
2691
2696
|
},
|
|
2692
2697
|
{
|
|
2693
2698
|
"path": "docs/recipes/instance-deploy.md",
|
|
@@ -4137,7 +4142,7 @@
|
|
|
4137
4142
|
{
|
|
4138
4143
|
"path": "modules/discord/bug-attachments.js",
|
|
4139
4144
|
"mode": "0000644",
|
|
4140
|
-
"sha256": "
|
|
4145
|
+
"sha256": "b773430f5abe8a742e35dc59335805c531821d0701a50f2090ed599f3c66dafe"
|
|
4141
4146
|
},
|
|
4142
4147
|
{
|
|
4143
4148
|
"path": "modules/discord/db.js",
|
|
@@ -5497,7 +5502,7 @@
|
|
|
5497
5502
|
{
|
|
5498
5503
|
"path": "modules/lifecycle/cascade.js",
|
|
5499
5504
|
"mode": "0000644",
|
|
5500
|
-
"sha256": "
|
|
5505
|
+
"sha256": "acc9cf557aabc3fe10efc8c7272162cf288428e8077a24f5df06648e30881337"
|
|
5501
5506
|
},
|
|
5502
5507
|
{
|
|
5503
5508
|
"path": "modules/lifecycle/claim-eligibility.js",
|
|
@@ -5917,7 +5922,7 @@
|
|
|
5917
5922
|
{
|
|
5918
5923
|
"path": "modules/lifecycle/tests/cascade.mjs",
|
|
5919
5924
|
"mode": "0000644",
|
|
5920
|
-
"sha256": "
|
|
5925
|
+
"sha256": "0734ad734ac5ef05c86fa81493c12fed08ffe421781d9ceeebe413c876bf9cf1"
|
|
5921
5926
|
},
|
|
5922
5927
|
{
|
|
5923
5928
|
"path": "modules/lifecycle/version-autoclose.js",
|
|
@@ -7797,12 +7802,12 @@
|
|
|
7797
7802
|
{
|
|
7798
7803
|
"path": "package-lock.json",
|
|
7799
7804
|
"mode": "0000644",
|
|
7800
|
-
"sha256": "
|
|
7805
|
+
"sha256": "80eb7e1ecf3c324277a00d732da45ff1e094ac64e4ba176777395fc5c4cb33e4"
|
|
7801
7806
|
},
|
|
7802
7807
|
{
|
|
7803
7808
|
"path": "package.json",
|
|
7804
7809
|
"mode": "0000644",
|
|
7805
|
-
"sha256": "
|
|
7810
|
+
"sha256": "30a0133ce3cc21770d6915e5d7bd65d7c8a3a8e0494d0eb0e3230586f66f0876"
|
|
7806
7811
|
},
|
|
7807
7812
|
{
|
|
7808
7813
|
"path": "public-docs/index.html",
|
|
@@ -8039,6 +8044,11 @@
|
|
|
8039
8044
|
"mode": "0000644",
|
|
8040
8045
|
"sha256": "bf2f9c0ccbdaaa615bcd4020402dac32466a5a3ace6a1fb5cb543baa905a2ecd"
|
|
8041
8046
|
},
|
|
8047
|
+
{
|
|
8048
|
+
"path": "scripts/gds/bug-attachment-audit.js",
|
|
8049
|
+
"mode": "0000644",
|
|
8050
|
+
"sha256": "959e349e988146d7be1eb872cf80eb0d25d3d4daf95bdcf1b077a0d81c0c598f"
|
|
8051
|
+
},
|
|
8042
8052
|
{
|
|
8043
8053
|
"path": "scripts/gds/bug-triage.js",
|
|
8044
8054
|
"mode": "0000644",
|
|
@@ -8062,7 +8072,7 @@
|
|
|
8062
8072
|
{
|
|
8063
8073
|
"path": "scripts/gds/capture.js",
|
|
8064
8074
|
"mode": "0000644",
|
|
8065
|
-
"sha256": "
|
|
8075
|
+
"sha256": "9edb2127b0712ec052c7c9946246665773e552c95f1e276c1991854fe9feb12f"
|
|
8066
8076
|
},
|
|
8067
8077
|
{
|
|
8068
8078
|
"path": "scripts/gds/category-advisory-guard.js",
|
|
@@ -8172,7 +8182,7 @@
|
|
|
8172
8182
|
{
|
|
8173
8183
|
"path": "scripts/gds/db-backup-nightly.sh",
|
|
8174
8184
|
"mode": "0000644",
|
|
8175
|
-
"sha256": "
|
|
8185
|
+
"sha256": "5e8b7ebfe0b82a426e229a256daf231cb597d8e1df2b908c528a0ed0a67066a5"
|
|
8176
8186
|
},
|
|
8177
8187
|
{
|
|
8178
8188
|
"path": "scripts/gds/db-backup-offsite.sh",
|
|
@@ -8512,7 +8522,7 @@
|
|
|
8512
8522
|
{
|
|
8513
8523
|
"path": "scripts/gds/linkify-refs.js",
|
|
8514
8524
|
"mode": "0000644",
|
|
8515
|
-
"sha256": "
|
|
8525
|
+
"sha256": "16e9c92e2466f92de1339eac911193807678c4c7d2c6b837ecd1195c102ebf56"
|
|
8516
8526
|
},
|
|
8517
8527
|
{
|
|
8518
8528
|
"path": "scripts/gds/lint-infra-exec.js",
|
|
@@ -8827,7 +8837,7 @@
|
|
|
8827
8837
|
{
|
|
8828
8838
|
"path": "scripts/gds/run-unit-tests.js",
|
|
8829
8839
|
"mode": "0000644",
|
|
8830
|
-
"sha256": "
|
|
8840
|
+
"sha256": "be80f6ea38abdbc71f5f718b51db9a54b931745859e885ea1a97ff9465a81596"
|
|
8831
8841
|
},
|
|
8832
8842
|
{
|
|
8833
8843
|
"path": "scripts/gds/sandbox-stage.js",
|
|
@@ -9137,7 +9147,7 @@
|
|
|
9137
9147
|
{
|
|
9138
9148
|
"path": "scripts/gds/skill-preflight.js",
|
|
9139
9149
|
"mode": "0000644",
|
|
9140
|
-
"sha256": "
|
|
9150
|
+
"sha256": "913e8b89d3023375462a0308480ddebddbc8536a0312961297a12382f5b40e70"
|
|
9141
9151
|
},
|
|
9142
9152
|
{
|
|
9143
9153
|
"path": "scripts/gds/smoke-batch-claim.sh",
|
|
@@ -9692,7 +9702,7 @@
|
|
|
9692
9702
|
{
|
|
9693
9703
|
"path": "src/module-api.js",
|
|
9694
9704
|
"mode": "0000644",
|
|
9695
|
-
"sha256": "
|
|
9705
|
+
"sha256": "37b1b65e9b2e4136ec74c3d22f13da1a58740de5f1a6e4ddd29dd46cc544eb1f"
|
|
9696
9706
|
},
|
|
9697
9707
|
{
|
|
9698
9708
|
"path": "src/module-loader/catalog.js",
|
|
@@ -10274,6 +10284,11 @@
|
|
|
10274
10284
|
"mode": "0000644",
|
|
10275
10285
|
"sha256": "a8fe5a479c38703217de2cb9357ac3a77b28d928feba125e97929bb31c669496"
|
|
10276
10286
|
},
|
|
10287
|
+
{
|
|
10288
|
+
"path": "tests/bug_attachment_audit.mjs",
|
|
10289
|
+
"mode": "0000644",
|
|
10290
|
+
"sha256": "995bc60d7c4d51c18c90dfe5e3e220ec884c793267f9d0403c299e7c2ecb96fa"
|
|
10291
|
+
},
|
|
10277
10292
|
{
|
|
10278
10293
|
"path": "tests/bug_attachments.mjs",
|
|
10279
10294
|
"mode": "0000644",
|
|
@@ -10427,7 +10442,7 @@
|
|
|
10427
10442
|
{
|
|
10428
10443
|
"path": "tests/cli_surface.mjs",
|
|
10429
10444
|
"mode": "0000644",
|
|
10430
|
-
"sha256": "
|
|
10445
|
+
"sha256": "1475bff8f7e51244f451401f92ac647ddf859eacecf277a8b3f9990a23b84059"
|
|
10431
10446
|
},
|
|
10432
10447
|
{
|
|
10433
10448
|
"path": "tests/cli_token_reissue.mjs",
|
|
@@ -11867,7 +11882,7 @@
|
|
|
11867
11882
|
{
|
|
11868
11883
|
"path": "tests/linkify_refs.mjs",
|
|
11869
11884
|
"mode": "0000644",
|
|
11870
|
-
"sha256": "
|
|
11885
|
+
"sha256": "d072512abdd464716a6880a528da08bc3abb06fd7c69d59d075a699c665229a8"
|
|
11871
11886
|
},
|
|
11872
11887
|
{
|
|
11873
11888
|
"path": "tests/live_channel.mjs",
|
|
@@ -12037,7 +12052,7 @@
|
|
|
12037
12052
|
{
|
|
12038
12053
|
"path": "tests/module_api_lazy.mjs",
|
|
12039
12054
|
"mode": "0000644",
|
|
12040
|
-
"sha256": "
|
|
12055
|
+
"sha256": "9183d78e4d195428afbee8c71746e12db2455f8529ac1223d690a7b8f7ded538"
|
|
12041
12056
|
},
|
|
12042
12057
|
{
|
|
12043
12058
|
"path": "tests/module_asset_subdomain.mjs",
|
|
@@ -12789,6 +12804,11 @@
|
|
|
12789
12804
|
"mode": "0000644",
|
|
12790
12805
|
"sha256": "b56a6945713e738542d612f6d43dd9bd00ce1a370b3d7b3e130f4e59a056ee15"
|
|
12791
12806
|
},
|
|
12807
|
+
{
|
|
12808
|
+
"path": "tests/routine_capture_kind.mjs",
|
|
12809
|
+
"mode": "0000644",
|
|
12810
|
+
"sha256": "0d45c19886939b8a068df438d2e03e749efb3a84258764ed0a345ce96128827d"
|
|
12811
|
+
},
|
|
12792
12812
|
{
|
|
12793
12813
|
"path": "tests/routine_schedule.mjs",
|
|
12794
12814
|
"mode": "0000644",
|
|
@@ -13187,7 +13207,7 @@
|
|
|
13187
13207
|
{
|
|
13188
13208
|
"path": "tests/skill_preflight.mjs",
|
|
13189
13209
|
"mode": "0000644",
|
|
13190
|
-
"sha256": "
|
|
13210
|
+
"sha256": "fe5f29a252d709d417e07f41e9ff2537980e5c42245821fbc966b6b2ba9d9e1e"
|
|
13191
13211
|
},
|
|
13192
13212
|
{
|
|
13193
13213
|
"path": "tests/skill_prefs.mjs",
|
|
@@ -32,10 +32,19 @@ node scripts/gds/skill-preflight.js strand-fix # exits 1 naming what is MISSIN
|
|
|
32
32
|
git log origin/main --oneline --grep="N" | head
|
|
33
33
|
gh pr list --search "N" --state all --json number,state,mergeable,url
|
|
34
34
|
```
|
|
35
|
-
2. **Pick the ONE matching case.**
|
|
35
|
+
2. **Pick the ONE matching case.** (Nothing here moves **main** by hand: landing is server-mediated (ADR 0031 §6 / ADR 0055), so the step that lands is always `ship.js`. `git fetch` always works.)
|
|
36
36
|
- **A. A main commit carries `(task N` and the post-merge `unit` + `main-audit` on main are green** (`gh run list --branch main --limit 4`): the land happened; only the proof failed. The proof is the claim's recorded tip (`claims.published_head_sha`, task 1547) being an ancestor of main — and a `no_branch_no_tip` claim has none (the work rode in on another branch, e.g. a cloud-agent batch, or a `--here` claim). **Record the tip first**: the owner (Archon) sets that column on the task's latest claim to the full 40-char SHA of the main commit — a direct DB write on the box as the DB owner, so it runs under this task's own ledger with the SQL in the ship note (`UPDATE claims SET published_head_sha = '<sha>' WHERE id = (SELECT id FROM claims WHERE task_id = N ORDER BY claimed_at DESC, id DESC LIMIT 1)`). Then the reconciler proves it within ≤5 min, or flip it yourself: `node scripts/gds/api.js POST /api/bongos/tasks/N/ship --body '{}'`. Never `no_artifact: true` here — the artifact exists, and the ledger would record a falsehood. Credits land on the flip (ADR 0120). (Before task 1003434 the manual POST refused even with a tip when the claim had no branch; the reconciler path always worked.)
|
|
37
37
|
- **B. A local branch exists with commits ahead of `origin/main` and no PR**: the ship never pushed. From that worktree: `git push -u origin <branch>` then `gh pr create --base main --head <branch> --fill`. Green `unit` → server auto-merge → the reconciler flips it within ~5 min. If it merges but does not flip, do **A**.
|
|
38
|
-
- **C. A PR exists but is `CONFLICTING`**: the branch owner
|
|
38
|
+
- **C. A PR exists but is `CONFLICTING`**: only the **branch owner** can resolve this, and **never with `--force`**. **Merge, never rebase** (task 1002788): the branch is already published and the server's publish is append-only, so rewriting its history makes every later push non-fast-forward and the task un-landable while the claim gate keeps the owner's queue shut.
|
|
39
|
+
- **If you ARE the branch owner** (the usual case — it is your own strand), from that worktree:
|
|
40
|
+
```bash
|
|
41
|
+
git fetch -q origin
|
|
42
|
+
git merge origin/main # resolve the conflicts, commit
|
|
43
|
+
node scripts/gds/ship.js <task-id>
|
|
44
|
+
```
|
|
45
|
+
**`ship.js` is the landing step, not `git push`.** Landing is **server-mediated** (ADR 0031 §6 / ADR 0055): `/builder-ship` uploads a bundle and the SERVER moves main. A push from your machine does not land the task even where it succeeds, and on the documented default machine a push to main has no credential at all. So "resolve and push" is not an exit — it leaves the task at `confirmed`, unpaid, with nothing named to do next. That is what stranded task 1003529.
|
|
46
|
+
**The resume does NOT re-grade.** `ship.js` sees `confirmed` + "you last held the claim", prints `resuming the merge + deploy (no re-claim needed)`, and lands it. The standing caution about re-running `ship.js` on graded work is about `--regrade`, which pays for a second panel; a bare resume from `confirmed` does not. Verified end to end on task 1003529 → PR #745 merged as `e827063f`.
|
|
47
|
+
- **If it is SOMEONE ELSE'S strand**: report the PR URL and stop. That exit is still correct — only the owner can resolve a real content conflict, and this skill does not resolve conflicts for them.
|
|
39
48
|
- **D. Nothing anywhere** — no branch, no PR, no commit: a codeless confirm. Ask what the task delivered. Work done outside this repo (on the box's instance checkout, a DB-only change captured in the ship note) is legitimately artifact-free → **A** with the reason in the ship body: `--body '{"note":"codeless: <where the work lives>"}'`. If the work was never done, release it instead so it returns to `ready`: `node scripts/gds/release.js N --reason "stranded codeless confirm; nothing delivered"`.
|
|
40
49
|
3. **Verify**: `node scripts/gds/api.js GET /api/bongos/tasks/N` → `"status": "shipped"` (or `ready` after a release). Re-run `strand-watch.js --line` — the task must be gone from it. If the claim gate was the symptom, `claim.js` now succeeds.
|
|
41
50
|
|
package/.gitattributes
CHANGED
|
@@ -28,6 +28,15 @@
|
|
|
28
28
|
*.py text eol=lf
|
|
29
29
|
*.json text eol=lf
|
|
30
30
|
|
|
31
|
+
# SQL: pinned for the same reason the .md above is, not for tidiness (task 1003945).
|
|
32
|
+
# Unpinned, *.sql fell through `* text=auto`, so a Windows clone with the common
|
|
33
|
+
# core.autocrlf=true got CRLF in the worktree for all 232 of them while the repo
|
|
34
|
+
# content stayed LF. Anything that reads a migration AS TEXT — a column-scanning
|
|
35
|
+
# helper, a doc generator, a checksum — then trips over a \r that CI on Linux never
|
|
36
|
+
# sees. That is how task 1003759's bug sat in the local failure baseline for weeks,
|
|
37
|
+
# read as environmental noise.
|
|
38
|
+
*.sql text eol=lf
|
|
39
|
+
|
|
31
40
|
# Future Windows-only wrappers (none today) must be CRLF — cmd.exe/PowerShell
|
|
32
41
|
# expect it. Declared up front so a later setup-builder.cmd lands correct.
|
|
33
42
|
*.bat text eol=crlf
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# ADR 0298 — The grader gets a reader lens: advisory, scoped to copy-bearing diffs, and it waits on a written voice
|
|
2
|
+
|
|
3
|
+
**Status:** accepted · **Date:** 2026-09-17 · **Task:** [task 1003937](https://cloudbongos.com/builders#/task/1003937) (goal 1000095 — Working area 6, the artist experience)
|
|
4
|
+
|
|
5
|
+
**Related:** [ADR 0241](0241-the-artist-gate-is-a-per-project-deploy-gate-that-reads-a-state.md) (the artist holds a deploy, never a ship) · [ADR 0242](0242-a-cascade-is-a-declaration-table-on-the-event-that-already-exists.md) (a cascade is a row, not a runner edit) · [ADR 0024](0024-multi-agent-system-architecture.md) (the panel) · [ADR 0233](0233-a-copy-proposal-is-a-task-carrying-a-patch.md) (there is no live CMS)
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Every artist review the cascade files opens with the same instruction:
|
|
10
|
+
|
|
11
|
+
> STEP 1 — REVIEW THE GRADER, not only the work. Read how the grader judged task N's copy
|
|
12
|
+
> and visuals, and coach it: where was it right, and where did it miss what an artist would
|
|
13
|
+
> catch? … this step is the one that compounds.
|
|
14
|
+
|
|
15
|
+
It cannot be followed, and [task 1003937](https://cloudbongos.com/builders#/task/1003937)
|
|
16
|
+
established why across four reviews worked on 2026-09-15 — 1003621 (of
|
|
17
|
+
[task 1003583](https://cloudbongos.com/builders#/task/1003583)), 1003831 (of
|
|
18
|
+
[task 1003761](https://cloudbongos.com/builders#/task/1003761)), 1003833 (of
|
|
19
|
+
[task 1003828](https://cloudbongos.com/builders#/task/1003828)) and 1003836 (of
|
|
20
|
+
[task 1003829](https://cloudbongos.com/builders#/task/1003829)). The grade itself reads back
|
|
21
|
+
fine; `GET /tasks/:id?include=grade` returns scores, verdict, rubric and per-worker notes. The
|
|
22
|
+
problem is what comes back. `modules/grading/grader-rubric.json` v4.0 runs four lenses:
|
|
23
|
+
|
|
24
|
+
| worker | judges |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `narc` | semantic completion — description vs diff |
|
|
27
|
+
| `quality` | code quality (idiom, dead code, tests) + economy |
|
|
28
|
+
| `hacker` | security red-team |
|
|
29
|
+
| `efficiency` | runtime cost, hot paths |
|
|
30
|
+
|
|
31
|
+
None of them reads a sentence for whether it is true, clear, in the project's voice, or worth
|
|
32
|
+
a person's attention. There is no axis an artist can coach. All four graded tasks passed, and
|
|
33
|
+
all four deserved to pass on the axes that were measured — the gate is not too lenient, it is
|
|
34
|
+
not pointed at this.
|
|
35
|
+
|
|
36
|
+
**One partial mechanism already exists, and its limit is the shape of the gap.**
|
|
37
|
+
`modules/grading/grader-prompt.js` injects the craft's written responsibility into the panel
|
|
38
|
+
prompt — *"No slop in the appearance and text of the project; the story, emotion and ideology
|
|
39
|
+
of the project are communicated effectively; project purpose and gravitas are upheld"* — but
|
|
40
|
+
only when `task.discipline === 'artist'`. All four graded tasks were engineer ships. So the
|
|
41
|
+
unjudged case is exactly **copy shipped by someone whose craft is not the copy**, which is
|
|
42
|
+
most of it.
|
|
43
|
+
|
|
44
|
+
## Decision
|
|
45
|
+
|
|
46
|
+
A fifth panel worker — the **reader** — scoring a copy/voice axis. Three things were open;
|
|
47
|
+
the owner settled all three on 2026-09-17.
|
|
48
|
+
|
|
49
|
+
### 1. It runs on copy-bearing diffs only, not on every ship
|
|
50
|
+
|
|
51
|
+
The panel already runs four subagents per ship. A fifth on every ship is a standing spend
|
|
52
|
+
increase on the large majority of ships that change no visible string.
|
|
53
|
+
|
|
54
|
+
The predicate is not new work: `hasCopyOrVisualWork(task)` in `modules/lifecycle/cascade.js`
|
|
55
|
+
already decides, for the artist-review cascade, whether a shipped task put copy or visuals in
|
|
56
|
+
front of a person — `source === 'copy-proposal'`, `discipline === 'artist'`, or a
|
|
57
|
+
`module_key` in the explicit `COPY_VISUAL_MODULES` set. **The reader lens keys off the same
|
|
58
|
+
predicate.** One definition of "this ship is copy-bearing", used by both the after-the-fact
|
|
59
|
+
review and the at-ship lens, so the two can never disagree about what they cover. Widening
|
|
60
|
+
that set widens both at once, deliberately.
|
|
61
|
+
|
|
62
|
+
Where the predicate says no, the panel is exactly what it is today and costs exactly what it
|
|
63
|
+
costs today.
|
|
64
|
+
|
|
65
|
+
### 2. It is advisory — findings, never a verdict
|
|
66
|
+
|
|
67
|
+
The reader records `issues[]` and a `copy_score`. It **cannot** verdict `fail`, and it cannot
|
|
68
|
+
drag the panel to `concerns`.
|
|
69
|
+
|
|
70
|
+
This is not caution, it is [ADR 0241](0241-the-artist-gate-is-a-per-project-deploy-gate-that-reads-a-state.md)
|
|
71
|
+
applied as written. That ADR settled that an artist's judgement may hold a **deploy**, decided
|
|
72
|
+
per project, and never a **builder's ship** — after ADR 0162 retired review gates for stranding
|
|
73
|
+
graded-PASS work. A reader worker that could verdict `fail` would put an artist-shaped
|
|
74
|
+
judgement back on the ship path, which is the precise thing 0241 removed. Gating it would
|
|
75
|
+
require amending 0241 first, and nothing here justifies that.
|
|
76
|
+
|
|
77
|
+
Advisory is also what makes STEP 1 answerable: the artist's review reads the reader's findings
|
|
78
|
+
and coaches *them*, which is a real conversation with a real counterpart, rather than coaching
|
|
79
|
+
four lenses that were never looking.
|
|
80
|
+
|
|
81
|
+
### 3. It waits on a written voice, and does not ship before one exists
|
|
82
|
+
|
|
83
|
+
A copy lens needs a standard to judge against or it is a second model's taste wearing a
|
|
84
|
+
score. The instance's voice is, today, almost entirely unwritten:
|
|
85
|
+
|
|
86
|
+
| source | what it actually carries |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `DESIGN.md` | one hard copy rule — no em/en dash in visible copy, no emoji |
|
|
89
|
+
| `config/branding.neutral.json` | a tagline and a handful of fixed strings |
|
|
90
|
+
| `docs/copy-registry.json` | which 1,860 strings exist — not what good is |
|
|
91
|
+
| `src/role-responsibilities.js` | one sentence, and it only reaches artist-discipline ships |
|
|
92
|
+
|
|
93
|
+
So the build order is fixed: **voice guide first, reader second.** The reader worker is not
|
|
94
|
+
started until the guide is written, and the guide is the artist's own work, not an agent's
|
|
95
|
+
draft of it. A lens built against the four rows above would be recording opinions and calling
|
|
96
|
+
them findings, and that residue would be worse than the silence it replaced — a score nobody
|
|
97
|
+
can appeal to a standard is harder to remove than a missing score.
|
|
98
|
+
|
|
99
|
+
### 4. Until the reader exists, the cascade brief says something an artist can do
|
|
100
|
+
|
|
101
|
+
STEP 1 stays — the instinct behind it is right and this ADR is the proof that it compounds —
|
|
102
|
+
but it stops asking for coaching that has no recipient. In the interim it asks the artist to
|
|
103
|
+
record *where the grade missed what an artist would catch*, naming that the record is the
|
|
104
|
+
evidence this lens is being built from. That is followable today, it produces exactly the
|
|
105
|
+
corpus the voice guide needs, and it converts back into true coaching the moment the reader
|
|
106
|
+
lands.
|
|
107
|
+
|
|
108
|
+
## Consequences
|
|
109
|
+
|
|
110
|
+
- `modules/lifecycle/cascade.js` — STEP 1 rewritten to an instruction that can be carried out,
|
|
111
|
+
pointing at this ADR. No change to the predicate, the trigger, the anti-recursion guard, or
|
|
112
|
+
the fact that the review blocks nothing.
|
|
113
|
+
- Two follow-up tasks are filed at `backlog`, in order, with the dependency edge wired:
|
|
114
|
+
[task 1003976](https://cloudbongos.com/builders#/task/1003976) — the **voice guide** (artist) —
|
|
115
|
+
then [task 1003977](https://cloudbongos.com/builders#/task/1003977) — the **reader worker**
|
|
116
|
+
(engineer, `depends_on` 1003976, so it cannot reach `ready` until the guide ships).
|
|
117
|
+
Neither is started by this ADR.
|
|
118
|
+
- The rubric, the active panel, and every existing worker are untouched. No ship's cost or
|
|
119
|
+
verdict changes as of this decision.
|
|
120
|
+
- If the voice guide is never written, the reader is never built, and STEP 1 is still honest —
|
|
121
|
+
which is the outcome this ADR is careful to leave standing rather than forcing.
|
package/docs/adr/README.md
CHANGED
|
@@ -389,3 +389,4 @@ This keeps the decision history honest and traceable.
|
|
|
389
389
|
| 0295 | [**The registry is the witness the publish lane cannot be** ([task 1003916](https://cloudbongos.com/builders#/task/1003916), goal 1000090). On 2026-09-13 eight publish runs failed over three hours and NOTHING reported it; it was found by a person idly reading the npm version list. Task 1003914 fixed the lossiness and left the silence, which is structural: a failed run is one red row nobody watches, `main` self-heals on the next merge and **erases the evidence**, and the lane's own verdict step writes to a job summary no one reads. Same shape as the nightly upgrade sweep that died reporting `failed=0`. **Decision: a separate reader compares what the lane ANNOUNCED (release commits) against what the registry HOLDS**; the difference is a phantom. The publisher infers from git because OIDC trusted publishing carries no read credential — **that never bound a reader**, and since task 1003872 the package is public, so an anonymous read needs none. Keeping witness and publisher separate IS the point. Daily not hourly (billable minutes, against a failure measured in hours), cadence pinned to the cron by `routine-schedule.js`; deliberately NOT on the session-start path (a third-party call behind a hook timeout). Healthy days are SILENT in Discord — findings to stdout, the all-clear to stderr — because a channel of reassurances stops being read. Two false-alarm sources, each guarded: a closed incident is permanent history (a 3-day window, by TIME not commit count, which a release burst would outrun), and **npm acknowledges a publish before its listing shows it** — found by RUNNING the check, which flagged 1.19.794 fifteen minutes after a publish whose log ended `published @bongos/core@1.19.794` and whose per-version endpoint answered 200; hence a 30-minute grace period plus per-version confirmation, with the listing only ever nominating suspects. A failed read throws rather than reading as "nothing missing". Proven by replaying the REAL outage from git + the live registry, not a synthetic failure. Does NOT detect a lane that stopped being triggered.](0295-the-registry-is-the-witness-the-lane-cannot-be.md) | release / observability |
|
|
390
390
|
| 0296 | [**A role speciality is data over three existing surfaces, not a fourth mechanism** ([task 1003855](https://cloudbongos.com/builders#/task/1003855), goal 1000095). A craft says HOW you work; nothing said what you work ON. A speciality is a named bundle of knowledge, skills and an interaction contract sitting under a craft — an ideator with an Economics speciality. **Decision: it is DATA, not an ADR 0083 code module** — a subject-matter expert authors one, and requiring them to ship a package puts the mechanism out of reach of exactly the person it exists for. It layers over three surfaces that already exist (discipline-modes.json, the `behavior_prefs.interaction` contract path, the manifest contribution seam); only the knowledge bundle is new. The contract **rides the existing GET /me -> Conductor injection path rather than adding a second one**, and is CRAFT-SCOPED: the hook injects only the contract matching the claim in hand (which needed `t.discipline` on the active-claims read), and with no claim there is no craft and **nothing is injected** — silence beats guessing which expertise applies. Training is two-fold per the owner's 2026-09-17 ruling: documents they supplied, plus learnings captured from sessions worked in it (pointing at existing `learnings` rows, never a second corpus). The corpus is **inventoried, never inlined** — a document may run to 100k chars — so `speciality_documents` joins the recall ingester, because otherwise the contract's "search it with /recall" is a lie whose failure is silent. **`sellable` is recorded with no storefront behind it**: ADR 0107 defers the marketplace and explicitly rejected building one, so the flag captures the owner's decision when they make it and grants nothing — a deliberate partial implementation, recorded rather than silently made. Index scoping errs toward UNDER-sharing: only a public speciality's documents are corpus-wide, because `project` names a set the index cannot express.](0296-a-role-speciality-is-data-over-three-existing-surfaces.md) | roles / modules |
|
|
391
391
|
| 0297 | [**A task’s creator may retire it, and that is the ownership axis’s first route** ([task 1002443](https://cloudbongos.com/builders#/task/1002443), goal 1000095 — owner request 2026-07-25: *"the creator of that task should be able to abandon that task directly inside of the UI"*). Attribution itself shipped earlier and elsewhere (`core_227`, [task 1003278](https://cloudbongos.com/builders#/task/1003278)); what had not moved is that **retiring a task was a rank power and nothing else** — `task.abandon` floors at `metic` (ADR 0152), so a sub-Metic builder could file a task and then could not withdraw their own proposal. The model already had the vocabulary and had never used it: ADR 0151 §3 defines an **ownership axis** where a `scope:'own'` key authorizes nothing until a handler supplies the row's owner, R100 made `requirePermission` refuse such a key outright so `.own` can never degrade into `.any`, and it recorded that **zero call sites passed one**. This is that axis’s first real consumer. **Decision: two authorities, only one rank-shaped** — `task.abandon` (metic, any task) OR new `task.abandon.own` (xenos, a task you created, while it is still backlog/ready/blocked). Route becomes `requireBuilder` + an in-handler decision (the `POST /goals/:id/members` shape), pin `metic+archon`→`any-builder` so it now catches an accidental **tightening** as loudly as a gate removal. **The two facts resolve in two places on purpose:** the RANK fact is request-scoped and resolves before the transaction (uncached, ADR 0016); the OWNER fact is a column that can change under us, so `created_by` joins `abandonTask`’s existing `FOR UPDATE` SELECT and a new optional `authorize` callback decides under that lock. Reading `created_by` in the route would be a TOCTOU window; resolving permissions inside the transaction would hold a row lock across a second pool acquire. The decision is a PURE module (`abandon-authz.js`, the `goal-route-authz.js` precedent) so every branch is pinned DB-free and the hall hint and server verdict provably read one rule. **A creator’s window stops at the grader** — completed/confirmed stay cross-owner, because past the grader a retire is a ledger correction, not an author changing their mind; nothing is subtracted from `task.abandon`, pinned by a test. `counterpart: ['task.abandon']` is declared not derived, verb `act` not `manage` — `manage` would silently dominate every future narrower `task.*.own` key (the verb-blind escalation task 1003195 closed). **A null creator belongs to nobody and that is load-bearing:** ~1,357 pre-`core_227` tasks plus seed/restock/discord vectors are unattributed, so the owner comparison is the government resolver’s own — injected, never re-implemented, since a duplicated ownership comparison is how one quietly widens — and one slip would open every historical task to a xenos grant at once. Fail-closed throughout: a missing `government` port, a throwing resolver, and anything but an explicit `{ok:true}` all deny, and `authorize` runs BEFORE the idempotent already-abandoned return and the shipped/active-claim guards so an unauthorized caller cannot probe a row through a 200. No guard removed. Does NOT widen edit/promote/dependency (still Metic), does not touch `task.merge`, and does not backfill attribution — the control simply does not appear on tasks predating `core_227`.](0297-a-tasks-creator-may-retire-it.md) | authority / lifecycle / ownership |
|
|
392
|
+
| 0298 | [**The grader gets a reader lens: advisory, scoped to copy-bearing diffs, and it waits on a written voice** ([task 1003937](https://cloudbongos.com/builders#/task/1003937), goal 1000095 — Working area 6; owner decision 2026-09-17). Every artist review the cascade files opened with *“REVIEW THE GRADER … coach it”*, and it could not be followed: the v4 panel runs narc, quality, hacker and efficiency — completion, code quality, security, runtime cost — and not one of them reads a sentence for whether it is true, clear, in the project’s voice or worth a person’s attention. Four consecutive reviews hit the same wall and all four graded ships deservedly PASSED on the axes that were measured; the gate is not lenient, it is not pointed at this. `grader-prompt.js` already injects the craft’s written responsibility, but only when `discipline === ‘artist’`, so the unjudged case is copy shipped by someone whose craft is not the copy. **Decision: a fifth ADVISORY `reader` worker, on three settled terms.** It runs only where `modules/lifecycle/cascade.js`’s existing `hasCopyOrVisualWork` says the ship is copy-bearing — the same predicate the after-the-fact review uses, so the two can never disagree about coverage, and every other ship costs exactly what it costs today. It records `issues[]` and a `copy_score` and can never verdict `fail` or drag the panel to `concerns`, because [ADR 0241](0241-the-artist-gate-is-a-per-project-deploy-gate-that-reads-a-state.md) put the artist’s judgement on the DEPLOY and off the ship path; gating would need that ADR amended first. And it is NOT BUILT until the instance writes a voice guide — today the whole written standard is one dash rule in `DESIGN.md`, a tagline, a registry of which 1,860 strings exist, and one responsibility sentence, so a lens built now would record taste and call it a finding. Until the reader exists, STEP 1 asks the artist to WRITE DOWN what the grade could not see rather than to coach a lens that is not listening — followable today, and the corpus both the guide and the lens get written from. Nothing in the rubric, the active panel or any existing worker changes.](0298-the-reader-lens-is-advisory-and-waits-on-a-voice-guide.md) | grading / the artist’s loop / voice |
|
|
@@ -2107,6 +2107,10 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2107
2107
|
1.19.816 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2108
2108
|
landed since 1.19.815 with no explicit bump. run 35198566737. (task 1002620)
|
|
2109
2109
|
1.19.817 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2110
|
-
landed since 1.19.816 with no explicit bump. run
|
|
2110
|
+
landed since 1.19.816 with no explicit bump. run 35200401779. (task 1002620)
|
|
2111
|
+
1.19.818 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2112
|
+
landed since 1.19.817 with no explicit bump. run 35201213516. (task 1002620)
|
|
2113
|
+
1.19.819 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2114
|
+
landed since 1.19.818 with no explicit bump. run 35205615659. (task 1002620)
|
|
2111
2115
|
---------------------------------------------------------------------------
|
|
2112
2116
|
```
|
|
@@ -242,6 +242,52 @@ curl -s https://example.com/api/gds/healthz
|
|
|
242
242
|
curl -s https://example.com/api/gds/public/progress | jq '.progress[0]'
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
+
## Bug attachments — the state that is not in the database (task 1003918)
|
|
246
|
+
|
|
247
|
+
The nightly dump is not the whole of this instance. Re-hosted `#bugs` screenshots
|
|
248
|
+
live on disk under `var/bug-attachments`, outside Postgres and outside git, and they
|
|
249
|
+
are the one artefact here that **cannot be regenerated**: the Discord CDN URL each was
|
|
250
|
+
copied from is signed and expires in about a day, so our copy *is* the image.
|
|
251
|
+
|
|
252
|
+
Nothing backed them up until this task. All five this instance had ever stored were
|
|
253
|
+
lost in the 2026-07-05 droplet move (ADR 0126) — `pg_dump` carried the database and
|
|
254
|
+
nothing carried `var/` — and the loss surfaced two months later, when somebody opened
|
|
255
|
+
an old bug task and saw a broken image.
|
|
256
|
+
|
|
257
|
+
Two things changed, and one thing is still on you:
|
|
258
|
+
|
|
259
|
+
1. **The nightly job archives them.** `db-backup-nightly.sh` now writes
|
|
260
|
+
`<db>-attachments-<TS>.tar.gz` beside the dump, same timestamp and same retention,
|
|
261
|
+
so a restore drill that picks a date gets the database and the images that go with
|
|
262
|
+
it. Best-effort: a missing or empty attachments dir is the ordinary case on a fresh
|
|
263
|
+
instance and never fails a night of database backup.
|
|
264
|
+
|
|
265
|
+
2. **The default store follows the instance, not the package.** It resolves against the
|
|
266
|
+
instance root, so an instance running a vendored core no longer keeps these files
|
|
267
|
+
inside `node_modules/@bongos/core/var`, where every core reinstall would wipe them.
|
|
268
|
+
|
|
269
|
+
3. **On a box where the instance root is itself impermanent, set `BUG_ATTACHMENTS_DIR`**
|
|
270
|
+
to a persistent path (e.g. `/srv/<instance>/bug-attachments`) in the service
|
|
271
|
+
environment, and make sure that path is on the volume your backups actually cover.
|
|
272
|
+
The override wins over everything above.
|
|
273
|
+
|
|
274
|
+
### Checking
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
node scripts/gds/bug-attachment-audit.js
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Reads every bug task's description, extracts the attachment links it promises a reader,
|
|
281
|
+
and asks the disk whether it can keep them. Exits 1 when any are missing and names the
|
|
282
|
+
task each broken image sits in. `--json` for a machine, `--line` for a one-line probe
|
|
283
|
+
that never blocks a session.
|
|
284
|
+
|
|
285
|
+
A missing file cannot be re-fetched. Restore it from an attachments archive:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
tar -xzf /var/backups/gds/<db>-attachments-<TS>.tar.gz -C "$BUG_ATTACHMENTS_DIR"
|
|
289
|
+
```
|
|
290
|
+
|
|
245
291
|
## Limits — what this recipe does NOT cover
|
|
246
292
|
|
|
247
293
|
1. **Whole-droplet loss window is ~1 week, not zero.** The weekly offsite copy ([#415](https://example.com/builders#/task/415), above) closes the catastrophic-loss gap, but it runs weekly — so in the worst case (droplet dies right before the weekly run) you lose up to ~7 days of writes that made it into the local daily dumps but not yet offsite. Acceptable for the Bongos DB's shape; tighten the offsite cadence to daily if that ever stops being true.
|
|
@@ -50,8 +50,43 @@ const NAME_RE = /^bug-[a-z0-9]{1,40}-[0-9a-f]{16}\.(png|jpg|gif|webp)$/;
|
|
|
50
50
|
// Where re-hosted images live. Outside the git tree (a `var/` dir) so a deploy's
|
|
51
51
|
// `git reset --hard` / pull never wipes them; gitignored. Env override for tests
|
|
52
52
|
// / non-default layouts.
|
|
53
|
+
//
|
|
54
|
+
// RESOLVED AGAINST THE INSTANCE ROOT, not this file (task 1003918). The default
|
|
55
|
+
// used to be __dirname/../../var, which is the repo root in a single checkout and
|
|
56
|
+
// `node_modules/@bongos/core/var` on an instance running a vendored core — where
|
|
57
|
+
// every core reinstall wipes it. That is the task 1002237 shape, and these files
|
|
58
|
+
// are the one thing here that cannot be regenerated: a Discord CDN URL expires in
|
|
59
|
+
// about a day, so our copy IS the image. All five attachments this instance ever
|
|
60
|
+
// stored are already gone (the 2026-07-05 droplet move carried the database and
|
|
61
|
+
// not var/), which is what this default is meant to stop happening again.
|
|
62
|
+
//
|
|
63
|
+
// BUG_ATTACHMENTS_DIR still wins, and on a box where the instance root is itself
|
|
64
|
+
// impermanent that override is the answer — the standup recipe says so.
|
|
65
|
+
// The instance root is resolved ONCE. resolveInstanceRoot() reads an optional
|
|
66
|
+
// bongos.config.json on every call, and this runs per served attachment — a sync
|
|
67
|
+
// file read per image request, where the old code did a path.join. The root cannot
|
|
68
|
+
// move under a running process, so caching it costs nothing and keeps the serve
|
|
69
|
+
// path as cheap as it was. The env override is still read every call, so a test
|
|
70
|
+
// (or an operator) can still change it without restarting.
|
|
71
|
+
let instanceRootCache;
|
|
72
|
+
function attachmentsRoot() {
|
|
73
|
+
if (instanceRootCache === undefined) {
|
|
74
|
+
try {
|
|
75
|
+
// Through the published doorway (ADR 0083), like every other core read here.
|
|
76
|
+
instanceRootCache = require('../../src/module-api').resolveInstanceRoot();
|
|
77
|
+
} catch (e) {
|
|
78
|
+
// Never let a storage-path lookup take down the bot: fall back to the historical
|
|
79
|
+
// location, which is correct in a single checkout and is where any existing
|
|
80
|
+
// files already are.
|
|
81
|
+
instanceRootCache = path.join(__dirname, '..', '..');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return instanceRootCache;
|
|
85
|
+
}
|
|
86
|
+
|
|
53
87
|
function attachmentsDir() {
|
|
54
|
-
|
|
88
|
+
if (process.env.BUG_ATTACHMENTS_DIR) return process.env.BUG_ATTACHMENTS_DIR;
|
|
89
|
+
return path.join(attachmentsRoot(), 'var', 'bug-attachments');
|
|
55
90
|
}
|
|
56
91
|
|
|
57
92
|
// PURE — is this a well-formed, bot-generated attachment name?
|
|
@@ -131,18 +131,31 @@ function hasCopyOrVisualWork(task) {
|
|
|
131
131
|
return false;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
// The two-step artist review body. STEP 1
|
|
135
|
-
//
|
|
136
|
-
//
|
|
134
|
+
// The two-step artist review body. STEP 1 records where the grade was blind;
|
|
135
|
+
// STEP 2 remakes the copy/visuals with both routes explicitly offered — R04's
|
|
136
|
+
// brief, verbatim in intent.
|
|
137
|
+
//
|
|
138
|
+
// STEP 1 used to say "coach the grader", and for as long as that was the wording
|
|
139
|
+
// it asked for something no artist could do (task 1003937, ADR 0298). The panel's
|
|
140
|
+
// four lenses — narc, quality, hacker, efficiency — read completion, code quality,
|
|
141
|
+
// security and runtime cost; not one of them reads a sentence, so there was no
|
|
142
|
+
// counterpart to coach and four consecutive reviews found the same wall. The
|
|
143
|
+
// ADR's decision is a fifth ADVISORY 'reader' lens, scoped to copy-bearing diffs
|
|
144
|
+
// by this file's own hasCopyOrVisualWork, built only after the instance writes a
|
|
145
|
+
// voice guide to judge against. Until it exists the step asks for the RECORD
|
|
146
|
+
// instead of the coaching: followable today, and it is the corpus both the guide
|
|
147
|
+
// and the lens will be written from. When the reader lands, this reverts to
|
|
148
|
+
// coaching a lens that is actually listening — and the wording is the signal for
|
|
149
|
+
// which of those two worlds we are in, so do not restore the old sentence early.
|
|
137
150
|
function artistReviewBody(task) {
|
|
138
151
|
return [
|
|
139
152
|
`This is an ARTIST REVIEW, generated automatically because task ${task.id} put copy or visuals in front of a person. It is a REQUEST, not a gate — it blocks nothing that shipped, and triage may dismiss it.`,
|
|
140
153
|
'',
|
|
141
|
-
`STEP 1 —
|
|
154
|
+
`STEP 1 — READ THE GRADE, AND WRITE DOWN WHAT IT COULD NOT SEE. Pull how the grader judged task ${task.id} (GET /tasks/${task.id}?include=grade), then record what an artist would have caught and no lens did: slop, an off-voice line, a claim that does not land, a visual that reads as generated. Do not expect to find a copy verdict to argue with — the panel's four lenses read completion, code quality, security and runtime cost, and none of them reads a sentence (ADR 0298). Your record IS the compounding part: it is the evidence the advisory 'reader' lens is being built from, and the corpus the voice guide it waits on will be written against.`,
|
|
142
155
|
'',
|
|
143
156
|
'STEP 2 — REMAKE OR TWEAK the copy/visuals if they need it. Your choice of route, both offered on purpose: OUTSIDE the AI chat (edit the surface directly and land it through the copy desk or the normal pipeline), or WITH it (draft in chat, then land). Either way it lands as a claimed, graded, shipped task like everything else — there is no live CMS (goal 1000074\'s hard rule).',
|
|
144
157
|
'',
|
|
145
|
-
'If the copy and visuals are fine and
|
|
158
|
+
'If the copy and visuals are fine and there was nothing the grade missed, DISMISS this in triage. That is a real, expected outcome — not a failure.',
|
|
146
159
|
].join('\n');
|
|
147
160
|
}
|
|
148
161
|
|
|
@@ -212,7 +212,17 @@ await test('a copy/visual ship generates the two-step artist review', () => {
|
|
|
212
212
|
assert.equal(spec.status, 'backlog', 'a request that triage may dismiss — not claimable, not nagging');
|
|
213
213
|
assert.match(spec.title, /7001/);
|
|
214
214
|
// Both steps, and both routes of step 2, are in the body.
|
|
215
|
-
|
|
215
|
+
//
|
|
216
|
+
// STEP 1's label tracks ADR 0298 (task 1003937). It used to read
|
|
217
|
+
// "REVIEW THE GRADER … coach it", which asked for something no artist could
|
|
218
|
+
// do: the panel's four lenses read completion, code quality, security and
|
|
219
|
+
// runtime cost, so there was no copy verdict to coach. Until the advisory
|
|
220
|
+
// reader worker ships (task 1003977, itself gated on the voice guide in
|
|
221
|
+
// task 1003976) the step asks for the RECORD instead — hence the pin on the
|
|
222
|
+
// new label AND on the ADR reference, which is what stops a well-meaning
|
|
223
|
+
// copy-edit from restoring the unfollowable version without reading why.
|
|
224
|
+
assert.match(spec.description, /STEP 1 — READ THE GRADE, AND WRITE DOWN WHAT IT COULD NOT SEE/);
|
|
225
|
+
assert.match(spec.description, /ADR 0298/, 'the step names the decision that shaped it');
|
|
216
226
|
assert.match(spec.description, /STEP 2 — REMAKE OR TWEAK/);
|
|
217
227
|
assert.match(spec.description, /OUTSIDE the AI chat/);
|
|
218
228
|
assert.match(spec.description, /WITH it/);
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.819",
|
|
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.819",
|
|
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.819",
|
|
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",
|