@bongos/core 1.19.1066 → 1.19.1068
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 +50 -30
- package/docs/adr/0341-the-page-is-the-unit-of-tweak-mode.md +2 -0
- package/docs/copy-inventory.md +64 -43
- package/docs/copy-registry.json +288 -79
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +10 -2
- package/docs/page-readings.json +74 -50
- package/modules/hall-ui/public/studio-lib.js +207 -0
- package/modules/hall-ui/public/studio.css +559 -170
- package/modules/hall-ui/public/studio.html +142 -48
- package/modules/hall-ui/public/studio.js +452 -40
- package/modules/hall-ui/public/studio.states.json +164 -4
- package/modules/hall-ui/records/studio.md +47 -0
- package/modules/lifecycle/cascade.js +48 -96
- package/modules/lifecycle/routes/lifecycle.js +9 -9
- package/modules/lifecycle/tests/cascade.mjs +106 -160
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/hall-preview/server.js +5 -0
- package/src/module-api.js +1 -1
- package/tests/grader_reader_lens.mjs +16 -24
- package/tests/hall_audit.mjs +4 -0
- package/tests/hall_studio_home.mjs +429 -0
- package/tests/hall_studio_world.mjs +7 -1
- package/tests/removal_verdict.mjs +5 -2
- package/tests/tweak_replaces_artist_review.mjs +170 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "The
|
|
2
|
+
"_": "The studio home (task 1003971 / BV2.TW16), built 1:1 on docs/design/mocks/tweak-mode/Main.dc.html: the night room, the greeting and its sub-line (editable in place as a tweak of builders:studio), the tally, Resume, and three quick actions opening glass panels (the Tweak/CopyWrite Flow, the Approval queue, Artist Review Status). Behind a quiet door, the older reviews (R06 of goal 1000074, task 1003117; retired in TW14) still answer the reviews filed before Tweak Mode. Against the kit's own stub the page renders its unread states (no copy-desk feeds). TO SEE IT POPULATED, point the kit at the hall-preview harness: `node scripts/hall-preview/server.js --port 4636 --fixture-me` then `render.js --base-in http://builders.localhost:4636 --base-out http://builders.localhost:4636`. The harness answers the tally, the pages and the next page from copy-desk__tally.json, copy-desk__pages.json and copy-desk__next.json (built by the real composers), Resume's counter from copy-desk__pages__draft.json, the studio's own lines from copy-desk__pages__draft-studio.json, the claim and the draft save from its CANNED WRITES, and the older reviews from tasks.json (whose first row IS a cascade artist review).",
|
|
3
3
|
"page": "/builders/studio",
|
|
4
4
|
"surface": "hall-ui",
|
|
5
5
|
"stub": {
|
|
@@ -25,8 +25,9 @@
|
|
|
25
25
|
"load failed"
|
|
26
26
|
],
|
|
27
27
|
"states": {
|
|
28
|
-
"
|
|
28
|
+
"home": {
|
|
29
29
|
"auth": true,
|
|
30
|
+
"_": "The home: Resume, the three quick actions, the tally top right.",
|
|
30
31
|
"actions": [
|
|
31
32
|
[
|
|
32
33
|
"wait",
|
|
@@ -35,13 +36,172 @@
|
|
|
35
36
|
],
|
|
36
37
|
"expect": {
|
|
37
38
|
"visible": [
|
|
38
|
-
"#studio-
|
|
39
|
+
"#studio-greet",
|
|
40
|
+
"#st-flow",
|
|
41
|
+
"#studio-approvals",
|
|
42
|
+
"#st-status"
|
|
39
43
|
]
|
|
40
44
|
}
|
|
41
45
|
},
|
|
42
46
|
"out": {
|
|
43
47
|
"auth": false
|
|
44
48
|
},
|
|
49
|
+
"flow": {
|
|
50
|
+
"auth": true,
|
|
51
|
+
"_": "The Tweak/CopyWrite Flow: the recommended page, take it, pick another.",
|
|
52
|
+
"actions": [
|
|
53
|
+
[
|
|
54
|
+
"wait",
|
|
55
|
+
700
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
"click",
|
|
59
|
+
"#st-flow"
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
"wait",
|
|
63
|
+
300
|
|
64
|
+
]
|
|
65
|
+
],
|
|
66
|
+
"expect": {
|
|
67
|
+
"visible": [
|
|
68
|
+
".st-dialog",
|
|
69
|
+
"[data-take]"
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"flow-pick": {
|
|
74
|
+
"auth": true,
|
|
75
|
+
"_": "Pick another: the other pages as chips.",
|
|
76
|
+
"actions": [
|
|
77
|
+
[
|
|
78
|
+
"wait",
|
|
79
|
+
700
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
"click",
|
|
83
|
+
"#st-flow"
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
"wait",
|
|
87
|
+
300
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
"click",
|
|
91
|
+
"[data-pick]"
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
"wait",
|
|
95
|
+
200
|
|
96
|
+
]
|
|
97
|
+
],
|
|
98
|
+
"expect": {
|
|
99
|
+
"visible": [
|
|
100
|
+
"#st-picks"
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"status": {
|
|
105
|
+
"auth": true,
|
|
106
|
+
"_": "Artist Review Status: three surface lines, N of M tweaked.",
|
|
107
|
+
"actions": [
|
|
108
|
+
[
|
|
109
|
+
"wait",
|
|
110
|
+
700
|
|
111
|
+
],
|
|
112
|
+
[
|
|
113
|
+
"click",
|
|
114
|
+
"#st-status"
|
|
115
|
+
],
|
|
116
|
+
[
|
|
117
|
+
"wait",
|
|
118
|
+
300
|
|
119
|
+
]
|
|
120
|
+
],
|
|
121
|
+
"expect": {
|
|
122
|
+
"visible": [
|
|
123
|
+
".st-surfaces"
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"words-offer": {
|
|
128
|
+
"auth": true,
|
|
129
|
+
"_": "A click on the studio's own words offers to take the studio page.",
|
|
130
|
+
"actions": [
|
|
131
|
+
[
|
|
132
|
+
"wait",
|
|
133
|
+
700
|
|
134
|
+
],
|
|
135
|
+
[
|
|
136
|
+
"click",
|
|
137
|
+
"#studio-greet"
|
|
138
|
+
],
|
|
139
|
+
[
|
|
140
|
+
"wait",
|
|
141
|
+
500
|
|
142
|
+
]
|
|
143
|
+
],
|
|
144
|
+
"expect": {
|
|
145
|
+
"visible": [
|
|
146
|
+
"#st-offer",
|
|
147
|
+
"#st-offer-take"
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"words-edit": {
|
|
152
|
+
"auth": true,
|
|
153
|
+
"_": "Taken: the lines the studio's reading lists are typed over in place, saved to its draft.",
|
|
154
|
+
"actions": [
|
|
155
|
+
[
|
|
156
|
+
"wait",
|
|
157
|
+
700
|
|
158
|
+
],
|
|
159
|
+
[
|
|
160
|
+
"click",
|
|
161
|
+
"#studio-greet"
|
|
162
|
+
],
|
|
163
|
+
[
|
|
164
|
+
"wait",
|
|
165
|
+
500
|
|
166
|
+
],
|
|
167
|
+
[
|
|
168
|
+
"click",
|
|
169
|
+
"#st-offer-take"
|
|
170
|
+
],
|
|
171
|
+
[
|
|
172
|
+
"wait",
|
|
173
|
+
700
|
|
174
|
+
]
|
|
175
|
+
],
|
|
176
|
+
"expect": {
|
|
177
|
+
"visible": [
|
|
178
|
+
"#st-editing"
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"reviews": {
|
|
183
|
+
"auth": true,
|
|
184
|
+
"_": "The quiet door: the reviews filed before Tweak Mode, one at a time.",
|
|
185
|
+
"actions": [
|
|
186
|
+
[
|
|
187
|
+
"wait",
|
|
188
|
+
700
|
|
189
|
+
],
|
|
190
|
+
[
|
|
191
|
+
"click",
|
|
192
|
+
"#st-older"
|
|
193
|
+
],
|
|
194
|
+
[
|
|
195
|
+
"wait",
|
|
196
|
+
900
|
|
197
|
+
]
|
|
198
|
+
],
|
|
199
|
+
"expect": {
|
|
200
|
+
"visible": [
|
|
201
|
+
"#studio-focus"
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
},
|
|
45
205
|
"full": {
|
|
46
206
|
"auth": true,
|
|
47
207
|
"_": "Full screen (task 1004327): the shell switch pressed — the room fills the window, one button brings the chrome back.",
|
|
@@ -61,7 +221,7 @@
|
|
|
61
221
|
],
|
|
62
222
|
"expect": {
|
|
63
223
|
"visible": [
|
|
64
|
-
"#studio-
|
|
224
|
+
"#studio-greet",
|
|
65
225
|
"#fullscreen-toggle"
|
|
66
226
|
],
|
|
67
227
|
"hidden": [
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# The studio — /studio, the lofi home with Tweak Mode as its four doors (task 1003971)
|
|
2
|
+
|
|
3
|
+
**BV2.TW16 (goal 1000095; [ADR 0341](../../../docs/adr/0341-the-page-is-the-unit-of-tweak-mode.md) D2, D5, D8, D11).** Built 1:1 on the owner-approved master design [`docs/design/mocks/tweak-mode/Main.dc.html`](../../../docs/design/mocks/tweak-mode/README.md) ("I like the lofi design for now"): the night room (the window with its moon, stars and city lights, the floor, the lamp and its glow, the mug and the plant), the eyebrow and the tally pill, "Good evening." and its sub-line, "click any words here to rewrite them", Resume, and three glass quick actions that open panels in place.
|
|
4
|
+
|
|
5
|
+
Files: `studio.html` (the markup), `studio.css` (the artboard's inline styles, one rule per element, then the older reviews' garden in the hall's tokens), `studio-lib.js` (the pure rules, `window.OTBStudioLib`, node-require-safe) and `studio.js` (the reads, the panels, the words, and the garden). It also loads `tweak-editor-lib.js` for the page names and the draft body, and TW12's Approval queue (`approval-queue*.{js,css}`, `records/approval-queue.md`), unchanged.
|
|
6
|
+
|
|
7
|
+
- **The four reads.** `GET /copy-desk/tally` (the pill: credits, pages tweaked, this week), `GET /copy-desk/next` (the Flow's recommendation and "pick another", and `resume`, the held page), `GET /copy-desk/pages` (Artist Review Status's three surface lines and the whole count), and, when a page is held, `GET /copy-desk/pages/:id/draft` for Resume's "12 of 38 lines". Signed out, none of them is asked.
|
|
8
|
+
- **Resume** links to `/builders/tweak-editor?page=<held page>` (TW10). With nothing held it is not drawn, and the page's own sub-line stands.
|
|
9
|
+
- **The Flow** shows the recommendation (its `why_line`); "pick another" opens the others as chips and a chip swaps the page in place; "take it" is the claim route on THAT page (ADR 0341 D5), then the editor. A refusal stays in the panel in words.
|
|
10
|
+
- **The Approval queue** is TW12's component: `OTBApprovalQueue.mount({ api, apiBase, writes, onCount })`, opened from the quick action, with `onCount` feeding its detail line.
|
|
11
|
+
- **Artist Review Status** is three meters (landing, builders hall, status, "N of M tweaked"), each against its own surface's page count, so studio.js sits in `tests/hall_audit.mjs` `METERS`. The landing line names the instance's own address, stamped by the server from the branding pack (`<main data-apex="{{publicOrigin}}">`).
|
|
12
|
+
|
|
13
|
+
## The studio's own words are a tweak of the studio page
|
|
14
|
+
|
|
15
|
+
The greeting and its sub-line are the studio page's own lines (builders:studio), so rewriting them is a round of that page (ADR 0341 D2), never a live edit. The smallest honest flow:
|
|
16
|
+
|
|
17
|
+
1. A click on the words (or on the hint, which is a button for the keyboard) reads `GET /copy-desk/pages/builders:studio/draft`. If the artist already holds the studio page, the words open for editing at once.
|
|
18
|
+
2. Otherwise a small glass offer says the words are the studio page itself, with "take the studio page" and "not now". Nothing is claimed until the artist says so.
|
|
19
|
+
3. Taken (the claim route), each word on the page is matched to its line in the studio's reading (`matchLine`: a `{…}` hole matches any run of words, the most specific line wins, a line mostly of holes never matches, shell text never), and the matched elements become `contenteditable`, showing the reading's text, holes and all, as the editor does.
|
|
20
|
+
4. Typing autosaves the WHOLE draft of the studio page (the lines typed here and any the editor saved) through `PUT /copy-desk/pages/builders:studio/draft`, debounced and on blur. A strip under the words says "The studio page is yours · N of M rewritten · saved · submit it in the editor". Submit is the editor's: one submit path, not two.
|
|
21
|
+
|
|
22
|
+
The page keeps its words until the round is submitted, applied, approved and shipped like any other. A word the reading does not list (a sentence newer than the last reading) keeps its words and loses the cue.
|
|
23
|
+
|
|
24
|
+
**Why the sub-line's sentence is in studio.js, not studio-lib.js.** The copy registry places a line at its `textContent` assignment. Built inside the lib it read as unplaced, and the reader's pattern tier then matched it to the generic `{…} of {…}` row (sky-panel.js). Written where studio.js assigns it, it is placed on studio.js, which is the page's own file and wins.
|
|
25
|
+
|
|
26
|
+
## The older reviews stay one quiet door away
|
|
27
|
+
|
|
28
|
+
The garden this page used to be (R06 of goal 1000074; retired in TW14) still answers the reviews filed before Tweak Mode, because they may hold a deploy under the strict artist gate. It sits behind a quiet line under the actions ("1 review from before Tweak Mode still waits"), shown only while a review waits, and opens in its own dialog in the hall's tokens with the focus card, the stones and the two links, unchanged. Its verdict writes are unchanged.
|
|
29
|
+
|
|
30
|
+
## What it writes
|
|
31
|
+
|
|
32
|
+
`WRITE_ROUTES` in studio.js, pinned in `tests/hall_studio_world.mjs`: the four verdicts on an older review, the Approval queue's approve and send-back, and TW16's two, `claim` (take a page's round) and `draft` (the studio page's whole draft). Six write sites; none writes a live string.
|
|
33
|
+
|
|
34
|
+
## Where it differs from the artboard, and what forced it
|
|
35
|
+
|
|
36
|
+
- **The words are the ledger's**, not the sample's: 180 credits, 6 pages tweaked, +60 this week, "2 of 75 lines", "6 of 38 pages tweaked" on the harness fixtures (built by the real composers). The artboard's are sample data (the mocks README).
|
|
37
|
+
- **The recommendation order is the spec's** (asked, changed, untweaked), so the chips read Tasks, Goals, Privacy, Terms where the artboard's sample has Work, Privacy, Goals, Explore (ADR 0341 pick 8). "Tasks" is builders:work's own title.
|
|
38
|
+
- **The hint is #85858c, not #6f6f76**: #6f6f76 on the room's #07080d is about 4:1, under the AA floor for 12.5px type.
|
|
39
|
+
- **The panels' scrim is .94 over the light hall and .88 on a phone** (the artboard's .42 over the dark room otherwise), TW12's reason: the glass's grey words fall under AA at .42 over a light page.
|
|
40
|
+
- **A quick action is at least 72px and grows**, with 10px of padding, so a long detail wraps rather than clipping.
|
|
41
|
+
- **The window never slides under the words** (its left edge holds at 680px) when the hall chrome narrows the room.
|
|
42
|
+
- **Resume is not drawn when nothing is held**; the artboard always draws it (sample).
|
|
43
|
+
- **Not on the artboard at all:** the hall chrome view (the full-screen switch off, the default since TW20), the full-screen button top right, light mode, the phone layout (the pill across the top as three stacked figures, the window scaled whole to the top right, the desk on a floor at the bottom), the words' offer, edit strip and refusals, the older reviews' door and dialog, and the panels' loading and could-not-read lines.
|
|
44
|
+
|
|
45
|
+
## The proof
|
|
46
|
+
|
|
47
|
+
`studio.states.json` renders sixteen states (home, out, flow, flow-pick, status, words-offer, words-edit, reviews, full, full-back and TW12's six) against the hall-preview harness (`node scripts/hall-preview/server.js --port 4636 --fixture-me`), which answers the tally, the pages and the next page from `copy-desk__tally.json`, `copy-desk__pages.json` and `copy-desk__next.json`, the studio's own lines from `copy-desk__pages__draft-studio.json` (a new fallback ahead of the editor's catch-all), and the claim and the save from its CANNED WRITES. All clean at 1440, 390 and 320, dark and light. The studio fixture carries only the studio's PLACED lines: the page reader treats every string a page reads as data, so a fixture holding its unplaced lines would make the reading drop them. Behaviour: `tests/hall_studio_home.mjs` (the pure rules over the real composers, and the real page in a mini-DOM: the four reads, the panels, take it, and the words typed over and saved as the whole draft).
|
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
// mechanism, its idempotency and its non-blocking property are proved by the
|
|
22
22
|
// tests' own declarations, and R04 (task 1003115) writes the first production
|
|
23
23
|
// entry. A mechanism whose first real rule is written by the task that needs it
|
|
24
|
-
// is honest; one that ships a rule nobody asked for is a guess.
|
|
24
|
+
// is honest; one that ships a rule nobody asked for is a guess. (R04's artist
|
|
25
|
+
// review has since been removed by Tweak Mode, TW14; the removal proposal and
|
|
26
|
+
// the idea cascade remain. See the note above COPY_VISUAL_MODULES.)
|
|
25
27
|
//
|
|
26
28
|
// THE FOUR PROPERTIES THAT MAKE IT SAFE, each of which the tests pin:
|
|
27
29
|
//
|
|
@@ -87,32 +89,34 @@
|
|
|
87
89
|
// board has not triaged has, precisely, no classification yet.
|
|
88
90
|
//
|
|
89
91
|
// ---------------------------------------------------------------------------
|
|
90
|
-
// The
|
|
92
|
+
// The artist review that USED to be the first rule (removed, TW14).
|
|
91
93
|
// ---------------------------------------------------------------------------
|
|
92
94
|
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
95
|
+
// R04 (task 1003115) wrote the first production entry here: `artist-review-on-
|
|
96
|
+
// ship`, which filed a two-step artist review at backlog for every ship with
|
|
97
|
+
// copy or visuals. It is GONE (task 1004325 / BV2.TW14, ADR 0341 D9). The reviews
|
|
98
|
+
// were one per ship, and 34 of the 39 it filed were abandoned, 33 with no note.
|
|
99
|
+
// Tweak Mode replaced it: the PAGE is the unit an artist works on, and a ship
|
|
100
|
+
// that changes a page's words now WRITES NOTHING. The page reads "changed since
|
|
101
|
+
// last tweak" because drift is DERIVED (ADR 0341 D8, modules/copy-desk/
|
|
102
|
+
// page-status.js driftFor): the page's current reading against the lines its
|
|
103
|
+
// last round left. Nothing to store, so nothing here to file.
|
|
98
104
|
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
// would on ship generate a review OF the review, and so on. The exclusion
|
|
103
|
-
// makes the generation a single hop, never a chain.
|
|
104
|
-
// • "Copy or visuals" is a NARROW, explicit predicate (hasCopyOrVisualWork),
|
|
105
|
-
// not "every ship". A cascade that fired on everything would bury the artist,
|
|
106
|
-
// and the criterion's "dismissable rather than nagging" would be a lie.
|
|
105
|
+
// Do not bring a per-ship artist rule back to "mark the page": the mark already
|
|
106
|
+
// exists, derived, and a stored one would be a second record of what the
|
|
107
|
+
// readings and the ledger say.
|
|
107
108
|
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
109
|
+
// What stays, and why:
|
|
110
|
+
// • `hasCopyOrVisualWork` stays exported. The grader's advisory reader lens
|
|
111
|
+
// (ADR 0298, scripts/gds/ship-grade.js) is scoped by it.
|
|
112
|
+
// • The runner still resolves `ctx.artistGate` and `ctx.changedFiles` when a
|
|
113
|
+
// caller injects them (routes/lifecycle.js does). No production rule reads
|
|
114
|
+
// either today; the reads are shared with the removal rule's (ship-files.js
|
|
115
|
+
// memoises them), so they cost nothing, and ADR 0341 D9 keeps the runner.
|
|
116
|
+
// • Review rows already filed (source 'cascade', ref prefix `cascade-artist-
|
|
117
|
+
// review-on-ship-`) are ordinary data. The artist gate (ADR 0241) and the
|
|
118
|
+
// studio still read them; nothing deletes them.
|
|
111
119
|
|
|
112
|
-
// The modules whose shipped work puts user-facing copy or visuals in front of a
|
|
113
|
-
// person. Kept as an explicit set rather than inferred, because "is this
|
|
114
|
-
// copy/visual work" is a judgement the rule should state out loud and a test
|
|
115
|
-
// should be able to pin — an inferred predicate is the kind that quietly widens.
|
|
116
120
|
// The removal rule's key, lifted out of its declaration so the SURFACE that
|
|
117
121
|
// answers a removal proposal (task 1004095 / BV2.R16) can find those proposals
|
|
118
122
|
// without hand-typing the string. The key is half of every generated row's
|
|
@@ -120,6 +124,11 @@
|
|
|
120
124
|
// reader that silently returns nothing the day the key changes.
|
|
121
125
|
const REMOVAL_RULE_KEY = 'removal-proposed-on-ship';
|
|
122
126
|
|
|
127
|
+
// The modules whose shipped work puts user-facing copy or visuals in front of a
|
|
128
|
+
// person. Kept as an explicit set rather than inferred, because "is this
|
|
129
|
+
// copy/visual work" is a judgement the predicate should state out loud and a
|
|
130
|
+
// test should be able to pin — an inferred predicate is the kind that quietly
|
|
131
|
+
// widens.
|
|
123
132
|
const COPY_VISUAL_MODULES = Object.freeze([
|
|
124
133
|
'copy-desk', // the copy registry + proposals (goal 1000074's own spine)
|
|
125
134
|
'hall-ui', // the builders' hall — the most-seen surface
|
|
@@ -142,8 +151,9 @@ function isUiSurfacePath(p) { return typeof p === 'string' && UI_SURFACE_PATH.te
|
|
|
142
151
|
// `changedFiles` is what the work actually CHANGED (task 1004201). The label
|
|
143
152
|
// alone missed most UI work: of 19 recent ships that changed page files, 14 had
|
|
144
153
|
// module_key null — the goal forest (task 1003480) shipped its overflowing
|
|
145
|
-
// titles with no visual lens
|
|
146
|
-
//
|
|
154
|
+
// titles with no visual lens. Owner decision 2026-09-23: every UI change gets a
|
|
155
|
+
// visual lens. Since TW14 no cascade files a review on it; the grader's reader
|
|
156
|
+
// lens (ADR 0298) is what this predicate scopes.
|
|
147
157
|
function hasCopyOrVisualWork(task, changedFiles) {
|
|
148
158
|
if (!task) return false;
|
|
149
159
|
if (task.source === 'copy-proposal') return true; // a copy change that landed via the copy desk (ADR 0233)
|
|
@@ -153,44 +163,6 @@ function hasCopyOrVisualWork(task, changedFiles) {
|
|
|
153
163
|
return false;
|
|
154
164
|
}
|
|
155
165
|
|
|
156
|
-
// The two-step artist review body. STEP 1 records where the grade was blind;
|
|
157
|
-
// STEP 2 remakes the copy/visuals with both routes explicitly offered — R04's
|
|
158
|
-
// brief, verbatim in intent.
|
|
159
|
-
//
|
|
160
|
-
// STEP 1 ASKS FOR COACHING AGAIN, because there is now a lens listening.
|
|
161
|
-
//
|
|
162
|
-
// The history, because the wording is load-bearing: STEP 1 originally said
|
|
163
|
-
// "coach the grader", which asked for something no artist could do (task
|
|
164
|
-
// 1003937, ADR 0298) — the panel's four lenses read completion, code quality,
|
|
165
|
-
// security and runtime cost, and not one read a sentence, so four consecutive
|
|
166
|
-
// reviews found the same wall. The step was rewritten to ask for the RECORD
|
|
167
|
-
// instead, and that version carried an explicit instruction not to restore the
|
|
168
|
-
// coaching wording early, because the wording is the signal for which world we
|
|
169
|
-
// are in.
|
|
170
|
-
//
|
|
171
|
-
// Task 1003977 shipped the fifth lens: an ADVISORY 'reader', scoped to
|
|
172
|
-
// copy-bearing diffs by THIS FILE'S hasCopyOrVisualWork — the same predicate
|
|
173
|
-
// that fires this cascade, so an artist review and the copy lens cover exactly
|
|
174
|
-
// the same set by construction, and widening one widens both. The voice guide
|
|
175
|
-
// it waited on is docs/voice.md (task 1003976). So there is a counterpart to
|
|
176
|
-
// coach, it has a written standard to be coached against, and the record the
|
|
177
|
-
// interim wording collected is what both were built from.
|
|
178
|
-
//
|
|
179
|
-
// What has NOT changed: the reader is non-gating and cannot hold a ship (ADR
|
|
180
|
-
// 0241 keeps the artist's judgement on the deploy), so this review is still a
|
|
181
|
-
// request triage may dismiss.
|
|
182
|
-
function artistReviewBody(task) {
|
|
183
|
-
return [
|
|
184
|
-
`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.`,
|
|
185
|
-
'',
|
|
186
|
-
`STEP 1 — COACH THE READER. Pull how the grader judged task ${task.id} (GET /tasks/${task.id}?include=grade) and find the 'reader' row in signals.workers, with its copy_score in rubric_scores. That lens read the changed copy against docs/voice.md and cited rule ids. Say where it was RIGHT, where it was WRONG, and — the valuable one — what it did not see at all. A finding it filed that you disagree with is worth as much as one it missed: both are corrections to a lens that is listening, which is what this step could not ask for before task 1003977 shipped it (ADR 0298). If the miss is because no rule in docs/voice.md reaches it, that is a gap in the GUIDE — file it against the guide rather than expecting the lens to invent a rule at grade time. Two cases where there will be no reader row and that is not a fault: a ship that took the trivial-diff path (a single generalist shot, no panel), and one where the lens errored — it is advisory, so an outage is recorded and nothing is blocked.`,
|
|
187
|
-
'',
|
|
188
|
-
'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).',
|
|
189
|
-
'',
|
|
190
|
-
'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.',
|
|
191
|
-
].join('\n');
|
|
192
|
-
}
|
|
193
|
-
|
|
194
166
|
// The removal proposal's body (task 1004090 / BV2.R15, ADR 0313).
|
|
195
167
|
//
|
|
196
168
|
// IT ASKS, IT DOES NOT TELL. Every line here is written so a reader confirms or
|
|
@@ -235,44 +207,25 @@ function removalProposalBody(task, ctx) {
|
|
|
235
207
|
return lines.join('\n');
|
|
236
208
|
}
|
|
237
209
|
|
|
238
|
-
// Production rules.
|
|
239
|
-
//
|
|
210
|
+
// Production rules. Add one by appending an entry, never by touching the runner
|
|
211
|
+
// (ADR 0242). The artist review (R04) was the first and is gone (TW14, ADR 0341
|
|
212
|
+
// D9; see the note above COPY_VISUAL_MODULES), so the removal proposal is the
|
|
213
|
+
// only ship rule today.
|
|
240
214
|
const CASCADES = Object.freeze([
|
|
241
|
-
Object.freeze({
|
|
242
|
-
key: 'artist-review-on-ship',
|
|
243
|
-
discipline: 'artist',
|
|
244
|
-
kind: 'unclassified',
|
|
245
|
-
// The anti-recursion guard (source !== CASCADE_SOURCE) is FIRST so a
|
|
246
|
-
// cascade-generated task is rejected before any other test runs.
|
|
247
|
-
//
|
|
248
|
-
// The project's artist_gate at 'off' means "not generated" (ADR 0241 §2's
|
|
249
|
-
// table, task 1003575) — the one level at which this rule files nothing. It
|
|
250
|
-
// is read from ctx, never from branding, so the predicate stays pure; an
|
|
251
|
-
// absent ctx (a caller that resolved no policy) files, because the gate's
|
|
252
|
-
// default is 'strict' and silence must not be read as an opt-out.
|
|
253
|
-
//
|
|
254
|
-
// A changed-files list that hit GitHub's read cap FILES (task 1004201): a UI
|
|
255
|
-
// file may sit past the cap, and a padded PR must not skip its review.
|
|
256
|
-
when: (t, ctx) => (ctx || {}).artistGate !== 'off'
|
|
257
|
-
&& t.source !== CASCADE_SOURCE
|
|
258
|
-
&& (hasCopyOrVisualWork(t, (ctx || {}).changedFiles) || (ctx || {}).changedFilesTruncated === true),
|
|
259
|
-
title: (t) => `Artist review: the copy and visuals that shipped in task ${t.id}`,
|
|
260
|
-
body: artistReviewBody,
|
|
261
|
-
}),
|
|
262
215
|
Object.freeze({
|
|
263
216
|
key: REMOVAL_RULE_KEY,
|
|
264
217
|
// ENGINEER, not artist: confirming asks whether deleted code is genuinely
|
|
265
218
|
// gone or was moved, which is a question about the diff.
|
|
266
219
|
discipline: 'engineer',
|
|
267
220
|
kind: 'unclassified',
|
|
268
|
-
// The anti-recursion guard FIRST
|
|
269
|
-
//
|
|
221
|
+
// The anti-recursion guard FIRST: a cascade-generated task must never
|
|
222
|
+
// cascade, so it is rejected before any other test runs.
|
|
270
223
|
//
|
|
271
224
|
// The candidates are resolved ONCE into ctx by runCascadesForShip, never
|
|
272
225
|
// here: a `when` is pure and synchronous by contract, and this derivation is
|
|
273
226
|
// a network read. An absent ctx therefore means "nothing was derived" and
|
|
274
|
-
// files nothing
|
|
275
|
-
//
|
|
227
|
+
// files nothing, deliberately: silence about a removal must never be read
|
|
228
|
+
// as a removal.
|
|
276
229
|
when: (t, ctx) => t.source !== CASCADE_SOURCE
|
|
277
230
|
&& Array.isArray((ctx || {}).removals) && (ctx || {}).removals.length > 0,
|
|
278
231
|
title: (t) => `Was this removed? Task ${t.id} deleted earlier shipped work`,
|
|
@@ -418,9 +371,9 @@ function cascadesFor(task, rules = CASCADES, ctx = {}, trigger = TRIGGERS.ship)
|
|
|
418
371
|
|
|
419
372
|
// The createTask payload for one matched rule. The attribution preamble is
|
|
420
373
|
// prepended here rather than left to each rule, so no rule can forget it.
|
|
421
|
-
// `ctx` reaches body() as a second argument, mirroring when(). The
|
|
422
|
-
//
|
|
423
|
-
//
|
|
374
|
+
// `ctx` reaches body() as a second argument, mirroring when(). The removal rule
|
|
375
|
+
// needs it, because what a ship deleted is derived per run and is not a property
|
|
376
|
+
// of the task row.
|
|
424
377
|
function cascadeTaskSpec(rule, task, trigger = TRIGGERS.ship, ctx = {}) {
|
|
425
378
|
const body = String(rule.body(task, ctx) || '').trim();
|
|
426
379
|
const preamble =
|
|
@@ -559,9 +512,9 @@ async function runCascadesForShip(shipped, deps = {}) {
|
|
|
559
512
|
catch (e) { report.errors.push(`cascade: artistGateMode() threw (${e && e.message}) — policy unread`); }
|
|
560
513
|
}
|
|
561
514
|
// WHAT THIS SHIP CHANGED (task 1004201), from the server's own view of the
|
|
562
|
-
// merged pull request, so
|
|
563
|
-
// module_key label
|
|
564
|
-
//
|
|
515
|
+
// merged pull request, so a rule can key on the work rather than a
|
|
516
|
+
// module_key label (the artist review did, until TW14 removed it). A resolver
|
|
517
|
+
// that throws is reported and leaves the key unset, and the ship costs nothing.
|
|
565
518
|
if (typeof deps.changedFiles === 'function') {
|
|
566
519
|
try {
|
|
567
520
|
const changed = await deps.changedFiles(task);
|
|
@@ -650,7 +603,6 @@ module.exports = {
|
|
|
650
603
|
COPY_VISUAL_MODULES,
|
|
651
604
|
hasCopyOrVisualWork,
|
|
652
605
|
isUiSurfacePath,
|
|
653
|
-
artistReviewBody,
|
|
654
606
|
cascadeSourceRef,
|
|
655
607
|
assertRuleShape,
|
|
656
608
|
validateCascades,
|
|
@@ -79,16 +79,16 @@ function registerSeams() {
|
|
|
79
79
|
getTask: (id) => db.getTask(id),
|
|
80
80
|
findTaskBySourceRef: (ref) => db.findTaskBySourceRef(ref),
|
|
81
81
|
createTask: (spec) => db.createTask(spec),
|
|
82
|
-
// The project's artist gate (ADR 0241, task 1003575)
|
|
83
|
-
// doorway's one reader rather than from branding().project
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
// rule files nothing.
|
|
82
|
+
// The project's artist gate (ADR 0241, task 1003575), resolved through the
|
|
83
|
+
// doorway's one reader rather than from branding().project. The artist-review
|
|
84
|
+
// rule that read it was removed by Tweak Mode (TW14, ADR 0341 D9), so no
|
|
85
|
+
// production rule reads it today; the runner still offers it as policy.
|
|
87
86
|
artistGateMode: () => api.artistGate.artistGateMode(),
|
|
88
|
-
// WHAT THIS SHIP CHANGED (task 1004201): the merged PR's paths
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
// the
|
|
87
|
+
// WHAT THIS SHIP CHANGED (task 1004201): the merged PR's paths. No rule reads
|
|
88
|
+
// them since TW14, and they cost nothing extra: sharedPrReads memoises the
|
|
89
|
+
// same two GitHub reads removalCandidates makes. The NAMED function, not a
|
|
90
|
+
// closure, for the reason removalCandidates gives below; the arrow only hands
|
|
91
|
+
// it this ship's shared reads.
|
|
92
92
|
changedFiles: (task) => shipFiles.changedFilesForTask(task, pr),
|
|
93
93
|
// WHAT THIS SHIP DELETED (task 1004090 / BV2.R15, ADR 0313). The derivation
|
|
94
94
|
// asks GitHub what the pull request actually changed and blames the deleted
|